Ogre 编辑器三(自动生成与更新Ogre对象编辑界面)
最开始设计这个编辑器时,其中一个要求就是能在运行过程中,通过UI来更新各对象,这样我们就能明确每个Ogre对象更新其属性影响的渲染效果.比如点光源,方向光源,聚光灯各属性与效果,深度测试开启与关闭,深度比较方式影响的效果等等.如下先看效果图:
这个位置没有用上一篇天龙的场景,主要是图片大小限制,场景复杂后,生成的gif图片太大.
这个功能当时我主界面完成后,就准备做的,但是当时一时想不到好的方案,如果针对每个Ogre属性来生成UI,然后关联每个对象,再想想后续如果要修改其中的字段属性或是位置,这个工作量让我不得不先想一个方案能够避免这些问题.在完成第二篇天龙场景的加载时,顺便在空闲时间想出现在的这种解决方法,不说这方法好,但是工作量少,简洁,易修改,避免很多BUG.灵活性高,只有二个Ogre之间有关联,就可以显示在一起.如上面的Entity,他显示出他父节点SceneNode,以及他的父类MovableObject的各属性.
这个方案简单来说,分成三个部分,一个部分主要是XML文件,XML文件包含每个Ogre对象要关联的对象,字段,关联字段等.第二个部分是自动生成控件界面,根据第一部分的信息来生成各个界面.第三部分是根据Ogre对象更新各个控件,以及更新控件上的值更新后反馈给对应Ogre对象.如下是三个部分代码图,VS中C++代码生成的代码关联图中,聚合关系不能显示出来,所以一些聚合信息在上面没有显示出来.
XML文件与基本类
这个部分主要有二方面,一方面是XML文件保存的信息,包含每个Ogre对象关联的对象,每个对象更新的字段,字段的项选择,如下图:
然后就是如第一张图中的类OgreStyle,我们可以看到,这个类三个部分都有引用到,也是我们的这解决方法的核心类,这个类是一个树型结构,这个结构在这主要有二个好处,一是容易添加在树型控件里.二是可以得到父节点信息与子节点信息.其中我们设计在删除节点时,所有子节点全删除.如下是OgreStyle代码.
namespace Ogre3DX
{
typedef std::vector<string> VectorString;
class OgreStyle;
typedef std::vector<OgreStyle*> OgreStyleVector; class OgreStyle
{
public:
//ResourceType,SceneType
int type = ;
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对象编辑界面)的更多相关文章
- mysql中timestamp的自动生成与更新
转自:mysql中timestamp的自动生成与更新 MYSQL中TIMESTAMP类型可以设定默认值,就像其他类型一样.1.自动UPDATE 和INSERT 到当前的时间:表:----------- ...
- UE4/Unity3d 根据元数据自动生成与更新UI
大家可能发现一些大佬讲UE4,首先都会讲类型系统,知道UE4会根据宏标记生成一些特定的内容,UE4几乎所有高级功能都离不开这些内容,一般来说,我们不会直接去使用它. 今天这个Demo内容希望能加深大家 ...
- Activiti工作流学习笔记(三)——自动生成28张数据库表的底层原理分析
原创/朱季谦 我接触工作流引擎Activiti已有两年之久,但一直都只限于熟悉其各类API的使用,对底层的实现,则存在较大的盲区. Activiti这个开源框架在设计上,其实存在不少值得学习和思考的地 ...
- 自动生成 java 测试 mock 对象框架 DataFactory-01-入门使用教程
项目简介 Data-Factory 用于根据对象,随机自动生成初始化信息,避免了手动创建对象的繁琐,便于测试. 特性 8 大基本类型的支持 String.Date.金额,日期等常见类型的支持 java ...
- 什么是静态代码块?java中如何使用空参构造方法自动生成不同名字的对象,使用非静态的属性和静态属性有什么区别,原因是什么?如何理解static关键字
静态代码块?类加载就执行,最先执行 class demo{ static int num; static{ num=10; num*=3; System.out.println("haha& ...
- eclipse、idea中自动生成元模型JPA元模型对象
一.eclipse 1.首先准备好两个jar包hibernate-jpa-2.0-api-1.0.1.Final和hibernate-jpamodelgen-4.3.5.Final 2.选中项目右击 ...
- python 自动生成C++代码 (代码生成器)
python 代码自动生成的方法 (代码生成器) 遇到的问题 工作中遇到这么一个事,需要写很多C++的底层数据库类,但这些类大同小异,无非是增删改查,如果人工来写代码,既费力又容易出错:而借用pyth ...
- Eclipse中R文件不能自动生成
R文件不能自动生成主要是因为编译有错误,这时你想什么办法都是没有用的,clean, fix properties,都不是从根上解决问题. R文件主要是自动生成资源文件的id的,里边静态子类 ...
- ionic + asp.net core webapi + keycloak实现前后端用户认证和自动生成客户端代码
概述 本文使用ionic/angular开发网页前台,asp.net core webapi开发restful service,使用keycloak保护前台页面和后台服务,并且利用open api自动 ...
随机推荐
- IIS配置,权限
2. cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 3. aspnet_regiis.exe -i Chen 19:04:42 %wind ...
- 关于docker容器和镜像的区别
docker的整个生命周期有三部分组成:镜像(image)+容器(container)+仓库(repository): 如下图所示,容器是由镜像实例化而来,这和我们学习的面向对象的概念十分相似,我们可 ...
- 聊一聊 Spring 中的线程安全性
Spring与线程安全 Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”.但其实,Spring并没有保证这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码. Sp ...
- [SQL Server 2014] 微软将于年底发布新版数据库SQL Server 2014
在今年的TechEd大会上,微软宣布SQL Server 2014的第一个技术预览版.SQL Server 2014的重点包括内存OLTP.实时的大数据分析.支持混合云端,以及提供更完整的商业智能(B ...
- 如何让 Xcode 在读写上提速100倍?
如何让 Xcode 在读写上提速100倍? 上个月参加了一场西雅图当地的线下 iOS 开发者聚会.Jeff Szuhay 作为一个有20+年开发经验的资深程序员,跟我讲了一套提高 iOS 开发效率的方 ...
- kafka消费者客户端启动之后消费不到消息的原因分析
如果你发现你的一个消费者客户端A已经启动了,但是就是不消费消息,此时你应该检查一下该消费者所在的组中(ConsumerGroup)是否还有其他的消费者,topic的分区可能被组中其他的消费者线程抢走( ...
- 安装Python2.7出现configure: error: no acceptable C compiler found in $PATH错误
安装Python2.7出现configure: error: no acceptable C compiler found in $PATH错误 安装步骤: 安装依赖 yum groupinstall ...
- Oracle生成流水号函数
一.参考 1:日期范围上 smalldatetime的有效时间范围1900/1/1~2079/6/6 datetime的有效时间范围1753/1/1~9999/12/31 2:精准度上 smallda ...
- ASP.NET学习笔记(5)——原生Ajax基本操作
说明(2017-11-4 15:32:49): 1. 回北京后又快一个月了,上次在家写的下回预告,到底是没把加水印写完,而且这次也不想写.. 2. 上次许的愿,十月份看完asp.net,已经泡汤了,翻 ...
- 6. 支持向量机(SVM)核函数
1. 感知机原理(Perceptron) 2. 感知机(Perceptron)基本形式和对偶形式实现 3. 支持向量机(SVM)拉格朗日对偶性(KKT) 4. 支持向量机(SVM)原理 5. 支持向量 ...