最开始设计这个编辑器时,其中一个要求就是能在运行过程中,通过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 = ;
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 = ,
Material,
Technique,
Pass,
Mesh,
SubMesh,
TextureUnit,
Texture,
Particle,
Compositor,
Program,
Shader_Cg,
Shader_hlsl,
Shader_glsl,
}; //对应Ogre场景
enum SceneType
{
Scene = ,
SceneNode,
Viewport,
Camera,
RenderSystem,
RenderTarget,
MovableObject,
Renderable
}; enum MovableType
{
MovableOther = ,
BillboardChain,
BillboardSet,
Entity,
Light,
ManualObject,
ParticleSystem,
RibbonTrail,
}; enum RenderType
{
RenderOther = ,
RenderSubEntity
}; enum FileType
{
FL_Materials = ,
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 = )
{
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 = ; 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() > )
{
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 = ; 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) = ;
//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(, 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 >= && 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 = ; 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 = ;
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 > );
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 != )
{
auto text = OgreManager::getInstance().getValue(currentOgreStyle, type, field->fieldName);
fieldLayout->setValue(text);
}
}
}
}
} void PanelProperties::updateSize()
{
int height = ; 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(, 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() == )
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 != )
{
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();
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();
}
object->setObject(tech);
}
else if (type == ResourceEnum::ResourceType::Pass)
{
auto tech = material->getBestTechnique();
if (tech == nullptr)
{
tech = material->getTechnique();
}
object->setObject(tech->getPass());
}
} 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 = ;
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) = ;
virtual bool setValue(std::string field, std::string value) = ;
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(, -, , );
}
~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 = ;
auto st = ogreObject->getShadowTechnique();
if (st == 0x12)
{
index = ;
}
else if (st == 0x11)
{
index = ;
}
else if (st == 0x22)
{
index = ;
}
else if (st == 0x21)
{
index = ;
}
else if (st == 0x25)
{
index = ;
}
else if (st == 0x26)
{
index = ;
}
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 == )
{
st = ShadowTechnique::SHADOWTYPE_STENCIL_MODULATIVE;
}
else if (index == )
{
st = ShadowTechnique::SHADOWTYPE_STENCIL_ADDITIVE;
}
else if (index == )
{
st = ShadowTechnique::SHADOWTYPE_TEXTURE_MODULATIVE;
}
else if (index == )
{
st = ShadowTechnique::SHADOWTYPE_TEXTURE_ADDITIVE;
}
else if (index == )
{
st = ShadowTechnique::SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED;
}
else if (index == )
{
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新手参考.

Ogre 编辑器三(自动生成与更新Ogre对象编辑界面)的更多相关文章

  1. mysql中timestamp的自动生成与更新

    转自:mysql中timestamp的自动生成与更新 MYSQL中TIMESTAMP类型可以设定默认值,就像其他类型一样.1.自动UPDATE 和INSERT 到当前的时间:表:----------- ...

  2. UE4/Unity3d 根据元数据自动生成与更新UI

    大家可能发现一些大佬讲UE4,首先都会讲类型系统,知道UE4会根据宏标记生成一些特定的内容,UE4几乎所有高级功能都离不开这些内容,一般来说,我们不会直接去使用它. 今天这个Demo内容希望能加深大家 ...

  3. Activiti工作流学习笔记(三)——自动生成28张数据库表的底层原理分析

    原创/朱季谦 我接触工作流引擎Activiti已有两年之久,但一直都只限于熟悉其各类API的使用,对底层的实现,则存在较大的盲区. Activiti这个开源框架在设计上,其实存在不少值得学习和思考的地 ...

  4. 自动生成 java 测试 mock 对象框架 DataFactory-01-入门使用教程

    项目简介 Data-Factory 用于根据对象,随机自动生成初始化信息,避免了手动创建对象的繁琐,便于测试. 特性 8 大基本类型的支持 String.Date.金额,日期等常见类型的支持 java ...

  5. 什么是静态代码块?java中如何使用空参构造方法自动生成不同名字的对象,使用非静态的属性和静态属性有什么区别,原因是什么?如何理解static关键字

    静态代码块?类加载就执行,最先执行 class demo{ static int num; static{ num=10; num*=3; System.out.println("haha& ...

  6. eclipse、idea中自动生成元模型JPA元模型对象

    一.eclipse 1.首先准备好两个jar包hibernate-jpa-2.0-api-1.0.1.Final和hibernate-jpamodelgen-4.3.5.Final 2.选中项目右击 ...

  7. python 自动生成C++代码 (代码生成器)

    python 代码自动生成的方法 (代码生成器) 遇到的问题 工作中遇到这么一个事,需要写很多C++的底层数据库类,但这些类大同小异,无非是增删改查,如果人工来写代码,既费力又容易出错:而借用pyth ...

  8. Eclipse中R文件不能自动生成

       R文件不能自动生成主要是因为编译有错误,这时你想什么办法都是没有用的,clean, fix properties,都不是从根上解决问题.    R文件主要是自动生成资源文件的id的,里边静态子类 ...

  9. ionic + asp.net core webapi + keycloak实现前后端用户认证和自动生成客户端代码

    概述 本文使用ionic/angular开发网页前台,asp.net core webapi开发restful service,使用keycloak保护前台页面和后台服务,并且利用open api自动 ...

随机推荐

  1. 【Unity】第12章 导航网格和寻路

    开发环境:Win10.Unity5.3.4.C#.VS2015 创建日期:2016-05-09 一.简介 NavMesh(导航网格)是3D游戏世界中用于实现"动态"物体自动寻路的一 ...

  2. 每日英语:When The Boss Works Long Hours, Do We All Have To?

    The problem: Every night, your workaholic boss is still glued to the computer when you need to leave ...

  3. Custom Sublime Text Build Systems For Popular Tools And Languages

    Sublime Text is currently the text editor of choice for a number of developers in the open-source co ...

  4. 【MySQL】MySQL层级数据的递归遍历

    层级的业务数据在系统中很常见,如组织机构.商品品类等. 如果要获取层级数据的全路径,除了缓存起来,就是递归访问的方式了: 将层级数据缓存在redis中,用redis递归获取层级结构.此方法效率高. 在 ...

  5. (转)Go和HTTPS

    转自:http://studygolang.com/articles/2946 Go和HTTPS  2015-04-30   bigwhite  阅读 5688 次   4 人喜欢  3 条评论  收 ...

  6. golang将interface{}转换为struct

    项目中需要用到golang的队列,container/list,需要放入的元素是struct,但是因为golang中list的设计,从list中取出时的类型为interface{},所以需要想办法把i ...

  7. js获取上传图片真实的尺寸大小和存储大小

    https://blog.csdn.net/u014236259/article/details/52885591 ****************************************** ...

  8. maven多环境发布.

    需要设定profile和build <profiles> <profile> <id>develop</id> <properties> & ...

  9. Django import / export实现数据库导入导出

    使用django-import-export库,导入导出数据,支持csv.xls.json.html等格式 官网:http://django-import-export.readthedocs.io/ ...

  10. JAVA-JSP内置对象之request获得参数的所有参数值(多个值)

    相关资料:<21天学通Java Web开发> 获得参数的所有参数值(多个值)1.需要使用request对象的getParameterValues()方法. RequestForm4.jsp ...