最开始设计这个编辑器时,其中一个要求就是能在运行过程中,通过UI来更新各对象,这样我们就能明确每个Ogre对象更新其属性影响的渲染效果.比如点光源,方向光源,聚光灯各属性与效果,深度测试开启与关闭,深度比较方式影响的效果等等.如下先看效果图:
这个位置没有用上一篇天龙的场景,主要是图片大小限制,场景复杂后,生成的gif图片太大.
这个功能当时我主界面完成后,就准备做的,但是当时一时想不到好的方案,如果针对每个Ogre属性来生成UI,然后关联每个对象,再想想后续如果要修改其中的字段属性或是位置,这个工作量让我不得不先想一个方案能够避免这些问题.在完成第二篇天龙场景的加载时,顺便在空闲时间想出现在的这种解决方法,不说这方法好,但是工作量少,简洁,易修改,避免很多BUG.灵活性高,只有二个Ogre之间有关联,就可以显示在一起.如上面的Entity,他显示出他父节点SceneNode,以及他的父类MovableObject的各属性.
这个方案简单来说,分成三个部分,一个部分主要是XML文件,XML文件包含每个Ogre对象要关联的对象,字段,关联字段等.第二个部分是自动生成控件界面,根据第一部分的信息来生成各个界面.第三部分是根据Ogre对象更新各个控件,以及更新控件上的值更新后反馈给对应Ogre对象.如下是三个部分代码图,VS中C++代码生成的代码关联图中,聚合关系不能显示出来,所以一些聚合信息在上面没有显示出来.
XML文件与基本类
这个部分主要有二方面,一方面是XML文件保存的信息,包含每个Ogre对象关联的对象,每个对象更新的字段,字段的项选择,如下图:
然后就是如第一张图中的类OgreStyle,我们可以看到,这个类三个部分都有引用到,也是我们的这解决方法的核心类,这个类是一个树型结构,这个结构在这主要有二个好处,一是容易添加在树型控件里.二是可以得到父节点信息与子节点信息.其中我们设计在删除节点时,所有子节点全删除.如下是OgreStyle代码.
namespaceOgre3DX { typedef std::vector<string>VectorString; classOgreStyle; typedef std::vector<OgreStyle*>OgreStyleVector; classOgreStyle { 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::stringgetText() { if(ogreText.empty()) returnogreName; returnogreText; } 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); } voidremoveAll() { //删除所有子节点 while (!childs.empty()) { auto child =childs.back(); childs.pop_back(); //这句 避免递归时重复delete child->parent =nullptr; if (child !=nullptr) { deletechild; child =nullptr; } } } OgreStyle* findChild(inttype) { if(childs.empty()) returnnullptr; for (auto child : this->childs) { if (child->type ==type) returnchild; } returnnullptr; } OgreStyle* findChild(std::stringname) { if(childs.empty()) returnnullptr; for (auto child : this->childs) { if (child->ogreName ==name) returnchild; } returnnullptr; } 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) { deletechild; child =nullptr; } } } private: void remove(OgreStyle* child, boolbDelete) { 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) { deletechild; child =nullptr; } } }; }
其中OgreStyle中的Type就是对应的Ogre中的对象类型,而ogreName是对应Ogre对象的标识,parent与childs树结构.type对应的值在如下类中:
namespaceResourceEnum { //对应Ogre资源类型 enumResourceType { Resource = 100, Material, Technique, Pass, Mesh, SubMesh, TextureUnit, Texture, Particle, Compositor, Program, Shader_Cg, Shader_hlsl, Shader_glsl, }; //对应Ogre场景 enumSceneType { Scene = 200, SceneNode, Viewport, Camera, RenderSystem, RenderTarget, MovableObject, Renderable }; enumMovableType { MovableOther = 300, BillboardChain, BillboardSet, Entity, Light, ManualObject, ParticleSystem, RibbonTrail, }; enumRenderType { RenderOther = 400, RenderSubEntity }; enumFileType { FL_Materials = 500, FL_Material, FL_Group, }; }
先暂时列这么多,其中上面第一份XML文件就是每个type关联那些type,第二份文件就是每个type对应的字段,第三个type就是其中Ogre中的一些enum.下面这个类把type与三份三XML文件关联起来.其中方法initXML不是有意要使用这种比较难理解的方式,主要是因为DataStreamHolder里面的数据是shared_ptr智能指针类型,最后得到的root只有在函数initXML内才有效,传出这个值无效,而针对XML文件有效性检查的代码是一样,这样只有把方法传过来.
namespaceOgre3DX { structNodeStyle { std::stringnodeName; std::stringnodeText; NodeStyle(){}; NodeStyle(std::string name, std::stringtext) :nodeName(name), nodeText(text){} stringgetText() { if(nodeText.empty()) { stringresult; for (auto ch = nodeName.begin(); ch != nodeName.end(); ++ch) { if (ch != nodeName.begin() && isupper(*ch)) { result.push_back(' '); } result.push_back(*ch); } returnresult; } returnnodeText; } }; structOgreField { stringfieldName; stringfieldType; stringfieldKey; stringfieldText; bool fieldManual = false; OgreField(){}; OgreField(std::string name, std::string type, std::stringkey) :fieldName(name), fieldType(type), fieldKey(key){} stringgetText() { if(fieldText.empty()) { stringresult; for (auto ch = fieldName.begin(); ch != fieldName.end(); ++ch) { if (ch != fieldName.begin() && isupper(*ch)) { result.push_back(' '); } result.push_back(*ch); } returnresult; } returnfieldText; } }; structRelationVisible { stringname; stringrelationName; stringrelationValue; boolrelationVisible; stringnodeName; RelationVisible(std::string name, std::string dName, std::string value, bool visible, stringnode) :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; } namespaceOgre3DX { classOgreStyleManager : public tools::Singleton<OgreStyleManager> { public: OgreStyleManager(); ~OgreStyleManager(); voidinitialise(); int getOgreStyleType(const string& name, int deault = 0) { for(auto kv : ogreStyles) { if (kv.second ==name) returnkv.first; } returndeault; } std::string getOgreStyleName(int type, const string& name = "") { if (ogreStyles.find(type) !=ogreStyles.end()) { returnogreStyles[type]; } returnname; } NodeStyleVector getNodeStyles(inttype) { 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); } } } returnresult; } FieldVector getFieldVector(const string&nodeName) { if (nodeFields.find(nodeName) !=nodeFields.end()) { returnnodeFields[nodeName]; } returnFieldVector(); } VectorString getKeyItems(const string&key) { if (keyItems.find(key) !=keyItems.end()) { returnkeyItems[key]; } returnVectorString(); } RelationVector getRelationFields(const string&nodeName) { RelationVector result; for(auto rf : relationFields) { if (rf->nodeName ==nodeName) result.push_back(rf); } returnresult; } 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::stringxmlName, ptrFun func); voidinitOgreStyles(); voidinitStyleNodes(xml::ElementPtr root); voidinitNodeFields(xml::ElementPtr root); voidinitKeyItems(xml::ElementPtr root); voidinitRelationFields(); 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; namespaceOgre3DX { OgreStyleManager::OgreStyleManager() { } OgreStyleManager::~OgreStyleManager() { } voidOgreStyleManager::initialise() { initOgreStyles(); initXML("StyleNode.xml", &OgreStyleManager::initStyleNodes); initXML("NodeField.xml", &OgreStyleManager::initNodeFields); initXML("KeyItem.xml", &OgreStyleManager::initKeyItems); initRelationFields(); } voidOgreStyleManager::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::stringxmlName, 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); } voidOgreStyleManager::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); } } } voidOgreStyleManager::initNodeFields(xml::ElementPtr root) { auto node = root->getElementEnumerator(); while (node.next("OgreNode")) { auto name = node->findAttribute("name"); auto text = node->findAttribute("text"); NodeStyle* nodeStyle = newNodeStyle(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 = newOgreField(fieldName, fieldType, fieldKey); field->fieldText =fieldText; if (fieldManual.size() > 0) { field->fieldManual =Ogre::StringConverter::parseBool(fieldManual); } nodeFields[name].push_back(field); } } } voidOgreStyleManager::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); } } } voidOgreStyleManager::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(""); } returnroot; } }
通过这个类,我们知道,一个OgreStyle对应多个NodeStyle,就如Entity分别显示Entiy, SceneNode, MovableObject 这三个节点,每个节点NodeStyle又包含多个OgreField,就如SceneManager包含name,typename,ambientLight等字段.上面的RelationVisible主要是指二个字段有主从关系的那种,如上面是聚光灯才显示一些聚光灯的信息,后面也会放入XML文件中.
然后就是OgreQuery,这个类比较复杂,在这我们只列出一个方法,就是得到我们现在树型控件上的数据.
OgreStyle*OgreQuery::getTLRoot() { ogreRoot = shared_ptr<OgreStyle>(newOgreStyle()); auto ogreSceneRoot = newOgreStyle(); 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 = newOgreStyle(); 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 = newOgreStyle(); 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 = newOgreStyle(); movable->add(subStyle); getRenderable(subEntity, subStyle, ResourceEnum::RenderType::RenderSubEntity, i); } } } } auto ogreRenderRoot = newOgreStyle(); ogreRoot->add(ogreRenderRoot); auto render =Ogre::Root::getSingleton().getRenderSystem(); getRenderSystem(render, ogreRenderRoot); return ogreRoot.get(); }
这个类现在设计了二种显示方式,一种是上面的这种,不显示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用于管理这些类,我们先看这个类的实现.
namespacetools { classPropertyFieldManager : public MyGUI::Singleton<PropertyFieldManager> { public: PropertyFieldManager(); virtual ~PropertyFieldManager(); voidinitialise(); voidshutdown(); 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"; namespacetools { template <typename Type> classGenericFactory { public: typedef MyGUI::delegates::CDelegate2<IPropertyField*&, MyGUI::Widget*>Delegate; static typename Delegate::IDelegate*getFactory() { returnMyGUI::newDelegate(createFromFactory); } private: static void createFromFactory(IPropertyField*& _instance, MyGUI::Widget*_parent) { _instance = newType(_parent); } }; PropertyFieldManager::PropertyFieldManager() { } PropertyFieldManager::~PropertyFieldManager() { } voidPropertyFieldManager::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(); } voidPropertyFieldManager::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); returnresult; } }
PropertyFieldManager主要是根据不同的NodeField.xml中NodeField中的Type来生成对应IPropertyField.IPropertyField是上面各字段控件的基类,我们来看下他的实现,了解所有字段控件实现能那些功能.对比Mygui中的LayoutEditor中的IPropertyField有些改动主要一是为了让IPropertyField承载更多公共实现,免的子类型的各部分代码差不多.二是更好的适合我们自己的项目,针对Ogre的各属性更改.
using namespaceOgre3DX; namespacetools { typedef MyGUI::delegates::CDelegate3<const std::string&, const std::string&, bool>PropertyFieldActionDelegate; classIPropertyField { public: virtual ~IPropertyField(); virtual void initialise(const std::string&_type); virtual void setValue(const std::string& _value) = 0; //Relation Field virtual stringgetValue(); virtual voidonFillValues(); virtual void setField(OgreField*_field); virtual OgreField*getField(); //Update Field virtual boolgetFocus(); virtual void setName(const std::string&_value); virtual void setVisible(bool_value); virtual boolgetVisible(); virtual void onAction(const std::string& _value, bool_final); virtual boolonCheckValue(); virtualMyGUI::IntSize getContentSize(); virtual void setCoord(const MyGUI::IntCoord&_coord); virtual bool checkFocus(MyGUI::Widget*widget); PropertyFieldActionDelegate eventAction; protected: MyGUI::TextBox* mText =nullptr; std::stringmType; std::stringmName; OgreField* field =nullptr; MyGUI::Widget* mainWidget =nullptr; }; } namespacetools { IPropertyField::~IPropertyField() { } void IPropertyField::initialise(const std::string&_type) { mType =_type; } stringIPropertyField::getValue() { return ""; } voidIPropertyField::onFillValues() { } void IPropertyField::setField(OgreField*_field) { field =_field; onFillValues(); } OgreField*IPropertyField::getField() { returnfield; } boolIPropertyField::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); } boolIPropertyField::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); } boolIPropertyField::getVisible() { return mainWidget->getVisible(); } }
可以看到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的具体实现,这个类可以让我们更好理解相关方法需要如何实现.
namespacetools { classPropertyFieldComboBox : publicwraps::BaseLayout, publicIPropertyField { public: PropertyFieldComboBox(MyGUI::Widget*_parent); virtual ~PropertyFieldComboBox(); virtual void setValue(const std::string&_value); virtual stringgetValue(); protected: virtual voidonFillValues(); private: void notifyApplyProperties(MyGUI::Widget*_sender); void notifyForceApplyProperties2(MyGUI::ComboBox*_widget, size_t _index); protected: MyGUI::ComboBox*mField; }; } namespacetools { 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() { } voidPropertyFieldComboBox::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); } stringPropertyFieldComboBox::getValue() { return mField->getOnlyText(); } }
当我们知道如何根据OgreField生成IPropertyField后,如前面一个OgreStyle对应多个NodeStyle,每个节点NodeStyle又包含多个OgreField.对应PropertiesPanelView包含多个PanelProperties.每个PanelProperties又包含多个IPropertyField.
当PropertiesPanelView得到OgreStyle后,根据OgreStyle的NodeStyle生成PanelProperties,然后PanelProperties根据对应的NodeStyle中的OgreField 生成IPropertyField.这样OgreStyle需要的各个控件显示出来了,如下是相关PropertiesPanelView与PanelProperties代码.
namespacetools { classPropertiesPanelView : publicwraps::BaseLayout { public: PropertiesPanelView(MyGUI::Widget* _parent =nullptr); virtual ~PropertiesPanelView(); void notifyChangeSelectedWidget(OgreStyle*data); voidhideAllPanel(); private: void notifyWindowChangeCoord(MyGUI::Window*_sender); std::vector<PanelProperties*> getPropertyWindow(OgreStyle*_style); size_t getIndexPanel(PanelProperties*_panel); void onRefleshFiled(floattime); private: MyGUI::IntSize mOldSize; PanelView*mPanelView; typedef std::map<string, PanelProperties*>MapPropertyWindow; MapPropertyWindow mMapPropertyWindow; OgreStyle*currentStyle; }; } namespacetools { 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(); } } voidPropertiesPanelView::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 = newPanelProperties(); mPanelView->addItem(panel);//insertItem(getIndexByDepth(_depth), result); mMapPropertyWindow[key] =panel; } mMapPropertyWindow[key]->setStyle(_style, nodeStyle); result.push_back(mMapPropertyWindow[key]); } returnresult; } size_t PropertiesPanelView::getIndexPanel(PanelProperties*_panel) { for (size_t index = 0; index < mPanelView->getItemCount(); ++index) { if (mPanelView->getItem(index) ==_panel) returnindex; } returnMyGUI::ITEM_NONE; } void PropertiesPanelView::onRefleshFiled(floattime) { for (auto item = mMapPropertyWindow.begin(); item != mMapPropertyWindow.end(); ++item) { if ((*item).second->getVisible()) { (*item).second->onReflesh(); } } } }
using namespaceOgre3DX; namespacetools { typedef std::vector<std::pair<std::string, IPropertyField*>>MapPropertyField; typedef MapPropertyField::iterator FieldIter; classPanelProperties : publicwraps::BasePanelViewItem { public: PanelProperties(); virtual voidinitialise(); virtual voidshutdown(); void setStyle(OgreStyle* ogreStyle, NodeStyle*nodeStyle); voidupdate(); voidonReflesh(); private: void notifyAction(const std::string& _name, const std::string& _value, bool_final); size_t addParametrs(); voiddestroyPropertyFields(); voidhidePropertyFields(); voidupdateSize(); voidupdateRelationFields(); 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; }; } namespacetools { PanelProperties::PanelProperties() : BasePanelViewItem("PanelProperties.layout") { } voidPanelProperties::initialise() { } voidPanelProperties::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++; } } returnresult; } void PanelProperties::setStyle(OgreStyle* ogreStyle, NodeStyle*nodeStyle) { currentOgreStyle =ogreStyle; currentNodeStyle =nodeStyle; } voidPanelProperties::update() { hidePropertyFields(); if (currentNodeStyle !=nullptr) mPanelCell->setCaption(currentNodeStyle->getText()); size_t count =addParametrs(); setVisible(count > 0); onReflesh(); updateSize(); updateRelationFields(); } voidPanelProperties::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); } } } } } voidPanelProperties::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); } voidPanelProperties::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(); } } voidPanelProperties::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(); } voidPanelProperties::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) returni; } returnmFields.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)); returnresult; } }
这样第二部分就把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找到对象.如下是代码:
namespaceOgre3DX { classOgreManager : public tools::Singleton<OgreManager> { public: OgreManager() { mapObject[ResourceEnum::ResourceType::Resource] = newOgreResource(); mapObject[ResourceEnum::ResourceType::Material] = newOgreMaterial(); mapObject[ResourceEnum::ResourceType::Technique] = newOgreTechnique(); mapObject[ResourceEnum::ResourceType::Pass] = newOgrePass(); mapObject[ResourceEnum::SceneType::Viewport] = newOgreViewport(); mapObject[ResourceEnum::SceneType::SceneNode] = newOgreSceneNode(); mapObject[ResourceEnum::SceneType::MovableObject] = newOgreMovableObject(); mapObject[ResourceEnum::SceneType::RenderTarget] = newOgreRenderTarget(); mapObject[ResourceEnum::SceneType::Camera] = newOgreCamera(); mapObject[ResourceEnum::SceneType::Renderable] = newOgreRenderable(); mapObject[ResourceEnum::SceneType::Scene] = newOgreSceneManager(); mapObject[ResourceEnum::MovableType::Entity] = newOgreEntity(); mapObject[ResourceEnum::MovableType::Light] = newOgreLight(); 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, inttype) { auto object =getType(type); if (object ==nullptr) returnnullptr; //重新设置ogre值. if (style != preStyle || type !=preType) { fillStyleObject(style, type); preStyle =style; preType =type; } return object; } OgreBasic* getType(inttype) { if (mapObject.find(type) !=mapObject.end()) { returnmapObject[type]; } returnnullptr; } void fillStyleObject(OgreStyle* style, inttype) { auto object =getType(type); if (object ==nullptr) return; switch (style->type) { caseResourceEnum::SceneType::Scene: if (true) { if (type ==ResourceEnum::SceneType::Scene) { object->setObject(scene); } else if (type ==ResourceEnum::SceneType::Viewport) { object->setObject(scene->getCurrentViewport()); } } break; caseResourceEnum::MovableType::Entity: if (true) { auto entity = scene->getEntity(style->ogreName); if (type ==ResourceEnum::MovableType::Entity) { object->setObject(entity); } fillMovableable(object, entity, type); } break; caseResourceEnum::ResourceType::Material: if (true) { auto mat = MaterialManager::getSingleton().getByName(style->ogreName); fillMaterial(object, mat, type); } break; caseResourceEnum::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; caseResourceEnum::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; caseResourceEnum::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; caseResourceEnum::SceneType::Camera: if (true) { auto camera = scene->getCamera(style->ogreName); if (camera !=nullptr) { fillCamera(object, camera, type); } } break; caseResourceEnum::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, inttype) { 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, inttype) { 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, inttype) { 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, inttype) { 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; }; }
其中OgreBasic是所有ogre对象更新类的基类,OgreTemplate是一个泛型类,提供一些方便.如下代码:
using namespaceOgre; typedef Ogre::StringConverter format; namespaceOgre3DX { classOgreBasic { 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(){ returnnullptr; } }; template<typename T> classOgreTemplate : publicOgreBasic { public: OgreTemplate() { } virtual ~OgreTemplate(){}; virtual std::string getValue(std::stringfield) { return ""; } virtual bool setValue(std::string field, std::stringvalue) { return false; } virtual void setObject(T* object) { ogreObject = object; } virtual T*getOgreObject() { returnogreObject; } protected: virtual void setObject(void* object) { return setObject((T*)object); } virtual void*getObject() { if (ogreObject ==nullptr) returnnullptr; return (void*)ogreObject; } protected: T* ogreObject =nullptr; }; }
其中OgreBasic是根据字段名得到对应Ogre属性的值,setValue是对某个字段赋值,setObject是传入相关Ogre对象指针,getObject是返回某对象指针,而OgreTemplate主要给我们提供一些方便,如上面都是void*指针,在C#中相当于object一样,需要强制转化后才能用,这个OgreTemplate自动帮我们做这些事件,基于最少可见原则,在OgreTemplate我们把父类中的setObject与getObject隐藏,我们并不希望用OgreTemplate泛型类后还在使用对应void*指针,这样可以避免很多不安全的问题.而OgreBasic本身是给OgreManager使用,在这个类中,会保证生成正确的OgreTemplate,传入正确的Ogre对象指针.
在这我们给出OgreSceneManager的一个OgreTemplate泛型具体化实现.
namespaceOgre3DX { classOgreSceneManager : 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) { deleteplane; plane =nullptr; } } virtual std::string getValue(std::stringfield) { 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()); } returnresult; } virtual bool setValue(std::string field, std::stringvalue) { 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; } }; }
需要注意的是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新手参考.