From 0bcbcda8b827954f560a5cb75e5940e95fd23f30 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 17:26:36 +0100 Subject: [PATCH 01/18] EntityFilePreprocessor WIP WIP EntityFilePreprocessor --- assets | 2 +- include/Engine/Core/EntityFile.h | 321 +++++++++++++++++++ include/Engine/Core/EntityFilePreprocessor.h | 226 +++++++++++++ include/Engine/Core/Util/XercesString.h | 46 +++ include/Game/Game.h | 2 + resources/Schema/Entities/RaptorCopter.xml | 68 ++++ resources/Schema/Types/Entity.xsd | 8 + src/Engine/Core/EntityFile.cpp | 47 +++ src/Engine/Core/EntityXMLFile.cpp | 78 +---- src/Engine/Core/World.cpp | 1 + src/Game/Game.cpp | 5 +- 11 files changed, 741 insertions(+), 63 deletions(-) create mode 100644 include/Engine/Core/EntityFile.h create mode 100644 include/Engine/Core/EntityFilePreprocessor.h create mode 100644 include/Engine/Core/Util/XercesString.h create mode 100644 resources/Schema/Entities/RaptorCopter.xml create mode 100644 src/Engine/Core/EntityFile.cpp diff --git a/assets b/assets index 673d4a4e..c8e631f4 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 673d4a4e4c5a3f5bc9fedf82234e8f8751f63a44 +Subproject commit c8e631f449515cdbe3647b96ce472839748e28f9 diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h new file mode 100644 index 00000000..bca0bf96 --- /dev/null +++ b/include/Engine/Core/EntityFile.h @@ -0,0 +1,321 @@ +#ifndef EntityFile_h__ +#define EntityFile_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../GLM.h" +#include "Util/XercesString.h" +#include "ResourceManager.h" +#include "Entity.h" +#include "ComponentInfo.h" + +class EntityFileHandler +{ + friend class EntityFileSAXHandler; +public: + // @param EntityID The entity found + // @param EntityID The parent of the entity + typedef std::function OnStartEntityCallback; + void SetStartEntityCallback(OnStartEntityCallback c) { m_OnStartEntityCallback = c; } + // @param std::string Type name of the component + typedef std::function OnStartComponentCallback; + void SetStartComponentCallback(OnStartComponentCallback c) { m_OnStartComponentCallback = c; } + // @param std::string Field name + // @param std::string Field type + typedef std::function OnStartFieldCallback; + void SetStartFieldCallback(OnStartFieldCallback c) { m_OnStartFieldCallback = c; } + // @param char* Field data + typedef std::function OnStartFieldDataCallback; + void SetStartFieldDataCallback(OnStartFieldDataCallback c) { m_OnStartFieldDataCallback = c; } + +private: + OnStartEntityCallback m_OnStartEntityCallback = nullptr; + OnStartComponentCallback m_OnStartComponentCallback = nullptr; + OnStartFieldCallback m_OnStartFieldCallback = nullptr; + OnStartFieldDataCallback m_OnStartFieldDataCallback = nullptr; +}; + +class EntityFileSAXHandler : public xercesc::DefaultHandler +{ +public: + enum class State + { + Unknown, + Entity, + Component, + ComponentField + }; + + EntityFileSAXHandler(const EntityFileHandler* handler, xercesc::SAX2XMLReader* reader) + : m_Handler(handler) + , m_Reader(reader) + { + // 0 is imaginary world entity + m_EntityStack.push(0); + } + + void startElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname, const xercesc::Attributes& attrs) override + { + std::string name = XS::ToString(_localName); + + if (m_CurrentScope == State::Unknown) { + if (name == "Entity") { + m_CurrentScope = State::Entity; + onStartEntity(attrs); + return; + } + if (name == "EntityRef") { + onStartEntityRef(attrs); + return; + } + } + + std::string uri = XS::ToString(_uri); + if (m_CurrentScope == State::Entity) { + if (uri == "components") { + m_CurrentScope = State::Component; + onStartComponent(name); + return; + } + } + + if (m_CurrentScope == State::Component) { + m_CurrentScope = State::ComponentField; + onStartComponentField(name, attrs); + return; + } + } + + void characters(const XMLCh* const chars, const XMLSize_t length) override + { + if (m_CurrentScope == State::ComponentField) { + char* transcoded = xercesc::XMLString::transcode(chars); + onFieldData(transcoded); + } + } + + void endElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname) override + { + std::string name = XS::ToString(_localName); + if (m_CurrentScope == State::Entity) { + if (name == "Entity") { + m_CurrentScope = State::Unknown; + onEndEntity(); + return; + } + } + + std::string uri = XS::ToString(_uri); + if (m_CurrentScope == State::Component) { + //if (uri == "components") { + m_CurrentScope = State::Entity; + onEndComponent(name); + return; + //} + } + + if (m_CurrentScope == State::ComponentField) { + m_CurrentScope = State::Component; + onEndComponentField(name); + return; + } + } + + void fatalError(const xercesc::SAXParseException& e) + { + XS::ToString s(e.getMessage()); + LOG_ERROR("SAXParseException: %s", ((std::string)s).c_str()); + //throw e; + } + +private: + const EntityFileHandler* m_Handler; + xercesc::SAX2XMLReader* m_Reader; + State m_CurrentScope = State::Unknown; + unsigned int m_NextEntityID = 1; + std::stack m_EntityStack; + std::map m_CurrentAttributes; + + void onStartEntity(const xercesc::Attributes& attrs) + { + EntityID parent = m_EntityStack.top(); + + // TODO: Create entity here + auto xName = attrs.getValue(XS::ToXMLCh("name")); + std::string name = XS::ToString(xName); + //LOG_DEBUG("Entity %i (%i): %s", m_NextEntityID, parent, name.c_str()); + + if (m_Handler->m_OnStartEntityCallback) { + m_Handler->m_OnStartEntityCallback(m_NextEntityID, parent); + } + + m_EntityStack.push(m_NextEntityID); + m_NextEntityID++; + } + void onEndEntity() + { + m_EntityStack.pop(); + } + void onStartEntityRef(const xercesc::Attributes& attrs) + { + std::string path = XS::ToString(attrs.getValue(XS::ToXMLCh("file"))); + + xercesc::SAX2XMLReader* parser = xercesc::XMLReaderFactory::createXMLReader(); + parser->setContentHandler(this); + parser->setErrorHandler(this); + parser->parse(path.c_str()); + delete parser; + } + void onStartComponent(std::string name) + { + //LOG_DEBUG(" Component: %s", name.c_str()); + + if (m_Handler->m_OnStartComponentCallback) { + m_Handler->m_OnStartComponentCallback(name); + } + } + void onEndComponent(std::string name) + { + } + void onStartComponentField(std::string field, const xercesc::Attributes& attrs) + { + //LOG_DEBUG(" Field: %s", field.c_str()); + + for (int i = 0; i < attrs.getLength(); i++) { + auto name = attrs.getQName(i); + auto value = attrs.getValue(name); + + //LOG_DEBUG(" %s = %s", (char*)XS::ToString(name), (char*)XS::ToString(value)); + m_CurrentAttributes[XS::ToString(name).operator std::string()] = XS::ToString(value).operator std::string(); + } + + if (m_Handler->m_OnStartFieldCallback) { + m_Handler->m_OnStartFieldCallback(field, "comment?"); + } + } + void onEndComponentField(std::string field) + { + + } + + void onFieldData(char* data) + { + //LOG_DEBUG(" Data: %s", data); + + if (m_Handler->m_OnStartFieldDataCallback) { + m_Handler->m_OnStartFieldDataCallback(data); + } + + xercesc::XMLString::release(&data); + } +}; + +class EntityFile : public Resource +{ + friend class ResourceManager; +private: + EntityFile(boost::filesystem::path path); + ~EntityFile(); + +public: + static std::size_t GetTypeStride(std::string typeName); +static void WriteElementData(const xercesc::DOMElement* element, std::string typeName, char* outData) +{ + using namespace xercesc; + + if (typeName == "Vector") { + glm::vec3 vec; + vec.x = getFloatAttribute(element, "X"); + vec.y = getFloatAttribute(element, "Y"); + vec.z = getFloatAttribute(element, "Z"); + memcpy(outData, reinterpret_cast(&vec), GetTypeStride(typeName)); + } else if (typeName == "Color") { + glm::vec4 vec; + vec.r = getFloatAttribute(element, "R"); + vec.g = getFloatAttribute(element, "G"); + vec.b = getFloatAttribute(element, "B"); + vec.a = getFloatAttribute(element, "A"); + memcpy(outData, reinterpret_cast(&vec), GetTypeStride(typeName)); + } else if (typeName == "Quaternion") { + glm::quat q; + q.x = getFloatAttribute(element, "X"); + q.y = getFloatAttribute(element, "Y"); + q.z = getFloatAttribute(element, "Z"); + q.w = getFloatAttribute(element, "W"); + memcpy(outData, reinterpret_cast(&q), GetTypeStride(typeName)); + } else if (typeName == "float") { + XSValue::Status status; + XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_float, status); + memcpy(outData, reinterpret_cast(&val->fData.fValue.f_float), GetTypeStride(typeName)); + } else if (typeName == "double") { + XSValue::Status status; + XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_double, status); + memcpy(outData, reinterpret_cast(&val->fData.fValue.f_double), GetTypeStride(typeName)); + } else if (typeName == "bool") { + XSValue::Status status; + XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_boolean, status); + memcpy(outData, reinterpret_cast(&val->fData.fValue.f_bool), GetTypeStride(typeName)); + } else { + XSValue::DataType dataType = XSValue::getDataType(XS::ToXMLCh(typeName)); + if (dataType == XSValue::DataType::dt_string) { + char* str = XMLString::transcode(element->getTextContent()); + std::string standardString(str); + new (outData) std::string(str); + XMLString::release(&str); + //memcpy(outData, reinterpret_cast(&standardString), getTypeStride(typeName)); + } else { + //XSValue::Status status; + //XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status); + //memcpy(outData, reinterpret_cast(&val->fData.fValue), getTypeStride(typeName)); + LOG_WARNING("Unknown native data type: %s", typeName.c_str()); + } + } +} + + void Parse(const EntityFileHandler* handler); + //const std::map& ComponentInfo() const { return m_ComponentInfo; } + //const std::vector& EntityReferences() const { return m_EntityReferences; } + + xercesc::XMLGrammarPool* GrammarPool() const { return m_GrammarPool; } + +private: + boost::filesystem::path m_FilePath; + xercesc::XMLGrammarPool* m_GrammarPool; + xercesc::SAX2XMLReader* m_SAX2XMLReader; + //std::map m_ComponentInfo; + //std::vector m_EntityReferences; +static float getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) +{ + using namespace xercesc; + XSValue::Status status; + XSValue* val = XSValue::getActualValue(element->getAttribute(XS::ToXMLCh(attribute)), xercesc::XSValue::DataType::dt_double, status); + if (val == nullptr) { + LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", ((std::string)XS::ToString(element->getTagName())).c_str(), attribute); + return 0.f; + } else { + return static_cast(val->fData.fValue.f_double); + } +} +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h new file mode 100644 index 00000000..9df46722 --- /dev/null +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Util/XercesString.h" +#include "ResourceManager.h" +#include "World.h" +#include "EntityFile.h" + +class EntityFilePreprocessor +{ +public: + EntityFilePreprocessor(std::string path) + : m_FilePath(path) + { + m_EntityFile = ResourceManager::Load(path); + EntityFileHandler handler; + handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1)); + m_EntityFile->Parse(&handler); + + LOG_DEBUG("___ COMPONENT DEFINITIONS ___"); + for (auto& kv : m_ComponentCounts) { + LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second); + } + + parseComponentInfo(); + + for (auto& kv : m_ComponentInfo) { + auto& info = kv.second; + LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str()); + LOG_DEBUG("Stride: %i", info.Meta.Stride); + LOG_DEBUG("Allocation: %i", info.Meta.Allocation); + for (auto& kv : info.FieldTypes) { + LOG_DEBUG("\t%i\t%s %s", info.FieldOffsets[kv.first], kv.second.c_str(), kv.first.c_str()); + } + } + + parseDefaults(); + } + + void RegisterComponents(World* world) + { + for (auto& kv : m_ComponentInfo) { + world->RegisterComponent(kv.second); + } + } + +private: + std::string m_FilePath; + EntityFile* m_EntityFile; + std::map m_ComponentCounts; + std::map m_ComponentInfo; + + void onStartComponent(std::string type) + { + //LOG_DEBUG("Component: %s", type.c_str()); + m_ComponentCounts[type]++; + } + + void parseComponentInfo() + { + using namespace xercesc; + auto grammarPool = m_EntityFile->GrammarPool(); + bool whateverTheFuckThisIs; + auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs); + + // Find component xsd element declarations + std::cout << "Enumerating components..." << std::endl; + // + auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION); + for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { + auto element = static_cast(topLevelElements->item(i)); + + std::string nameSpace = XS::ToString(element->getNamespace()); + if (nameSpace != "components") { + continue; + } + + ComponentInfo compInfo; + + // Name + compInfo.Name = XS::ToString(element->getName()); + // Known allocation + compInfo.Meta.Allocation = m_ComponentCounts[compInfo.Name]; + // Annotation + auto componentAnnotation = element->getAnnotation(); + if (componentAnnotation != nullptr) { + // Parse annotation XML + char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString()); + MemBufInputSource annotationInput(reinterpret_cast(annotationString), strlen(annotationString), "MemBuf: Annotation String"); + XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool); + //parser.setErrorHandler(m_ErrorHandler); + parser.parse(annotationInput); + XMLString::release(&annotationString); + auto doc = parser.getDocument(); + + // TODO: Add allocation estimations from external file on map-to-map basis + // Add allocation estimation(s) + //auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation")); + //for (int i = 0; i < allocationTags->getLength(); ++i) { + // auto allocation = dynamic_cast(allocationTags->item(i)); + // auto child = allocation->getFirstChild(); + // if (child == nullptr) { + // continue; + // } + + // XSValue::Status status; + // XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status); + // compInfo.Meta.Allocation += val->fData.fValue.f_int; + //} + + // Save documentation string + auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation")); + if (documentationTags->getLength() != 0) { + auto child = documentationTags->item(0)->getFirstChild(); + if (child != nullptr) { + compInfo.Meta.Annotation = XS::ToString(child->getNodeValue()); + } + } + } else { + LOG_WARNING("Component is missing an annotation!"); + } + + // + auto typeDefinition = element->getTypeDefinition(); + if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) { + LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping."); + continue; + } + auto complexTypeDefinition = dynamic_cast(typeDefinition); + + // + auto modelGroupParticle = complexTypeDefinition->getParticle(); + if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) { + LOG_ERROR("Model group particle wasn't TERM_MODELGROUP! Skipping."); + continue; + } + auto modelGroup = modelGroupParticle->getModelGroupTerm(); + + // getParticles(); + for (unsigned int i = 0; i < particles->size(); ++i) { + auto particle = particles->elementAt(i); + if (particle->getTermType() != XSParticle::TERM_ELEMENT) { + LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping."); + continue; + } + auto elementDeclaration = particle->getElementTerm(); + + std::string name = XS::ToString(elementDeclaration->getName()); + std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName()); + + size_t stride = EntityFile::GetTypeStride(type); + if (stride == 0) { + std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl; + continue; + } + + compInfo.FieldTypes[name] = type; + compInfo.FieldOffsets[name] = fieldOffset; + fieldOffset += stride; + } + + compInfo.Meta.Stride = fieldOffset; + m_ComponentInfo[compInfo.Name] = compInfo; + } + } + +void parseDefaults() +{ + using namespace xercesc; + + for (auto& ci : m_ComponentInfo) { + // Allocate memory for default values + ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); + memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); + + XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); + //parser.setErrorHandler(m_ErrorHandler); + + std::string componentName = ci.first; + LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); + boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml"; + + parser.parse(defaultsFile.string().c_str()); + auto doc = parser.getDocument(); + if (doc == nullptr) { + LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str()); + continue; + } + + // Find the node in the components namespace matching the component name + std::string tagName = "c:" + componentName; + auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName)); + if (rootNodes->getLength() == 0) { + LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str()); + continue; + } + auto componentElement = dynamic_cast(rootNodes->item(0)); + + // Fill the default value buffer with values + for (auto& field : ci.second.FieldOffsets) { + std::string fieldName = field.first; + auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName)); + auto fieldNode = fieldNodes->item(0); + if (fieldNode == nullptr) { + LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str()); + continue; + } + auto fieldElement = dynamic_cast(fieldNode); + + std::string fieldType = ci.second.FieldTypes.at(fieldName); + unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); + EntityFile::WriteElementData(fieldElement, fieldType, ci.second.Defaults.get() + fieldOffset); + } + } +} +}; \ No newline at end of file diff --git a/include/Engine/Core/Util/XercesString.h b/include/Engine/Core/Util/XercesString.h new file mode 100644 index 00000000..b0b6fa76 --- /dev/null +++ b/include/Engine/Core/Util/XercesString.h @@ -0,0 +1,46 @@ +#ifndef Util_XercesString_h__ +#define Util_XercesString_h__ + +#include +#include +#include + +namespace XS +{ + +class ToString +{ +public: + ToString(const XMLCh* const str) { m_AsChar = xercesc::XMLString::transcode(str); } + ~ToString() + { + if (m_AsChar != nullptr) { + xercesc::XMLString::release(&m_AsChar); + } + } + + operator std::string() const { return std::string(m_AsChar); } +private: + char* m_AsChar = nullptr; +}; + +class ToXMLCh +{ +public: + ToXMLCh(const std::string str) { m_Transcoded = xercesc::XMLString::transcode(str.c_str()); } + ToXMLCh(const char* str) { m_Transcoded = xercesc::XMLString::transcode(str); } + ~ToXMLCh() + { + if (m_Transcoded != nullptr) { + xercesc::XMLString::release(&m_Transcoded); + } + } + + operator const XMLCh*() const { return m_Transcoded; } +private: + XMLCh* m_Transcoded = nullptr; +}; + +} + +#endif diff --git a/include/Game/Game.h b/include/Game/Game.h index 33dc88a6..3e2ff5ae 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -14,10 +14,12 @@ #include "Input/MouseInputHandler.h" #include "Core/EKeyDown.h" #include "Core/EntityXMLFile.h" +#include "Core/EntityFilePreprocessor.h" #include "Core/SystemPipeline.h" #include "RaptorCopterSystem.h" #include "PlayerSystem.h" #include "Editor/EditorSystem.h" +#include "Core/EntityFile.h" // Network #include diff --git a/resources/Schema/Entities/RaptorCopter.xml b/resources/Schema/Entities/RaptorCopter.xml new file mode 100644 index 00000000..0ee62368 --- /dev/null +++ b/resources/Schema/Entities/RaptorCopter.xml @@ -0,0 +1,68 @@ + + + + + + + + + + Models/Core/UnitRaptor.obj + + + + + + + + + + + + 20 + + + + + + + + + + + + Models/Core/UnitCylinder.obj + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 5178b525..6562f3ed 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -24,11 +24,19 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Engine/Core/EntityFile.cpp b/src/Engine/Core/EntityFile.cpp new file mode 100644 index 00000000..672d3e84 --- /dev/null +++ b/src/Engine/Core/EntityFile.cpp @@ -0,0 +1,47 @@ +#include "Core/EntityFile.h" + +EntityFile::EntityFile(boost::filesystem::path path) + : m_FilePath(path) +{ + using namespace xercesc; + XMLPlatformUtils::Initialize(); + m_GrammarPool = new XMLGrammarPoolImpl(); + m_SAX2XMLReader = XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager, m_GrammarPool); +} + +EntityFile::~EntityFile() +{ + delete m_SAX2XMLReader; + delete m_GrammarPool; + xercesc::XMLPlatformUtils::Terminate(); +} + +void EntityFile::Parse(const EntityFileHandler* handler) +{ + using namespace xercesc; + + EntityFileSAXHandler saxHandler(handler, nullptr); + m_SAX2XMLReader->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true); + m_SAX2XMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true); + m_SAX2XMLReader->setContentHandler(&saxHandler); + m_SAX2XMLReader->setErrorHandler(&saxHandler); + m_SAX2XMLReader->setDeclarationHandler(&saxHandler); + m_SAX2XMLReader->parse(m_FilePath.string().c_str()); +} + +std::size_t EntityFile::GetTypeStride(std::string typeName) +{ + std::map typeStrides{ + { "bool", sizeof(bool) }, + { "int", sizeof(int) }, + { "float", sizeof(float) }, + { "double", sizeof(double) }, + { "string", sizeof(std::string) }, + { "Vector", sizeof(glm::vec3) }, + { "Quaternion", sizeof(glm::quat) }, + { "Color", sizeof(glm::vec4) } + }; + + auto it = typeStrides.find(typeName); + return (it != typeStrides.end()) ? it->second : 0; +} diff --git a/src/Engine/Core/EntityXMLFile.cpp b/src/Engine/Core/EntityXMLFile.cpp index c6e00a1b..58957347 100644 --- a/src/Engine/Core/EntityXMLFile.cpp +++ b/src/Engine/Core/EntityXMLFile.cpp @@ -71,7 +71,6 @@ void EntityXMLFile::PopulateWorld(World* world) parseEntityGraph(world, root, 0); } - void EntityXMLFile::preprocess(std::string inPath, std::string outPath) { using namespace xercesc; @@ -121,7 +120,7 @@ void EntityXMLFile::parseComponentInfo() for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { auto element = static_cast(topLevelElements->item(i)); - std::string nameSpace(XSTR(element->getNamespace())); + std::string nameSpace = XSTR(element->getNamespace()); if (nameSpace != "components") { continue; } @@ -129,7 +128,7 @@ void EntityXMLFile::parseComponentInfo() ComponentInfo compInfo; // Name - compInfo.Name = XSTR(element->getName()); + compInfo.Name = (std::string)XSTR(element->getName()); // Annotation auto componentAnnotation = element->getAnnotation(); if (componentAnnotation != nullptr) { @@ -345,69 +344,27 @@ void EntityXMLFile::parseEntityGraph(World* world, xercesc::DOMElement* element, } } - // Recurse children + // Recurse children entities auto children = m_DOMDocument->evaluate(XSTR("Children/Entity"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr); for (int i = 0; i < children->getSnapshotLength(); i++) { children->snapshotItem(i); parseEntityGraph(world, dynamic_cast(children->getNodeValue()), entity); } - //auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*")); - //for (int i = 0; i < components->getLength(); ++i) { - // auto component = dynamic_cast(components->item(i)); + // Recurse children entity references + auto refs = m_DOMDocument->evaluate(XSTR("Children/EntityRef"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr); + for (int i = 0; i < refs->getSnapshotLength(); i++) { + refs->snapshotItem(i); + auto entityRefElement = dynamic_cast(refs->getNodeValue()); + auto attribute = entityRefElement->getAttribute(XSTR("file")); + if (attribute == nullptr) { + continue; + } - // std::string componentName = XSTR(component->getLocalName()); - // auto& compStore = m_ComponentStore.at(componentName); - // auto& compInfo = compStore.Info; - - // char* data = &compStore.Data[compStore.Size*compStore.Stride]; - // compStore.Size += 1; - - // auto fields = component->getChildNodes(); - // for (int j = 0; j < fields->getLength(); ++j) { - // auto field = fields->item(j); - // auto nodeType = field->getNodeType(); - // if (nodeType != DOMNode::ELEMENT_NODE) { - // continue; - // } - // //auto field = dynamic_cast(fields->item(j)); - // //const XMLCh* value = fields->item(j)->getTextContent(); - // std::string fieldName = XSTR(field->getLocalName()); - // if (compInfo.FieldTypes.find(fieldName) == compInfo.FieldTypes.end()) { - // std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl; - // continue; - // } - - // std::string fieldType = compInfo.FieldTypes.at(fieldName); - // unsigned int fieldOffset = compInfo.FieldOffsets.at(fieldName); - - // XSValue::DataType dataType = XSValue::getDataType(XSTR(fieldType.c_str())); - // if (dataType == XSValue::DataType::dt_MAXCOUNT) { - // // TODO: - // continue; - // } - // if (dataType == XSValue::DataType::dt_string) { - // char* str = XMLString::transcode(field->getTextContent()); - // std::string standardString(str); - // XMLString::release(&str); - // memcpy(&data[fieldOffset], reinterpret_cast(&standardString), getTypeStride(fieldType)); - // } else { - // XSValue::Status status; - // XSValue* val = XSValue::getActualValue(field->getTextContent(), dataType, status); - // memcpy(&data[fieldOffset], reinterpret_cast(&val->fData.fValue), getTypeStride(fieldType)); - // } - // } - //} - - //auto entities = m_DOMDocument->getElementsByTagName(XSTR("Entity")); - //for (int i = 0; i < entities->getLength(); ++i) { - // auto entity = dynamic_cast(entities->item(i)); - - - // //entity->setIdAttribute() - - // std::cout << "ENTITY " << i + 1 << std::endl; - //} + std::string file((const char*)XSTR(attribute)); + boost::filesystem::path absolutePath = boost::filesystem::path(m_EntityFile).parent_path() / file; + ResourceManager::Load(absolutePath.string())->PopulateWorld(world); + } } std::size_t EntityXMLFile::getTypeStride(std::string typeName) @@ -491,5 +448,4 @@ void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string ty LOG_WARNING("Unknown native data type: %s", typeName.c_str()); } } -} - +} \ No newline at end of file diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 5443d388..9957e0ec 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -55,6 +55,7 @@ void World::RegisterComponent(ComponentInfo& ci) ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType) { + // TODO: Allocate dynamic pool if component isn't registered ComponentPool* pool = m_ComponentPools.at(componentType); const ComponentInfo& ci = pool->ComponentInfo(); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 7d294355..e349bb56 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -10,6 +10,7 @@ Game::Game(int argc, char* argv[]) ResourceManager::RegisterType("Texture"); ResourceManager::RegisterType("EntityXMLFile"); ResourceManager::RegisterType("ShaderProgram"); + ResourceManager::RegisterType("EntityFile"); m_Config = ResourceManager::Load("Config.ini"); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); @@ -48,7 +49,9 @@ Game::Game(int argc, char* argv[]) m_World = new World(); std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); if (!mapToLoad.empty()) { - ResourceManager::Load(mapToLoad)->PopulateWorld(m_World); + //ResourceManager::Load(mapToLoad)->PopulateWorld(m_World); + EntityFilePreprocessor fp(mapToLoad); + fp.RegisterComponents(m_World); } // Create system pipeline From 03456ad73152a473e34ca90652908e7deb6a62da Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 7 Jan 2016 13:10:44 +0100 Subject: [PATCH 02/18] EntityFileParser --- include/Engine/Core/EntityFile.h | 68 ++++++------ include/Engine/Core/EntityFileParser.h | 108 +++++++++++++++++++ include/Engine/Core/EntityFilePreprocessor.h | 4 +- include/Game/Game.h | 1 + src/Game/Game.cpp | 8 +- 5 files changed, 153 insertions(+), 36 deletions(-) create mode 100644 include/Engine/Core/EntityFileParser.h diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index bca0bf96..7f02ed8b 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -37,15 +37,21 @@ public: // @param EntityID The parent of the entity typedef std::function OnStartEntityCallback; void SetStartEntityCallback(OnStartEntityCallback c) { m_OnStartEntityCallback = c; } + // @param EntityID The entity the component corresponds to // @param std::string Type name of the component - typedef std::function OnStartComponentCallback; + typedef std::function OnStartComponentCallback; void SetStartComponentCallback(OnStartComponentCallback c) { m_OnStartComponentCallback = c; } + // @param EntityID Entity + // @param std::string Component name // @param std::string Field name - // @param std::string Field type - typedef std::function OnStartFieldCallback; + // @param std::map Field attribute names and values + typedef std::function)> OnStartFieldCallback; void SetStartFieldCallback(OnStartFieldCallback c) { m_OnStartFieldCallback = c; } + // @param EntityID Entity + // @param std::string Component name + // @param std::string Field name // @param char* Field data - typedef std::function OnStartFieldDataCallback; + typedef std::function OnStartFieldDataCallback; void SetStartFieldDataCallback(OnStartFieldDataCallback c) { m_OnStartFieldDataCallback = c; } private: @@ -72,15 +78,16 @@ public: { // 0 is imaginary world entity m_EntityStack.push(0); + m_StateStack.push(State::Unknown); } void startElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname, const xercesc::Attributes& attrs) override { std::string name = XS::ToString(_localName); - if (m_CurrentScope == State::Unknown) { + if (m_StateStack.top() == State::Unknown || m_StateStack.top() == State::Entity) { if (name == "Entity") { - m_CurrentScope = State::Entity; + m_StateStack.push(State::Entity); onStartEntity(attrs); return; } @@ -91,16 +98,16 @@ public: } std::string uri = XS::ToString(_uri); - if (m_CurrentScope == State::Entity) { + if (m_StateStack.top() == State::Entity) { if (uri == "components") { - m_CurrentScope = State::Component; + m_StateStack.push(State::Component); onStartComponent(name); return; } } - if (m_CurrentScope == State::Component) { - m_CurrentScope = State::ComponentField; + if (m_StateStack.top() == State::Component) { + m_StateStack.push(State::ComponentField); onStartComponentField(name, attrs); return; } @@ -108,7 +115,7 @@ public: void characters(const XMLCh* const chars, const XMLSize_t length) override { - if (m_CurrentScope == State::ComponentField) { + if (m_StateStack.top() == State::ComponentField) { char* transcoded = xercesc::XMLString::transcode(chars); onFieldData(transcoded); } @@ -117,25 +124,25 @@ public: void endElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname) override { std::string name = XS::ToString(_localName); - if (m_CurrentScope == State::Entity) { + if (m_StateStack.top() == State::Entity) { if (name == "Entity") { - m_CurrentScope = State::Unknown; + m_StateStack.pop(); onEndEntity(); return; } } std::string uri = XS::ToString(_uri); - if (m_CurrentScope == State::Component) { + if (m_StateStack.top() == State::Component) { //if (uri == "components") { - m_CurrentScope = State::Entity; + m_StateStack.pop(); onEndComponent(name); return; //} } - if (m_CurrentScope == State::ComponentField) { - m_CurrentScope = State::Component; + if (m_StateStack.top() == State::ComponentField) { + m_StateStack.pop(); onEndComponentField(name); return; } @@ -150,10 +157,14 @@ public: private: const EntityFileHandler* m_Handler; + xercesc::XMLGrammarPool* m_GrammarPool; xercesc::SAX2XMLReader* m_Reader; - State m_CurrentScope = State::Unknown; + //State m_CurrentScope = State::Unknown; + std::stack m_StateStack; unsigned int m_NextEntityID = 1; std::stack m_EntityStack; + std::string m_CurrentComponent; + std::string m_CurrentField; std::map m_CurrentAttributes; void onStartEntity(const xercesc::Attributes& attrs) @@ -189,41 +200,36 @@ private: void onStartComponent(std::string name) { //LOG_DEBUG(" Component: %s", name.c_str()); - + m_CurrentComponent = name; if (m_Handler->m_OnStartComponentCallback) { - m_Handler->m_OnStartComponentCallback(name); + m_Handler->m_OnStartComponentCallback(m_EntityStack.top(), name); } } - void onEndComponent(std::string name) - { - } + void onEndComponent(std::string name) { } void onStartComponentField(std::string field, const xercesc::Attributes& attrs) { //LOG_DEBUG(" Field: %s", field.c_str()); - + m_CurrentField = field; + m_CurrentAttributes.clear(); for (int i = 0; i < attrs.getLength(); i++) { auto name = attrs.getQName(i); auto value = attrs.getValue(name); - //LOG_DEBUG(" %s = %s", (char*)XS::ToString(name), (char*)XS::ToString(value)); m_CurrentAttributes[XS::ToString(name).operator std::string()] = XS::ToString(value).operator std::string(); } if (m_Handler->m_OnStartFieldCallback) { - m_Handler->m_OnStartFieldCallback(field, "comment?"); + m_Handler->m_OnStartFieldCallback(m_EntityStack.top(), m_CurrentComponent, field, m_CurrentAttributes); } } - void onEndComponentField(std::string field) - { - - } + void onEndComponentField(std::string field) { } void onFieldData(char* data) { //LOG_DEBUG(" Data: %s", data); if (m_Handler->m_OnStartFieldDataCallback) { - m_Handler->m_OnStartFieldDataCallback(data); + m_Handler->m_OnStartFieldDataCallback(m_EntityStack.top(), m_CurrentComponent, m_CurrentField, data); } xercesc::XMLString::release(&data); diff --git a/include/Engine/Core/EntityFileParser.h b/include/Engine/Core/EntityFileParser.h new file mode 100644 index 00000000..04e732d6 --- /dev/null +++ b/include/Engine/Core/EntityFileParser.h @@ -0,0 +1,108 @@ +#include "EntityFile.h" +#include "World.h" + +class EntityFileParser +{ +public: + EntityFileParser(EntityFile* entityFile) + : m_EntityFile(entityFile) + { + m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2)); + m_Handler.SetStartComponentCallback(std::bind(&EntityFileParser::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); + m_Handler.SetStartFieldCallback(std::bind(&EntityFileParser::onStartComponentField, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + } + + void MergeEntities(World* world) + { + m_World = world; + m_EntityIDMapper[0] = 0; + m_EntityFile->Parse(&m_Handler); + } + +private: + EntityFile* m_EntityFile; + EntityFileHandler m_Handler; + World* m_World = nullptr; + // Maps EntityIDs local to the file to real IDs in the world + std::map m_EntityIDMapper; + + void onStartEntity(EntityID entity, EntityID parent) + { + EntityID realParent = m_EntityIDMapper.at(parent); + EntityID realEntity = m_World->CreateEntity(realParent); + m_EntityIDMapper[entity] = realEntity; + LOG_DEBUG("Created entity #%i (%i) with parent %i (%i)", entity, realEntity, parent, realParent); + } + + void onStartComponent(EntityID entity, std::string component) + { + EntityID realEntity = m_EntityIDMapper.at(entity); + m_World->AttachComponent(entity, component); + LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity); + } + + void onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes) + { + EntityID realEntity = m_EntityIDMapper.at(entity); + ComponentWrapper component = m_World->GetComponent(realEntity, componentType); + std::string fieldType = component.Info.FieldTypes.at(fieldName); + + LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), fieldType.c_str()); + LOG_DEBUG("Attributes:"); + for (auto& kv : attributes) { + LOG_DEBUG("\t%s = %s", kv.first.c_str(), kv.second.c_str()); + } + + char* data = component.Data + component.Info.FieldOffsets.at(fieldName); + if (fieldType == "Vector") { + glm::vec3 vec; + vec.x = boost::lexical_cast(attributes.at("X")); + vec.y = boost::lexical_cast(attributes.at("Y")); + vec.z = boost::lexical_cast(attributes.at("Z")); + memcpy(data, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "Color") { + glm::vec4 vec; + vec.r = boost::lexical_cast(attributes.at("R")); + vec.g = boost::lexical_cast(attributes.at("G")); + vec.b = boost::lexical_cast(attributes.at("B")); + vec.a = boost::lexical_cast(attributes.at("A")); + memcpy(data, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "Quaternion") { + glm::quat q; + q.x = boost::lexical_cast(attributes.at("X")); + q.y = boost::lexical_cast(attributes.at("Y")); + q.z = boost::lexical_cast(attributes.at("Z")); + q.w = boost::lexical_cast(attributes.at("W")); + memcpy(data, reinterpret_cast(&q), EntityFile::GetTypeStride(fieldType)); + } else if (!attributes.empty()) { + LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size()); + } + } + + void onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData) + { + EntityID realEntity = m_EntityIDMapper.at(entity); + ComponentWrapper component = m_World->GetComponent(realEntity, componentType); + std::string fieldType = component.Info.FieldTypes.at(fieldName); + + char* data = component.Data + component.Info.FieldOffsets.at(fieldName); + if (fieldType == "int") { + int value = boost::lexical_cast(fieldData); + memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "float") { + float value = boost::lexical_cast(fieldData); + memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "double") { + double value = boost::lexical_cast(fieldData); + memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "bool") { + bool value = (fieldData[0] == 't'); // Lazy bool evaluation + memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "string") { + new (data) std::string(fieldData); + } else { + LOG_WARNING("Unknown data type: %s", fieldType.c_str()); + } + } +}; \ No newline at end of file diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h index 9df46722..81cd6992 100644 --- a/include/Engine/Core/EntityFilePreprocessor.h +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -21,7 +21,7 @@ public: { m_EntityFile = ResourceManager::Load(path); EntityFileHandler handler; - handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1)); + handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); m_EntityFile->Parse(&handler); LOG_DEBUG("___ COMPONENT DEFINITIONS ___"); @@ -57,7 +57,7 @@ private: std::map m_ComponentCounts; std::map m_ComponentInfo; - void onStartComponent(std::string type) + void onStartComponent(EntityID entity, std::string type) { //LOG_DEBUG("Component: %s", type.c_str()); m_ComponentCounts[type]++; diff --git a/include/Game/Game.h b/include/Game/Game.h index 3e2ff5ae..e82369e0 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -20,6 +20,7 @@ #include "PlayerSystem.h" #include "Editor/EditorSystem.h" #include "Core/EntityFile.h" +#include "Core/EntityFileParser.h" // Network #include diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index e349bb56..c2957d02 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -49,9 +49,11 @@ Game::Game(int argc, char* argv[]) m_World = new World(); std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); if (!mapToLoad.empty()) { - //ResourceManager::Load(mapToLoad)->PopulateWorld(m_World); - EntityFilePreprocessor fp(mapToLoad); - fp.RegisterComponents(m_World); + auto file = ResourceManager::Load(mapToLoad); + EntityFilePreprocessor fpp(mapToLoad); + fpp.RegisterComponents(m_World); + EntityFileParser fp(file); + fp.MergeEntities(m_World); } // Create system pipeline From ee83394678f286a963bf053b6b72a1a9138bd75a Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 7 Jan 2016 14:06:14 +0100 Subject: [PATCH 03/18] Unified some previously duplicated code --- include/Engine/Core/EntityFile.h | 95 +++++++++---------- include/Engine/Core/EntityFileParser.h | 42 +-------- include/Engine/Core/EntityFilePreprocessor.h | 99 ++++++++++++-------- 3 files changed, 107 insertions(+), 129 deletions(-) diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index 7f02ed8b..4c2c7b3e 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -2,6 +2,7 @@ #define EntityFile_h__ #include +#include #include #include #include @@ -245,58 +246,54 @@ private: public: static std::size_t GetTypeStride(std::string typeName); -static void WriteElementData(const xercesc::DOMElement* element, std::string typeName, char* outData) -{ - using namespace xercesc; - if (typeName == "Vector") { - glm::vec3 vec; - vec.x = getFloatAttribute(element, "X"); - vec.y = getFloatAttribute(element, "Y"); - vec.z = getFloatAttribute(element, "Z"); - memcpy(outData, reinterpret_cast(&vec), GetTypeStride(typeName)); - } else if (typeName == "Color") { - glm::vec4 vec; - vec.r = getFloatAttribute(element, "R"); - vec.g = getFloatAttribute(element, "G"); - vec.b = getFloatAttribute(element, "B"); - vec.a = getFloatAttribute(element, "A"); - memcpy(outData, reinterpret_cast(&vec), GetTypeStride(typeName)); - } else if (typeName == "Quaternion") { - glm::quat q; - q.x = getFloatAttribute(element, "X"); - q.y = getFloatAttribute(element, "Y"); - q.z = getFloatAttribute(element, "Z"); - q.w = getFloatAttribute(element, "W"); - memcpy(outData, reinterpret_cast(&q), GetTypeStride(typeName)); - } else if (typeName == "float") { - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_float, status); - memcpy(outData, reinterpret_cast(&val->fData.fValue.f_float), GetTypeStride(typeName)); - } else if (typeName == "double") { - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_double, status); - memcpy(outData, reinterpret_cast(&val->fData.fValue.f_double), GetTypeStride(typeName)); - } else if (typeName == "bool") { - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_boolean, status); - memcpy(outData, reinterpret_cast(&val->fData.fValue.f_bool), GetTypeStride(typeName)); - } else { - XSValue::DataType dataType = XSValue::getDataType(XS::ToXMLCh(typeName)); - if (dataType == XSValue::DataType::dt_string) { - char* str = XMLString::transcode(element->getTextContent()); - std::string standardString(str); - new (outData) std::string(str); - XMLString::release(&str); - //memcpy(outData, reinterpret_cast(&standardString), getTypeStride(typeName)); - } else { - //XSValue::Status status; - //XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status); - //memcpy(outData, reinterpret_cast(&val->fData.fValue), getTypeStride(typeName)); - LOG_WARNING("Unknown native data type: %s", typeName.c_str()); + static void WriteAttributeData(char* outData, const std::string fieldType, const std::map& attributes) + { + if (fieldType == "Vector") { + glm::vec3 vec; + vec.x = boost::lexical_cast(attributes.at("X")); + vec.y = boost::lexical_cast(attributes.at("Y")); + vec.z = boost::lexical_cast(attributes.at("Z")); + memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "Color") { + glm::vec4 vec; + vec.r = boost::lexical_cast(attributes.at("R")); + vec.g = boost::lexical_cast(attributes.at("G")); + vec.b = boost::lexical_cast(attributes.at("B")); + vec.a = boost::lexical_cast(attributes.at("A")); + memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "Quaternion") { + glm::quat q; + q.x = boost::lexical_cast(attributes.at("X")); + q.y = boost::lexical_cast(attributes.at("Y")); + q.z = boost::lexical_cast(attributes.at("Z")); + q.w = boost::lexical_cast(attributes.at("W")); + memcpy(outData, reinterpret_cast(&q), EntityFile::GetTypeStride(fieldType)); + } else if (!attributes.empty()) { + LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size()); + } + } + + static void WriteValueData(char* outData, const std::string fieldType, const char* valueData) + { + if (fieldType == "int") { + int value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "float") { + float value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "double") { + double value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "bool") { + bool value = (valueData[0] == 't'); // Lazy bool evaluation + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "string") { + new (outData) std::string(valueData); + } else { + LOG_WARNING("Unknown value data type: %s", fieldType.c_str()); } } -} void Parse(const EntityFileHandler* handler); //const std::map& ComponentInfo() const { return m_ComponentInfo; } diff --git a/include/Engine/Core/EntityFileParser.h b/include/Engine/Core/EntityFileParser.h index 04e732d6..4a989719 100644 --- a/include/Engine/Core/EntityFileParser.h +++ b/include/Engine/Core/EntityFileParser.h @@ -55,29 +55,7 @@ private: } char* data = component.Data + component.Info.FieldOffsets.at(fieldName); - if (fieldType == "Vector") { - glm::vec3 vec; - vec.x = boost::lexical_cast(attributes.at("X")); - vec.y = boost::lexical_cast(attributes.at("Y")); - vec.z = boost::lexical_cast(attributes.at("Z")); - memcpy(data, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "Color") { - glm::vec4 vec; - vec.r = boost::lexical_cast(attributes.at("R")); - vec.g = boost::lexical_cast(attributes.at("G")); - vec.b = boost::lexical_cast(attributes.at("B")); - vec.a = boost::lexical_cast(attributes.at("A")); - memcpy(data, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "Quaternion") { - glm::quat q; - q.x = boost::lexical_cast(attributes.at("X")); - q.y = boost::lexical_cast(attributes.at("Y")); - q.z = boost::lexical_cast(attributes.at("Z")); - q.w = boost::lexical_cast(attributes.at("W")); - memcpy(data, reinterpret_cast(&q), EntityFile::GetTypeStride(fieldType)); - } else if (!attributes.empty()) { - LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size()); - } + EntityFile::WriteAttributeData(data, fieldType, attributes); } void onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData) @@ -87,22 +65,6 @@ private: std::string fieldType = component.Info.FieldTypes.at(fieldName); char* data = component.Data + component.Info.FieldOffsets.at(fieldName); - if (fieldType == "int") { - int value = boost::lexical_cast(fieldData); - memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "float") { - float value = boost::lexical_cast(fieldData); - memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "double") { - double value = boost::lexical_cast(fieldData); - memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "bool") { - bool value = (fieldData[0] == 't'); // Lazy bool evaluation - memcpy(data, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "string") { - new (data) std::string(fieldData); - } else { - LOG_WARNING("Unknown data type: %s", fieldType.c_str()); - } + EntityFile::WriteValueData(data, fieldType, fieldData); } }; \ No newline at end of file diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h index 81cd6992..e0e4cd52 100644 --- a/include/Engine/Core/EntityFilePreprocessor.h +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -174,53 +174,72 @@ private: } } -void parseDefaults() -{ - using namespace xercesc; + void parseDefaults() + { + using namespace xercesc; - for (auto& ci : m_ComponentInfo) { - // Allocate memory for default values - ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); - memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); + for (auto& ci : m_ComponentInfo) { + // Allocate memory for default values + ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); + memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); - XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); - //parser.setErrorHandler(m_ErrorHandler); + XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); + //parser.setErrorHandler(m_ErrorHandler); - std::string componentName = ci.first; - LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); - boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml"; + std::string componentName = ci.first; + LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); + boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml"; - parser.parse(defaultsFile.string().c_str()); - auto doc = parser.getDocument(); - if (doc == nullptr) { - LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str()); - continue; - } - - // Find the node in the components namespace matching the component name - std::string tagName = "c:" + componentName; - auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName)); - if (rootNodes->getLength() == 0) { - LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str()); - continue; - } - auto componentElement = dynamic_cast(rootNodes->item(0)); - - // Fill the default value buffer with values - for (auto& field : ci.second.FieldOffsets) { - std::string fieldName = field.first; - auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName)); - auto fieldNode = fieldNodes->item(0); - if (fieldNode == nullptr) { - LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str()); + parser.parse(defaultsFile.string().c_str()); + auto doc = parser.getDocument(); + if (doc == nullptr) { + LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str()); continue; } - auto fieldElement = dynamic_cast(fieldNode); - std::string fieldType = ci.second.FieldTypes.at(fieldName); - unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); - EntityFile::WriteElementData(fieldElement, fieldType, ci.second.Defaults.get() + fieldOffset); + // Find the node in the components namespace matching the component name + std::string tagName = "c:" + componentName; + auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName)); + if (rootNodes->getLength() == 0) { + LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str()); + continue; + } + auto componentElement = dynamic_cast(rootNodes->item(0)); + + // Fill the default value buffer with values + for (auto& field : ci.second.FieldOffsets) { + std::string fieldName = field.first; + auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName)); + auto fieldNode = fieldNodes->item(0); + if (fieldNode == nullptr) { + LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str()); + continue; + } + auto fieldElement = dynamic_cast(fieldNode); + + std::string fieldType = ci.second.FieldTypes.at(fieldName); + unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); + char* data = ci.second.Defaults.get() + fieldOffset; + + // Handle potential field attributes + if (fieldElement->hasAttributes()) { + std::map attributes; + auto attributeMap = fieldElement->getAttributes(); + for (int i = 0; i < attributeMap->getLength(); ++i) { + auto attribItem = attributeMap->item(i); + attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue()); + } + EntityFile::WriteAttributeData(data, fieldType, attributes); + } + + // Handle potential field values + auto childNode = fieldElement->getFirstChild(); + if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) { + char* cstrValue = XMLString::transcode(childNode->getNodeValue()); + EntityFile::WriteValueData(data, fieldType, cstrValue); + XMLString::release(&cstrValue); + } + } } } -} }; \ No newline at end of file From 3e7f27e1fedc011ca4ae4a315966168edb41ae88 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 7 Jan 2016 14:30:39 +0100 Subject: [PATCH 04/18] Added error handler for DOM parsers --- include/Engine/Core/EntityFile.h | 29 ++++++++++++++++++++ include/Engine/Core/EntityFilePreprocessor.h | 7 +++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index 4c2c7b3e..42a3891d 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -237,6 +237,35 @@ private: } }; +class EntityFileXMLErrorHandler : public xercesc::ErrorHandler +{ +public: + void warning(const xercesc::SAXParseException& e) override + { + reportParseException("Warning", e); + } + void error(const xercesc::SAXParseException& e) override + { + reportParseException("Error", e); + } + void fatalError(const xercesc::SAXParseException& e) override + { + reportParseException("FATAL ERROR", e); + } + void resetErrors() override { } + +private: + void reportParseException(std::string type, const xercesc::SAXParseException& e) + { + char* message = xercesc::XMLString::transcode(e.getMessage()); + char* systemID = xercesc::XMLString::transcode(e.getSystemId()); + std::cerr << systemID << ":" << e.getLineNumber() << ":" << e.getColumnNumber() << std::endl; + std::cerr << type << ": " << message << std::endl; + xercesc::XMLString::release(&systemID); + xercesc::XMLString::release(&message); + } +}; + class EntityFile : public Resource { friend class ResourceManager; diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h index e0e4cd52..6bee4636 100644 --- a/include/Engine/Core/EntityFilePreprocessor.h +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -66,6 +66,7 @@ private: void parseComponentInfo() { using namespace xercesc; + EntityFileXMLErrorHandler errorHandler; auto grammarPool = m_EntityFile->GrammarPool(); bool whateverTheFuckThisIs; auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs); @@ -95,7 +96,7 @@ private: char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString()); MemBufInputSource annotationInput(reinterpret_cast(annotationString), strlen(annotationString), "MemBuf: Annotation String"); XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool); - //parser.setErrorHandler(m_ErrorHandler); + parser.setErrorHandler(&errorHandler); parser.parse(annotationInput); XMLString::release(&annotationString); auto doc = parser.getDocument(); @@ -178,13 +179,15 @@ private: { using namespace xercesc; + EntityFileXMLErrorHandler errorHandler; + for (auto& ci : m_ComponentInfo) { // Allocate memory for default values ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); - //parser.setErrorHandler(m_ErrorHandler); + parser.setErrorHandler(&errorHandler); std::string componentName = ci.first; LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); From 23e78c4947b85e3c48949ae0f88229eb26c24812 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 7 Jan 2016 14:43:54 +0100 Subject: [PATCH 05/18] Cleaned up code --- include/Engine/Core/EntityFile.h | 68 +-- include/Engine/Core/EntityFileParser.h | 65 +-- include/Engine/Core/EntityFilePreprocessor.h | 229 +--------- include/Engine/Core/EntityXMLFile.h | 141 ------ include/Game/Game.h | 1 - src/Engine/Core/EntityFile.cpp | 50 +- src/Engine/Core/EntityFileParser.cpp | 58 +++ src/Engine/Core/EntityFilePreprocessor.cpp | 225 +++++++++ src/Engine/Core/EntityXMLFile.cpp | 451 ------------------- src/Game/Game.cpp | 3 +- 10 files changed, 352 insertions(+), 939 deletions(-) delete mode 100644 include/Engine/Core/EntityXMLFile.h create mode 100644 src/Engine/Core/EntityFileParser.cpp create mode 100644 src/Engine/Core/EntityFilePreprocessor.cpp delete mode 100644 src/Engine/Core/EntityXMLFile.cpp diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index 42a3891d..3bee9a5b 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -275,79 +275,19 @@ private: public: static std::size_t GetTypeStride(std::string typeName); - - static void WriteAttributeData(char* outData, const std::string fieldType, const std::map& attributes) - { - if (fieldType == "Vector") { - glm::vec3 vec; - vec.x = boost::lexical_cast(attributes.at("X")); - vec.y = boost::lexical_cast(attributes.at("Y")); - vec.z = boost::lexical_cast(attributes.at("Z")); - memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "Color") { - glm::vec4 vec; - vec.r = boost::lexical_cast(attributes.at("R")); - vec.g = boost::lexical_cast(attributes.at("G")); - vec.b = boost::lexical_cast(attributes.at("B")); - vec.a = boost::lexical_cast(attributes.at("A")); - memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "Quaternion") { - glm::quat q; - q.x = boost::lexical_cast(attributes.at("X")); - q.y = boost::lexical_cast(attributes.at("Y")); - q.z = boost::lexical_cast(attributes.at("Z")); - q.w = boost::lexical_cast(attributes.at("W")); - memcpy(outData, reinterpret_cast(&q), EntityFile::GetTypeStride(fieldType)); - } else if (!attributes.empty()) { - LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size()); - } - } - - static void WriteValueData(char* outData, const std::string fieldType, const char* valueData) - { - if (fieldType == "int") { - int value = boost::lexical_cast(valueData); - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "float") { - float value = boost::lexical_cast(valueData); - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "double") { - double value = boost::lexical_cast(valueData); - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "bool") { - bool value = (valueData[0] == 't'); // Lazy bool evaluation - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "string") { - new (outData) std::string(valueData); - } else { - LOG_WARNING("Unknown value data type: %s", fieldType.c_str()); - } - } - - void Parse(const EntityFileHandler* handler); - //const std::map& ComponentInfo() const { return m_ComponentInfo; } - //const std::vector& EntityReferences() const { return m_EntityReferences; } + static void WriteAttributeData(char* outData, const std::string fieldType, const std::map& attributes); + static void WriteValueData(char* outData, const std::string fieldType, const char* valueData); xercesc::XMLGrammarPool* GrammarPool() const { return m_GrammarPool; } + void Parse(const EntityFileHandler* handler) const; + private: boost::filesystem::path m_FilePath; xercesc::XMLGrammarPool* m_GrammarPool; xercesc::SAX2XMLReader* m_SAX2XMLReader; //std::map m_ComponentInfo; //std::vector m_EntityReferences; -static float getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) -{ - using namespace xercesc; - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getAttribute(XS::ToXMLCh(attribute)), xercesc::XSValue::DataType::dt_double, status); - if (val == nullptr) { - LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", ((std::string)XS::ToString(element->getTagName())).c_str(), attribute); - return 0.f; - } else { - return static_cast(val->fData.fValue.f_double); - } -} }; #endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFileParser.h b/include/Engine/Core/EntityFileParser.h index 4a989719..d8e51a8b 100644 --- a/include/Engine/Core/EntityFileParser.h +++ b/include/Engine/Core/EntityFileParser.h @@ -4,67 +4,20 @@ class EntityFileParser { public: - EntityFileParser(EntityFile* entityFile) - : m_EntityFile(entityFile) - { - m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2)); - m_Handler.SetStartComponentCallback(std::bind(&EntityFileParser::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); - m_Handler.SetStartFieldCallback(std::bind(&EntityFileParser::onStartComponentField, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - } + EntityFileParser(const EntityFile* entityFile); - void MergeEntities(World* world) - { - m_World = world; - m_EntityIDMapper[0] = 0; - m_EntityFile->Parse(&m_Handler); - } + void MergeEntities(World* world); private: - EntityFile* m_EntityFile; + const EntityFile* m_EntityFile; EntityFileHandler m_Handler; World* m_World = nullptr; - // Maps EntityIDs local to the file to real IDs in the world + // Maps EntityIDs local to the file to real IDs in the world after they've been + // created in order to resolve parent-child relationships. std::map m_EntityIDMapper; - void onStartEntity(EntityID entity, EntityID parent) - { - EntityID realParent = m_EntityIDMapper.at(parent); - EntityID realEntity = m_World->CreateEntity(realParent); - m_EntityIDMapper[entity] = realEntity; - LOG_DEBUG("Created entity #%i (%i) with parent %i (%i)", entity, realEntity, parent, realParent); - } - - void onStartComponent(EntityID entity, std::string component) - { - EntityID realEntity = m_EntityIDMapper.at(entity); - m_World->AttachComponent(entity, component); - LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity); - } - - void onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes) - { - EntityID realEntity = m_EntityIDMapper.at(entity); - ComponentWrapper component = m_World->GetComponent(realEntity, componentType); - std::string fieldType = component.Info.FieldTypes.at(fieldName); - - LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), fieldType.c_str()); - LOG_DEBUG("Attributes:"); - for (auto& kv : attributes) { - LOG_DEBUG("\t%s = %s", kv.first.c_str(), kv.second.c_str()); - } - - char* data = component.Data + component.Info.FieldOffsets.at(fieldName); - EntityFile::WriteAttributeData(data, fieldType, attributes); - } - - void onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData) - { - EntityID realEntity = m_EntityIDMapper.at(entity); - ComponentWrapper component = m_World->GetComponent(realEntity, componentType); - std::string fieldType = component.Info.FieldTypes.at(fieldName); - - char* data = component.Data + component.Info.FieldOffsets.at(fieldName); - EntityFile::WriteValueData(data, fieldType, fieldData); - } + void onStartEntity(EntityID entity, EntityID parent); + void onStartComponent(EntityID entity, std::string component); + void onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes); + void onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData); }; \ No newline at end of file diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h index 6bee4636..b21edf7f 100644 --- a/include/Engine/Core/EntityFilePreprocessor.h +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -16,233 +16,16 @@ class EntityFilePreprocessor { public: - EntityFilePreprocessor(std::string path) - : m_FilePath(path) - { - m_EntityFile = ResourceManager::Load(path); - EntityFileHandler handler; - handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); - m_EntityFile->Parse(&handler); + EntityFilePreprocessor(const EntityFile* entityFile); - LOG_DEBUG("___ COMPONENT DEFINITIONS ___"); - for (auto& kv : m_ComponentCounts) { - LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second); - } - - parseComponentInfo(); - - for (auto& kv : m_ComponentInfo) { - auto& info = kv.second; - LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str()); - LOG_DEBUG("Stride: %i", info.Meta.Stride); - LOG_DEBUG("Allocation: %i", info.Meta.Allocation); - for (auto& kv : info.FieldTypes) { - LOG_DEBUG("\t%i\t%s %s", info.FieldOffsets[kv.first], kv.second.c_str(), kv.first.c_str()); - } - } - - parseDefaults(); - } - - void RegisterComponents(World* world) - { - for (auto& kv : m_ComponentInfo) { - world->RegisterComponent(kv.second); - } - } + void RegisterComponents(World* world); private: - std::string m_FilePath; - EntityFile* m_EntityFile; + const EntityFile* m_EntityFile; std::map m_ComponentCounts; std::map m_ComponentInfo; - void onStartComponent(EntityID entity, std::string type) - { - //LOG_DEBUG("Component: %s", type.c_str()); - m_ComponentCounts[type]++; - } - - void parseComponentInfo() - { - using namespace xercesc; - EntityFileXMLErrorHandler errorHandler; - auto grammarPool = m_EntityFile->GrammarPool(); - bool whateverTheFuckThisIs; - auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs); - - // Find component xsd element declarations - std::cout << "Enumerating components..." << std::endl; - // - auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION); - for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { - auto element = static_cast(topLevelElements->item(i)); - - std::string nameSpace = XS::ToString(element->getNamespace()); - if (nameSpace != "components") { - continue; - } - - ComponentInfo compInfo; - - // Name - compInfo.Name = XS::ToString(element->getName()); - // Known allocation - compInfo.Meta.Allocation = m_ComponentCounts[compInfo.Name]; - // Annotation - auto componentAnnotation = element->getAnnotation(); - if (componentAnnotation != nullptr) { - // Parse annotation XML - char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString()); - MemBufInputSource annotationInput(reinterpret_cast(annotationString), strlen(annotationString), "MemBuf: Annotation String"); - XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool); - parser.setErrorHandler(&errorHandler); - parser.parse(annotationInput); - XMLString::release(&annotationString); - auto doc = parser.getDocument(); - - // TODO: Add allocation estimations from external file on map-to-map basis - // Add allocation estimation(s) - //auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation")); - //for (int i = 0; i < allocationTags->getLength(); ++i) { - // auto allocation = dynamic_cast(allocationTags->item(i)); - // auto child = allocation->getFirstChild(); - // if (child == nullptr) { - // continue; - // } - - // XSValue::Status status; - // XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status); - // compInfo.Meta.Allocation += val->fData.fValue.f_int; - //} - - // Save documentation string - auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation")); - if (documentationTags->getLength() != 0) { - auto child = documentationTags->item(0)->getFirstChild(); - if (child != nullptr) { - compInfo.Meta.Annotation = XS::ToString(child->getNodeValue()); - } - } - } else { - LOG_WARNING("Component is missing an annotation!"); - } - - // - auto typeDefinition = element->getTypeDefinition(); - if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) { - LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping."); - continue; - } - auto complexTypeDefinition = dynamic_cast(typeDefinition); - - // - auto modelGroupParticle = complexTypeDefinition->getParticle(); - if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) { - LOG_ERROR("Model group particle wasn't TERM_MODELGROUP! Skipping."); - continue; - } - auto modelGroup = modelGroupParticle->getModelGroupTerm(); - - // getParticles(); - for (unsigned int i = 0; i < particles->size(); ++i) { - auto particle = particles->elementAt(i); - if (particle->getTermType() != XSParticle::TERM_ELEMENT) { - LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping."); - continue; - } - auto elementDeclaration = particle->getElementTerm(); - - std::string name = XS::ToString(elementDeclaration->getName()); - std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName()); - - size_t stride = EntityFile::GetTypeStride(type); - if (stride == 0) { - std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl; - continue; - } - - compInfo.FieldTypes[name] = type; - compInfo.FieldOffsets[name] = fieldOffset; - fieldOffset += stride; - } - - compInfo.Meta.Stride = fieldOffset; - m_ComponentInfo[compInfo.Name] = compInfo; - } - } - - void parseDefaults() - { - using namespace xercesc; - - EntityFileXMLErrorHandler errorHandler; - - for (auto& ci : m_ComponentInfo) { - // Allocate memory for default values - ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); - memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); - - XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); - parser.setErrorHandler(&errorHandler); - - std::string componentName = ci.first; - LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); - boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml"; - - parser.parse(defaultsFile.string().c_str()); - auto doc = parser.getDocument(); - if (doc == nullptr) { - LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str()); - continue; - } - - // Find the node in the components namespace matching the component name - std::string tagName = "c:" + componentName; - auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName)); - if (rootNodes->getLength() == 0) { - LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str()); - continue; - } - auto componentElement = dynamic_cast(rootNodes->item(0)); - - // Fill the default value buffer with values - for (auto& field : ci.second.FieldOffsets) { - std::string fieldName = field.first; - auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName)); - auto fieldNode = fieldNodes->item(0); - if (fieldNode == nullptr) { - LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str()); - continue; - } - auto fieldElement = dynamic_cast(fieldNode); - - std::string fieldType = ci.second.FieldTypes.at(fieldName); - unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); - char* data = ci.second.Defaults.get() + fieldOffset; - - // Handle potential field attributes - if (fieldElement->hasAttributes()) { - std::map attributes; - auto attributeMap = fieldElement->getAttributes(); - for (int i = 0; i < attributeMap->getLength(); ++i) { - auto attribItem = attributeMap->item(i); - attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue()); - } - EntityFile::WriteAttributeData(data, fieldType, attributes); - } - - // Handle potential field values - auto childNode = fieldElement->getFirstChild(); - if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) { - char* cstrValue = XMLString::transcode(childNode->getNodeValue()); - EntityFile::WriteValueData(data, fieldType, cstrValue); - XMLString::release(&cstrValue); - } - } - } - } + void onStartComponent(EntityID entity, std::string type); + void parseComponentInfo(); + void parseDefaults(); }; \ No newline at end of file diff --git a/include/Engine/Core/EntityXMLFile.h b/include/Engine/Core/EntityXMLFile.h deleted file mode 100644 index 6fa71dad..00000000 --- a/include/Engine/Core/EntityXMLFile.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef EntityXMLFile_h__ -#define EntityXMLFile_h__ - -#include - -#include "../Common.h" -#include "../GLM.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ResourceManager.h" -#include "Entity.h" -#include "ComponentInfo.h" -class World; - -class EntityPreprocessorXMLErrorHandler : public xercesc::DOMErrorHandler -{ -public: - bool handleError(const xercesc::DOMError &e) override - { - char* message = xercesc::XMLString::transcode(e.getMessage()); - std::cerr << "Preprocessor DOMError: " << message << std::endl; - xercesc::XMLString::release(&message); - return false; - } -}; - -class EntityParserXMLErrorHandler : public xercesc::ErrorHandler -{ -public: - void warning(const xercesc::SAXParseException& e) override - { - reportParseException("Warning", e); - } - void error(const xercesc::SAXParseException& e) override - { - reportParseException("Error", e); - } - void fatalError(const xercesc::SAXParseException& e) override - { - reportParseException("FATAL ERROR", e); - } - void resetErrors() override { } - -private: - void reportParseException(std::string type, const xercesc::SAXParseException& e) - { - char* message = xercesc::XMLString::transcode(e.getMessage()); - char* systemID = xercesc::XMLString::transcode(e.getSystemId()); - std::cerr << systemID << ":" << e.getLineNumber() << ":" << e.getColumnNumber() << std::endl; - std::cerr << type << ": " << message << std::endl; - xercesc::XMLString::release(&systemID); - xercesc::XMLString::release(&message); - } -}; - -class XSTR -{ -public: - XSTR(const XMLCh* const xmlString) - { - m_AsChar = xercesc::XMLString::transcode(xmlString); - } - - XSTR(const char* normalString) - { - m_AsXMLCh = xercesc::XMLString::transcode(normalString); - } - - ~XSTR() - { - if (m_AsChar != nullptr) { - xercesc::XMLString::release(&m_AsChar); - } - if (m_AsXMLCh != nullptr) { - xercesc::XMLString::release(&m_AsXMLCh); - } - } - - operator const char*() const { return m_AsChar; } - operator const XMLCh*() const { return m_AsXMLCh; } - -private: - char* m_AsChar = nullptr; - XMLCh* m_AsXMLCh = nullptr; -}; - -class EntityXMLFile : public Resource -{ - friend class ResourceManager; - -private: - EntityXMLFile(std::string path); - -public: - ~EntityXMLFile(); - - void PopulateWorld(World* world); - -private: - static unsigned int InstanceCount; - - std::string m_EntityFile; - xercesc::XMLGrammarPool* m_GrammarPool = nullptr; - EntityParserXMLErrorHandler* m_ErrorHandler = nullptr; - xercesc::XercesDOMParser* m_DOMParser = nullptr; - xercesc::DOMDocument* m_DOMDocument = nullptr; - std::map m_ComponentInfo; - - // Preprocesses the entity file to insert include-by-copy child entities - // TODO: Make this work in memory instead of saving to file - void preprocess(std::string inPath, std::string outPath); - - void parseComponentInfo(); - void parseDefaults(); - void predictComponentAllocation(); - void parseEntityGraph(World* world, xercesc::DOMElement* parent, EntityID parentEntity); - std::size_t getTypeStride(std::string typeName); - float getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) const; - void writeData(const xercesc::DOMElement* element, std::string typeName, char* outData); -}; - -#endif \ No newline at end of file diff --git a/include/Game/Game.h b/include/Game/Game.h index e82369e0..1c19aac2 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -13,7 +13,6 @@ #include "Input/KeyboardInputHandler.h" #include "Input/MouseInputHandler.h" #include "Core/EKeyDown.h" -#include "Core/EntityXMLFile.h" #include "Core/EntityFilePreprocessor.h" #include "Core/SystemPipeline.h" #include "RaptorCopterSystem.h" diff --git a/src/Engine/Core/EntityFile.cpp b/src/Engine/Core/EntityFile.cpp index 672d3e84..b05c9e48 100644 --- a/src/Engine/Core/EntityFile.cpp +++ b/src/Engine/Core/EntityFile.cpp @@ -16,7 +16,7 @@ EntityFile::~EntityFile() xercesc::XMLPlatformUtils::Terminate(); } -void EntityFile::Parse(const EntityFileHandler* handler) +void EntityFile::Parse(const EntityFileHandler* handler) const { using namespace xercesc; @@ -45,3 +45,51 @@ std::size_t EntityFile::GetTypeStride(std::string typeName) auto it = typeStrides.find(typeName); return (it != typeStrides.end()) ? it->second : 0; } + +void EntityFile::WriteAttributeData(char* outData, const std::string fieldType, const std::map& attributes) +{ + if (fieldType == "Vector") { + glm::vec3 vec; + vec.x = boost::lexical_cast(attributes.at("X")); + vec.y = boost::lexical_cast(attributes.at("Y")); + vec.z = boost::lexical_cast(attributes.at("Z")); + memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "Color") { + glm::vec4 vec; + vec.r = boost::lexical_cast(attributes.at("R")); + vec.g = boost::lexical_cast(attributes.at("G")); + vec.b = boost::lexical_cast(attributes.at("B")); + vec.a = boost::lexical_cast(attributes.at("A")); + memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "Quaternion") { + glm::quat q; + q.x = boost::lexical_cast(attributes.at("X")); + q.y = boost::lexical_cast(attributes.at("Y")); + q.z = boost::lexical_cast(attributes.at("Z")); + q.w = boost::lexical_cast(attributes.at("W")); + memcpy(outData, reinterpret_cast(&q), EntityFile::GetTypeStride(fieldType)); + } else if (!attributes.empty()) { + LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size()); + } +} + +void EntityFile::WriteValueData(char* outData, const std::string fieldType, const char* valueData) +{ + if (fieldType == "int") { + int value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "float") { + float value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "double") { + double value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "bool") { + bool value = (valueData[0] == 't'); // Lazy bool evaluation + memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); + } else if (fieldType == "string") { + new (outData) std::string(valueData); + } else { + LOG_WARNING("Unknown value data type: %s", fieldType.c_str()); + } +} diff --git a/src/Engine/Core/EntityFileParser.cpp b/src/Engine/Core/EntityFileParser.cpp new file mode 100644 index 00000000..e14e5508 --- /dev/null +++ b/src/Engine/Core/EntityFileParser.cpp @@ -0,0 +1,58 @@ +#include "Core/EntityFileParser.h" + +EntityFileParser::EntityFileParser(const EntityFile* entityFile) + : m_EntityFile(entityFile) +{ + m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2)); + m_Handler.SetStartComponentCallback(std::bind(&EntityFileParser::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); + m_Handler.SetStartFieldCallback(std::bind(&EntityFileParser::onStartComponentField, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); +} + +void EntityFileParser::MergeEntities(World* world) +{ + m_World = world; + m_EntityIDMapper[0] = 0; + m_EntityFile->Parse(&m_Handler); +} + +void EntityFileParser::onStartEntity(EntityID entity, EntityID parent) +{ + EntityID realParent = m_EntityIDMapper.at(parent); + EntityID realEntity = m_World->CreateEntity(realParent); + m_EntityIDMapper[entity] = realEntity; + LOG_DEBUG("Created entity #%i (%i) with parent %i (%i)", entity, realEntity, parent, realParent); +} + +void EntityFileParser::onStartComponent(EntityID entity, std::string component) +{ + EntityID realEntity = m_EntityIDMapper.at(entity); + m_World->AttachComponent(entity, component); + LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity); +} + +void EntityFileParser::onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes) +{ + EntityID realEntity = m_EntityIDMapper.at(entity); + ComponentWrapper component = m_World->GetComponent(realEntity, componentType); + std::string fieldType = component.Info.FieldTypes.at(fieldName); + + LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), fieldType.c_str()); + LOG_DEBUG("Attributes:"); + for (auto& kv : attributes) { + LOG_DEBUG("\t%s = %s", kv.first.c_str(), kv.second.c_str()); + } + + char* data = component.Data + component.Info.FieldOffsets.at(fieldName); + EntityFile::WriteAttributeData(data, fieldType, attributes); +} + +void EntityFileParser::onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData) +{ + EntityID realEntity = m_EntityIDMapper.at(entity); + ComponentWrapper component = m_World->GetComponent(realEntity, componentType); + std::string fieldType = component.Info.FieldTypes.at(fieldName); + + char* data = component.Data + component.Info.FieldOffsets.at(fieldName); + EntityFile::WriteValueData(data, fieldType, fieldData); +} diff --git a/src/Engine/Core/EntityFilePreprocessor.cpp b/src/Engine/Core/EntityFilePreprocessor.cpp new file mode 100644 index 00000000..1f6b36f1 --- /dev/null +++ b/src/Engine/Core/EntityFilePreprocessor.cpp @@ -0,0 +1,225 @@ +#include "Core/EntityFilePreprocessor.h" + +EntityFilePreprocessor::EntityFilePreprocessor(const EntityFile* entityFile) + : m_EntityFile(entityFile) +{ + EntityFileHandler handler; + handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); + m_EntityFile->Parse(&handler); + + LOG_DEBUG("___ COMPONENT DEFINITIONS ___"); + for (auto& kv : m_ComponentCounts) { + LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second); + } + + parseComponentInfo(); + + for (auto& kv : m_ComponentInfo) { + auto& info = kv.second; + LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str()); + LOG_DEBUG("Stride: %i", info.Meta.Stride); + LOG_DEBUG("Allocation: %i", info.Meta.Allocation); + for (auto& kv : info.FieldTypes) { + LOG_DEBUG("\t%i\t%s %s", info.FieldOffsets[kv.first], kv.second.c_str(), kv.first.c_str()); + } + } + + parseDefaults(); +} + +void EntityFilePreprocessor::RegisterComponents(World* world) +{ + for (auto& kv : m_ComponentInfo) { + world->RegisterComponent(kv.second); + } +} + +void EntityFilePreprocessor::onStartComponent(EntityID entity, std::string type) +{ + //LOG_DEBUG("Component: %s", type.c_str()); + m_ComponentCounts[type]++; +} + +void EntityFilePreprocessor::parseComponentInfo() +{ + using namespace xercesc; + EntityFileXMLErrorHandler errorHandler; + auto grammarPool = m_EntityFile->GrammarPool(); + bool whateverTheFuckThisIs; + auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs); + + // Find component xsd element declarations + std::cout << "Enumerating components..." << std::endl; + // + auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION); + for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { + auto element = static_cast(topLevelElements->item(i)); + + std::string nameSpace = XS::ToString(element->getNamespace()); + if (nameSpace != "components") { + continue; + } + + ComponentInfo compInfo; + + // Name + compInfo.Name = XS::ToString(element->getName()); + // Known allocation + compInfo.Meta.Allocation = m_ComponentCounts[compInfo.Name]; + // Annotation + auto componentAnnotation = element->getAnnotation(); + if (componentAnnotation != nullptr) { + // Parse annotation XML + char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString()); + MemBufInputSource annotationInput(reinterpret_cast(annotationString), strlen(annotationString), "MemBuf: Annotation String"); + XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool); + parser.setErrorHandler(&errorHandler); + parser.parse(annotationInput); + XMLString::release(&annotationString); + auto doc = parser.getDocument(); + + // TODO: Add allocation estimations from external file on map-to-map basis + // Add allocation estimation(s) + //auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation")); + //for (int i = 0; i < allocationTags->getLength(); ++i) { + // auto allocation = dynamic_cast(allocationTags->item(i)); + // auto child = allocation->getFirstChild(); + // if (child == nullptr) { + // continue; + // } + + // XSValue::Status status; + // XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status); + // compInfo.Meta.Allocation += val->fData.fValue.f_int; + //} + + // Save documentation string + auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation")); + if (documentationTags->getLength() != 0) { + auto child = documentationTags->item(0)->getFirstChild(); + if (child != nullptr) { + compInfo.Meta.Annotation = XS::ToString(child->getNodeValue()); + } + } + } else { + LOG_WARNING("Component is missing an annotation!"); + } + + // + auto typeDefinition = element->getTypeDefinition(); + if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) { + LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping."); + continue; + } + auto complexTypeDefinition = dynamic_cast(typeDefinition); + + // + auto modelGroupParticle = complexTypeDefinition->getParticle(); + if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) { + LOG_ERROR("Model group particle wasn't TERM_MODELGROUP! Skipping."); + continue; + } + auto modelGroup = modelGroupParticle->getModelGroupTerm(); + + // getParticles(); + for (unsigned int i = 0; i < particles->size(); ++i) { + auto particle = particles->elementAt(i); + if (particle->getTermType() != XSParticle::TERM_ELEMENT) { + LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping."); + continue; + } + auto elementDeclaration = particle->getElementTerm(); + + std::string name = XS::ToString(elementDeclaration->getName()); + std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName()); + + size_t stride = EntityFile::GetTypeStride(type); + if (stride == 0) { + std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl; + continue; + } + + compInfo.FieldTypes[name] = type; + compInfo.FieldOffsets[name] = fieldOffset; + fieldOffset += stride; + } + + compInfo.Meta.Stride = fieldOffset; + m_ComponentInfo[compInfo.Name] = compInfo; + } +} + +void EntityFilePreprocessor::parseDefaults() +{ + using namespace xercesc; + + EntityFileXMLErrorHandler errorHandler; + + for (auto& ci : m_ComponentInfo) { + // Allocate memory for default values + ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); + memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); + + XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); + parser.setErrorHandler(&errorHandler); + + std::string componentName = ci.first; + LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); + boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml"; + + parser.parse(defaultsFile.string().c_str()); + auto doc = parser.getDocument(); + if (doc == nullptr) { + LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str()); + continue; + } + + // Find the node in the components namespace matching the component name + std::string tagName = "c:" + componentName; + auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName)); + if (rootNodes->getLength() == 0) { + LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str()); + continue; + } + auto componentElement = dynamic_cast(rootNodes->item(0)); + + // Fill the default value buffer with values + for (auto& field : ci.second.FieldOffsets) { + std::string fieldName = field.first; + auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName)); + auto fieldNode = fieldNodes->item(0); + if (fieldNode == nullptr) { + LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str()); + continue; + } + auto fieldElement = dynamic_cast(fieldNode); + + std::string fieldType = ci.second.FieldTypes.at(fieldName); + unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); + char* data = ci.second.Defaults.get() + fieldOffset; + + // Handle potential field attributes + if (fieldElement->hasAttributes()) { + std::map attributes; + auto attributeMap = fieldElement->getAttributes(); + for (int i = 0; i < attributeMap->getLength(); ++i) { + auto attribItem = attributeMap->item(i); + attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue()); + } + EntityFile::WriteAttributeData(data, fieldType, attributes); + } + + // Handle potential field values + auto childNode = fieldElement->getFirstChild(); + if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) { + char* cstrValue = XMLString::transcode(childNode->getNodeValue()); + EntityFile::WriteValueData(data, fieldType, cstrValue); + XMLString::release(&cstrValue); + } + } + } +} + diff --git a/src/Engine/Core/EntityXMLFile.cpp b/src/Engine/Core/EntityXMLFile.cpp deleted file mode 100644 index 58957347..00000000 --- a/src/Engine/Core/EntityXMLFile.cpp +++ /dev/null @@ -1,451 +0,0 @@ -#include "Core/EntityXMLFile.h" -#include "Core/World.h" - -unsigned int EntityXMLFile::InstanceCount = 0; - -EntityXMLFile::EntityXMLFile(std::string path) - : m_EntityFile(path) -{ - using namespace xercesc; - - if (InstanceCount == 0) { - XMLPlatformUtils::Initialize(); - } - InstanceCount++; - - m_GrammarPool = new XMLGrammarPoolImpl(); - m_ErrorHandler = new EntityParserXMLErrorHandler(); - m_DOMParser = new XercesDOMParser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool); - m_DOMParser->setErrorHandler(m_ErrorHandler); - m_DOMParser->setDoNamespaces(true); - m_DOMParser->setDoXInclude(true); - m_DOMParser->setDoSchema(true); - m_DOMParser->setValidationSchemaFullChecking(true); - m_DOMParser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto); - m_DOMParser->setValidationSchemaFullChecking(true); - m_DOMParser->setValidationConstraintFatal(false); - m_DOMParser->setIncludeIgnorableWhitespace(false); - // Make sure schema grammar is kept after validation - m_DOMParser->cacheGrammarFromParse(true); - - // HACK: Use Sax2 parser instead so the entire DOM doesn't have to reside in memory - m_DOMParser->parse(m_EntityFile.c_str()); - m_DOMDocument = m_DOMParser->getDocument(); - - // 1. Fill in ComponentInfo name, fields, default values and metadata from PSVI - parseComponentInfo(); - // 2. Parse default value files for those components - parseDefaults(); - // 3. Allocate component structures - predictComponentAllocation(); -} - -EntityXMLFile::~EntityXMLFile() -{ - using namespace xercesc; - - if (m_DOMParser != nullptr) { - delete m_DOMParser; - } - if (m_ErrorHandler != nullptr) { - delete m_ErrorHandler; - } - if (m_GrammarPool != nullptr) { - delete m_GrammarPool; - } - - InstanceCount--; - if (InstanceCount == 0) { - XMLPlatformUtils::Terminate(); - } -} - -void EntityXMLFile::PopulateWorld(World* world) -{ - for (auto& pair : m_ComponentInfo) { - world->RegisterComponent(pair.second); - } - - // 4. Parse entity hierarchy - auto root = m_DOMDocument->getDocumentElement(); - parseEntityGraph(world, root, 0); -} - -void EntityXMLFile::preprocess(std::string inPath, std::string outPath) -{ - using namespace xercesc; - - static const XMLCh gLS[] = { 'L', 'S', '\0' }; - DOMImplementationLS* di = static_cast(DOMImplementationRegistry::getDOMImplementation(gLS)); - - // Parse the file - DOMLSParser* parser = di->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, nullptr); - DOMConfiguration* config = parser->getDomConfig(); - config->setParameter(XMLUni::fgDOMNamespaces, true); - config->setParameter(XMLUni::fgXercesSchema, true); - config->setParameter(XMLUni::fgXercesHandleMultipleImports, true); - config->setParameter(XMLUni::fgXercesSchemaFullChecking, true); - config->setParameter(XMLUni::fgXercesDoXInclude, true); - auto errHandler = new EntityPreprocessorXMLErrorHandler(); - config->setParameter(XMLUni::fgDOMErrorHandler, errHandler); - - auto source = new LocalFileInputSource(XSTR(inPath.c_str())); - Wrapper4InputSource* domSourceWrapper = new Wrapper4InputSource(source); - DOMDocument* doc = parser->parse(dynamic_cast(domSourceWrapper)); - - // Serialize and output the new XML - DOMLSSerializer* writer = di->createLSSerializer(); - DOMLSOutput* output = di->createLSOutput(); - XMLFormatTarget* formatTarget = new LocalFileFormatTarget(outPath.c_str()); - // TODO: MemBufFormatTarget* formatTarget = new MemBufFormatTarget() - output->setByteStream(formatTarget); - writer->write(doc, output); - - delete formatTarget; - output->release(); - writer->release(); - parser->release(); -} - -void EntityXMLFile::parseComponentInfo() -{ - using namespace xercesc; - bool wasChanged; - XSModel* xsModel = m_GrammarPool->getXSModel(wasChanged); - - // Find component xsd element declarations - std::cout << "Enumerating components..." << std::endl; - // - auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION); - for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { - auto element = static_cast(topLevelElements->item(i)); - - std::string nameSpace = XSTR(element->getNamespace()); - if (nameSpace != "components") { - continue; - } - - ComponentInfo compInfo; - - // Name - compInfo.Name = (std::string)XSTR(element->getName()); - // Annotation - auto componentAnnotation = element->getAnnotation(); - if (componentAnnotation != nullptr) { - // Parse annotation XML - char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString()); - MemBufInputSource annotationInput(reinterpret_cast(annotationString), strlen(annotationString), "MemBuf: Annotation String"); - XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool); - parser.setErrorHandler(m_ErrorHandler); - parser.parse(annotationInput); - XMLString::release(&annotationString); - auto doc = parser.getDocument(); - - // Add allocation estimation(s) - auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation")); - for (int i = 0; i < allocationTags->getLength(); ++i) { - auto allocation = dynamic_cast(allocationTags->item(i)); - auto child = allocation->getFirstChild(); - if (child == nullptr) { - continue; - } - - XSValue::Status status; - XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status); - compInfo.Meta.Allocation += val->fData.fValue.f_int; - } - - // Save documentation string - auto documentationTags = doc->getElementsByTagName(XSTR("xs:documentation")); - if (documentationTags->getLength() != 0) { - auto child = documentationTags->item(0)->getFirstChild(); - if (child != nullptr) { - compInfo.Meta.Annotation = XSTR(child->getNodeValue()); - } - } - // TODO: Parse annotation string XML - // compInfo.Meta.Allocation = ... - } else { - std::cout << "Warning: Component is missing an annotation!" << std::endl; - } - - // - auto typeDefinition = element->getTypeDefinition(); - if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) { - std::cerr << "Error: Type definition wasn't COMPLEX_TYPE! Skipping." << std::endl; - continue; - } - auto complexTypeDefinition = dynamic_cast(typeDefinition); - - // - auto modelGroupParticle = complexTypeDefinition->getParticle(); - if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) { - std::cerr << "Error: Model group particle wasn't TERM_MODELGROUP! Skipping." << std::endl; - continue; - } - auto modelGroup = modelGroupParticle->getModelGroupTerm(); - - // getParticles(); - for (unsigned int i = 0; i < particles->size(); ++i) { - auto particle = particles->elementAt(i); - if (particle->getTermType() != XSParticle::TERM_ELEMENT) { - std::cerr << "Error: Particle wasn't TERM_ELEMENT! Skipping." << std::endl; - continue; - } - auto elementDeclaration = particle->getElementTerm(); - - std::string name = XSTR(elementDeclaration->getName()); - std::string type = XSTR(elementDeclaration->getTypeDefinition()->getName()); - - size_t stride = getTypeStride(type); - if (stride == 0) { - std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl; - continue; - } - - compInfo.FieldTypes[name] = type; - compInfo.FieldOffsets[name] = fieldOffset; - fieldOffset += getTypeStride(type); - } - - compInfo.Meta.Stride = fieldOffset; - m_ComponentInfo[compInfo.Name] = compInfo; - } -} - -void EntityXMLFile::parseDefaults() -{ - using namespace xercesc; - - for (auto& ci : m_ComponentInfo) { - // Allocate memory for default values - ci.second.Defaults = std::shared_ptr(new char[ci.second.Meta.Stride]); - memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride); - - XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager); - parser.setErrorHandler(m_ErrorHandler); - - std::string componentName = ci.first; - LOG_DEBUG("Parsing defaults for component %s", componentName.c_str()); - boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml"; - - parser.parse(defaultsFile.string().c_str()); - auto doc = parser.getDocument(); - if (doc == nullptr) { - LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str()); - continue; - } - - // Find the node in the components namespace matching the component name - std::string tagName = "c:" + componentName; - auto rootNodes = doc->getElementsByTagName(XSTR(tagName.c_str())); - if (rootNodes->getLength() == 0) { - LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str()); - continue; - } - auto componentElement = dynamic_cast(rootNodes->item(0)); - - // Fill the default value buffer with values - for (auto& field : ci.second.FieldOffsets) { - std::string fieldName = field.first; - auto fieldNodes = componentElement->getElementsByTagName(XSTR(fieldName.c_str())); - auto fieldNode = fieldNodes->item(0); - if (fieldNode == nullptr) { - LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str()); - continue; - } - auto fieldElement = dynamic_cast(fieldNode); - - std::string fieldType = ci.second.FieldTypes.at(fieldName); - unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); - writeData(fieldElement, fieldType, ci.second.Defaults.get() + fieldOffset); - } - } -} - -void EntityXMLFile::predictComponentAllocation() -{ - using namespace xercesc; - - auto root = m_DOMDocument->getDocumentElement(); - - // Count static instances of components present in entity hierarchy - auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*")); - for (int i = 0; i < components->getLength(); ++i) { - auto component = dynamic_cast(components->item(i)); - - std::string componentName = XSTR(component->getLocalName()); - auto& compInfo = m_ComponentInfo.at(componentName); - compInfo.Meta.Allocation += 1; - } - - std::cout << "COMPONENT INFO" << std::endl; - for (auto& pair : m_ComponentInfo) { - ComponentInfo& ci = pair.second; - std::cout << "Component: " << ci.Name << " (" << ci.Meta.Annotation << ")" << std::endl; - std::cout << " Allocation: " << ci.Meta.Allocation << std::endl; - std::cout << " Fields:" << std::endl; - - // Calculate component size - std::size_t stride = 0; - // Add size of fields - for (auto& field : ci.FieldTypes) { - std::cout << " " << field.second << " " << field.first << " (" << getTypeStride(field.second) << " byte)" << std::endl; - stride += getTypeStride(field.second); - } - std::cout << " Stride: " << ci.Meta.Stride << std::endl; - } -} - -void EntityXMLFile::parseEntityGraph(World* world, xercesc::DOMElement* element, EntityID parentEntity) -{ - using namespace xercesc; - - // Create entity - EntityID entity = world->CreateEntity(parentEntity); - LOG_DEBUG("Created entity %i, parent %i", entity, parentEntity); - - // Add components - auto components = m_DOMDocument->evaluate(XSTR("Components/*"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr); - for (int i = 0; i < components->getSnapshotLength(); i++) { - components->snapshotItem(i); - auto componentElement = dynamic_cast(components->getNodeValue()); - std::string componentName = XSTR(componentElement->getLocalName()); - auto& ci = m_ComponentInfo.at(componentName); - - // Attach the component - auto c = world->AttachComponent(entity, componentName); - LOG_DEBUG("Attached %s component", componentName.c_str()); - - // Write field data - auto fields = componentElement->getChildNodes(); - for (int j = 0; j < fields->getLength(); ++j) { - auto fieldNode = fields->item(j); - auto nodeType = fieldNode->getNodeType(); - if (nodeType != DOMNode::ELEMENT_NODE) { - continue; - } - auto field = dynamic_cast(fields->item(j)); - //const XMLCh* value = fields->item(j)->getTextContent(); - std::string fieldName(XSTR(field->getLocalName())); - if (ci.FieldTypes.find(fieldName) == ci.FieldTypes.end()) { - std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl; - continue; - } - - std::string fieldType = ci.FieldTypes.at(fieldName); - unsigned int fieldOffset = ci.FieldOffsets.at(fieldName); - std::string fieldValue(XSTR(field->getTextContent())); - LOG_DEBUG(" %s %s = %s", fieldType.c_str(), fieldName.c_str(), fieldValue.c_str()); - writeData(field, fieldType, c.Data + fieldOffset); - } - } - - // Recurse children entities - auto children = m_DOMDocument->evaluate(XSTR("Children/Entity"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr); - for (int i = 0; i < children->getSnapshotLength(); i++) { - children->snapshotItem(i); - parseEntityGraph(world, dynamic_cast(children->getNodeValue()), entity); - } - - // Recurse children entity references - auto refs = m_DOMDocument->evaluate(XSTR("Children/EntityRef"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr); - for (int i = 0; i < refs->getSnapshotLength(); i++) { - refs->snapshotItem(i); - auto entityRefElement = dynamic_cast(refs->getNodeValue()); - auto attribute = entityRefElement->getAttribute(XSTR("file")); - if (attribute == nullptr) { - continue; - } - - std::string file((const char*)XSTR(attribute)); - boost::filesystem::path absolutePath = boost::filesystem::path(m_EntityFile).parent_path() / file; - ResourceManager::Load(absolutePath.string())->PopulateWorld(world); - } -} - -std::size_t EntityXMLFile::getTypeStride(std::string typeName) -{ - std::map typeStrides{ - { "bool", sizeof(bool) }, - { "int", sizeof(int) }, - { "float", sizeof(float) }, - { "double", sizeof(double) }, - { "string", sizeof(std::string) }, - { "Vector", sizeof(glm::vec3) }, - { "Quaternion", sizeof(glm::quat) }, - { "Color", sizeof(glm::vec4) } - }; - - auto it = typeStrides.find(typeName); - return (it != typeStrides.end()) ? it->second : 0; -} - -float EntityXMLFile::getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) const -{ - using namespace xercesc; - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getAttribute(XSTR(attribute)), xercesc::XSValue::DataType::dt_double, status); - if (val == nullptr) { - LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", XSTR(element->getTagName()), attribute); - return 0.f; - } else { - return static_cast(val->fData.fValue.f_double); - } -} - -void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string typeName, char* outData) -{ - using namespace xercesc; - - if (typeName == "Vector") { - glm::vec3 vec; - vec.x = getFloatAttribute(element, "X"); - vec.y = getFloatAttribute(element, "Y"); - vec.z = getFloatAttribute(element, "Z"); - memcpy(outData, reinterpret_cast(&vec), getTypeStride(typeName)); - } else if (typeName == "Color") { - glm::vec4 vec; - vec.r = getFloatAttribute(element, "R"); - vec.g = getFloatAttribute(element, "G"); - vec.b = getFloatAttribute(element, "B"); - vec.a = getFloatAttribute(element, "A"); - memcpy(outData, reinterpret_cast(&vec), getTypeStride(typeName)); - } else if (typeName == "Quaternion") { - glm::quat q; - q.x = getFloatAttribute(element, "X"); - q.y = getFloatAttribute(element, "Y"); - q.z = getFloatAttribute(element, "Z"); - q.w = getFloatAttribute(element, "W"); - memcpy(outData, reinterpret_cast(&q), getTypeStride(typeName)); - } else if (typeName == "float") { - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_float, status); - memcpy(outData, reinterpret_cast(&val->fData.fValue.f_float), getTypeStride(typeName)); - } else if (typeName == "double") { - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_double, status); - memcpy(outData, reinterpret_cast(&val->fData.fValue.f_double), getTypeStride(typeName)); - } else if (typeName == "bool") { - XSValue::Status status; - XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_boolean, status); - memcpy(outData, reinterpret_cast(&val->fData.fValue.f_bool), getTypeStride(typeName)); - } else { - XSValue::DataType dataType = XSValue::getDataType(XSTR(typeName.c_str())); - if (dataType == XSValue::DataType::dt_string) { - char* str = XMLString::transcode(element->getTextContent()); - std::string standardString(str); - new (outData) std::string(str); - XMLString::release(&str); - //memcpy(outData, reinterpret_cast(&standardString), getTypeStride(typeName)); - } else { - //XSValue::Status status; - //XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status); - //memcpy(outData, reinterpret_cast(&val->fData.fValue), getTypeStride(typeName)); - LOG_WARNING("Unknown native data type: %s", typeName.c_str()); - } - } -} \ No newline at end of file diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index c2957d02..1e9e15aa 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -8,7 +8,6 @@ Game::Game(int argc, char* argv[]) ResourceManager::RegisterType("ConfigFile"); ResourceManager::RegisterType("Model"); ResourceManager::RegisterType("Texture"); - ResourceManager::RegisterType("EntityXMLFile"); ResourceManager::RegisterType("ShaderProgram"); ResourceManager::RegisterType("EntityFile"); @@ -50,7 +49,7 @@ Game::Game(int argc, char* argv[]) std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); if (!mapToLoad.empty()) { auto file = ResourceManager::Load(mapToLoad); - EntityFilePreprocessor fpp(mapToLoad); + EntityFilePreprocessor fpp(file); fpp.RegisterComponents(m_World); EntityFileParser fp(file); fp.MergeEntities(m_World); From 81024b4a1d5336170e87d211b9184a6524d8ad0b Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Fri, 8 Jan 2016 11:21:10 +0100 Subject: [PATCH 06/18] First work on EntityFileWriter for saving levels --- include/Engine/Core/EntityFileParser.h | 7 +- include/Engine/Core/EntityFilePreprocessor.h | 7 +- include/Engine/Core/EntityFileWriter.h | 38 ++++++ include/Engine/Core/World.h | 2 +- src/Engine/Core/EntityFileWriter.cpp | 121 +++++++++++++++++++ src/Engine/Core/World.cpp | 2 +- src/Game/Game.cpp | 4 + 7 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 include/Engine/Core/EntityFileWriter.h create mode 100644 src/Engine/Core/EntityFileWriter.cpp diff --git a/include/Engine/Core/EntityFileParser.h b/include/Engine/Core/EntityFileParser.h index d8e51a8b..c4c8c221 100644 --- a/include/Engine/Core/EntityFileParser.h +++ b/include/Engine/Core/EntityFileParser.h @@ -1,3 +1,6 @@ +#ifndef EntityFileParser_h__ +#define EntityFileParser_h__ + #include "EntityFile.h" #include "World.h" @@ -20,4 +23,6 @@ private: void onStartComponent(EntityID entity, std::string component); void onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes); void onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData); -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h index b21edf7f..3c3998b1 100644 --- a/include/Engine/Core/EntityFilePreprocessor.h +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -1,3 +1,6 @@ +#ifndef EntityFilePreprocessor_h__ +#define EntityFilePreprocessor_h__ + #include #include #include @@ -28,4 +31,6 @@ private: void onStartComponent(EntityID entity, std::string type); void parseComponentInfo(); void parseDefaults(); -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFileWriter.h b/include/Engine/Core/EntityFileWriter.h new file mode 100644 index 00000000..ba4035e4 --- /dev/null +++ b/include/Engine/Core/EntityFileWriter.h @@ -0,0 +1,38 @@ +#ifndef EntityFileWriter_h__ +#define EntityFileWriter_h__ + +#include +#include +#include +#include +#include + +#include "Util/XercesString.h" +#include "EntityFile.h" +#include "World.h" + +class EntityFileWriter +{ +public: + EntityFileWriter(boost::filesystem::path file) + : m_FilePath(file) + { + using namespace xercesc; + m_DOMImplementation = DOMImplementationRegistry::getDOMImplementation(XS::ToXMLCh("LS")); + m_DOMLSSerializer = static_cast(m_DOMImplementation)->createLSSerializer(); + m_DOMLSSerializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true); + m_DOMLSSerializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true); + } + + void WriteWorld(World* world); + +private: + boost::filesystem::path m_FilePath; + xercesc::DOMImplementation* m_DOMImplementation; + xercesc::DOMLSSerializer* m_DOMLSSerializer; + + void appendEntityChildren(xercesc::DOMElement* parentElement, const World* world, EntityID entity); + void appentEntityComponents(xercesc::DOMElement* parentElemetn, const World* world, EntityID entity); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 92378816..b3209ea7 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -22,7 +22,7 @@ public: // Attach a component to an entity and fill it with default values ComponentWrapper AttachComponent(EntityID entity, std::string componentType); // Check if an entity has a component - bool HasComponent(EntityID entity, std::string componentType); + bool HasComponent(EntityID entity, std::string componentType) const; // Get a component of an entity ComponentWrapper GetComponent(EntityID entity, std::string componentType); // Delete a component off an entity diff --git a/src/Engine/Core/EntityFileWriter.cpp b/src/Engine/Core/EntityFileWriter.cpp new file mode 100644 index 00000000..8207fbe3 --- /dev/null +++ b/src/Engine/Core/EntityFileWriter.cpp @@ -0,0 +1,121 @@ +#include "Core/EntityFileWriter.h" + +#define X(str) XS::ToXMLCh(str) + +void EntityFileWriter::WriteWorld(World* world) +{ + using namespace xercesc; + DOMDocument* doc = m_DOMImplementation->createDocument(nullptr, X("Entity"), nullptr); + DOMElement* root = doc->getDocumentElement(); + root->setAttribute(X("xmlns:xsi"), X("http://www.w3.org/2001/XMLSchema-instance")); + root->setAttribute(X("xsi:noNamespaceSchemaLocation"), X("../Types/Entity.xsd")); + root->setAttribute(X("xmlns:c"), X("components")); + + DOMElement* componentsElement = doc->createElement(X("Components")); + root->appendChild(componentsElement); + appentEntityComponents(componentsElement, world, 0); + + DOMElement* childrenElement = doc->createElement(X("Children")); + root->appendChild(childrenElement); + appendEntityChildren(childrenElement, world, 0); + + XMLFormatTarget* target = new StdOutFormatTarget(); + DOMLSOutput* output = static_cast(m_DOMImplementation)->createLSOutput(); + output->setByteStream(target); + m_DOMLSSerializer->write(doc, output); +} + +void EntityFileWriter::appendEntityChildren(xercesc::DOMElement* parentElement, const World* world, EntityID entity) +{ + using namespace xercesc; + DOMDocument* doc = parentElement->getOwnerDocument(); + + auto childrenRange = world->GetEntityChildren().equal_range(entity); + for (auto it = childrenRange.first; it != childrenRange.second; ++it) { + EntityID childEntity = it->second; + + DOMElement* entityElement = doc->createElement(X("Entity")); + parentElement->appendChild(entityElement); + + DOMElement* componentsElement = doc->createElement(X("Components")); + entityElement->appendChild(componentsElement); + appentEntityComponents(componentsElement, world, childEntity); + + DOMElement* childrenElement = doc->createElement(X("Children")); + entityElement->appendChild(childrenElement); + appendEntityChildren(childrenElement, world, childEntity); + } +} + +void EntityFileWriter::appentEntityComponents(xercesc::DOMElement* parentElement, const World* world, EntityID entity) +{ + using namespace xercesc; + DOMDocument* doc = parentElement->getOwnerDocument(); + auto& componentPools = world->GetComponentPools(); + + // Step through all component pools to get an entity's components + // HACK: This is sloooow. + for (auto& kv : componentPools) { + const std::string& componentName = kv.first; + if (!world->HasComponent(entity, componentName)) { + continue; + } + std::string qualifiedComponentName = "c:" + componentName; + DOMElement* componentElement = doc->createElement(X(qualifiedComponentName)); + parentElement->appendChild(componentElement); + ComponentWrapper c = kv.second->GetByEntity(entity); + for (auto& kv : c.Info.FieldTypes) { + std::string fieldName = kv.first; + std::string fieldType = kv.second; + std::size_t fieldOffset = c.Info.FieldOffsets.at(fieldName); + + // Ignore fields that are equal to the default + // HACK: This is probably sloooooow, but it's okay. + if (memcmp(c.Data + fieldOffset, c.Info.Defaults.get() + fieldOffset, EntityFile::GetTypeStride(fieldType)) == 0) { + continue; + } + + DOMElement* fieldElement = doc->createElement(X(fieldName)); + componentElement->appendChild(fieldElement); + + if (fieldType == "Vector") { + const glm::vec3& vec = c[fieldName]; + fieldElement->setAttribute(X("X"), X(boost::lexical_cast(vec.x))); + fieldElement->setAttribute(X("Y"), X(boost::lexical_cast(vec.y))); + fieldElement->setAttribute(X("Z"), X(boost::lexical_cast(vec.z))); + } else if (fieldType == "Color") { + const glm::vec4& vec = c[fieldName]; + fieldElement->setAttribute(X("R"), X(boost::lexical_cast(vec.r))); + fieldElement->setAttribute(X("G"), X(boost::lexical_cast(vec.g))); + fieldElement->setAttribute(X("B"), X(boost::lexical_cast(vec.b))); + fieldElement->setAttribute(X("A"), X(boost::lexical_cast(vec.a))); + } else if (fieldType == "Quaternion") { + const glm::quat& q = c[fieldName]; + fieldElement->setAttribute(X("X"), X(boost::lexical_cast(q.x))); + fieldElement->setAttribute(X("Y"), X(boost::lexical_cast(q.y))); + fieldElement->setAttribute(X("Z"), X(boost::lexical_cast(q.z))); + fieldElement->setAttribute(X("W"), X(boost::lexical_cast(q.w))); + } else if (fieldType == "int") { + const int& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); + } else if (fieldType == "float") { + const float& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); + } else if (fieldType == "double") { + const double& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); + } else if (fieldType == "bool") { + const bool& value = c[fieldName]; + if (value) { + fieldElement->appendChild(doc->createTextNode(X("true"))); + } else { + fieldElement->appendChild(doc->createTextNode(X("false"))); + } + } else if (fieldType == "string") { + const std::string& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(value))); + } + } + } +} + diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 9957e0ec..4c45d0d5 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -68,7 +68,7 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy } -bool World::HasComponent(EntityID entity, std::string componentType) +bool World::HasComponent(EntityID entity, std::string componentType) const { ComponentPool* pool = m_ComponentPools.at(componentType); return pool->KnowsEntity(entity); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 1e9e15aa..c6f2286e 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -2,6 +2,7 @@ #include "Collision/TriggerSystem.h" #include "Collision/CollisionSystem.h" #include "Game/HealthSystem.h" +#include "Core/EntityFileWriter.h" Game::Game(int argc, char* argv[]) { @@ -53,6 +54,9 @@ Game::Game(int argc, char* argv[]) fpp.RegisterComponents(m_World); EntityFileParser fp(file); fp.MergeEntities(m_World); + + EntityFileWriter writer("Testasdasdasd.xml"); + writer.WriteWorld(m_World); } // Create system pipeline From 24b9e951c29bf8b80d21c71d3202e828e4eca356 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Fri, 8 Jan 2016 11:47:58 +0100 Subject: [PATCH 07/18] Added stride information to ComponentInfo, refactored FieldOffsets and FieldTypes into new struct Field_t Fields while I was at it. --- include/Engine/Core/ComponentInfo.h | 10 ++++-- include/Engine/Core/ComponentWrapper.h | 7 ++-- include/Engine/Core/EntityFile.h | 4 +-- src/Engine/Core/EntityFile.cpp | 36 +++++++++---------- src/Engine/Core/EntityFileParser.cpp | 14 ++++---- src/Engine/Core/EntityFilePreprocessor.cpp | 24 +++++++------ src/Engine/Core/EntityFileWriter.cpp | 23 ++++++------- src/Engine/Editor/EditorSystem.cpp | 40 +++++++++++----------- 8 files changed, 83 insertions(+), 75 deletions(-) diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h index ee5f1a11..909224e2 100644 --- a/include/Engine/Core/ComponentInfo.h +++ b/include/Engine/Core/ComponentInfo.h @@ -12,9 +12,15 @@ struct ComponentInfo unsigned int Stride = 0; }; + struct Field_t + { + std::string Type; + unsigned int Offset; + unsigned int Stride; + }; + std::string Name; - std::unordered_map FieldTypes; - std::unordered_map FieldOffsets; + std::unordered_map Fields; Meta_t Meta; std::shared_ptr Defaults = nullptr; }; diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 56ae943f..922b3d79 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -21,7 +21,7 @@ struct ComponentWrapper template T& Property(std::string name) { - unsigned int offset = Info.FieldOffsets.at(name); + unsigned int offset = Info.Fields.at(name).Offset; return *reinterpret_cast(&Data[offset]); } @@ -78,8 +78,9 @@ public: void AddProperty(std::string fieldName, T defaultValue) { m_DefaultValues.push_back(defaultValue); - m_ComponentInfo.FieldTypes[fieldName] = typeid(T).name(); - m_ComponentInfo.FieldOffsets[fieldName] = m_ComponentInfo.Meta.Stride; + m_ComponentInfo.Fields[fieldName].Name = typeid(T).name(); + m_ComponentInfo.Fields[fieldName].Offset = m_ComponentInfo.Meta.Stride; + m_ComponentInfo.Fields[fieldName].Stride = sizeof(T); m_ComponentInfo.Meta.Stride += sizeof(T); } diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index 3bee9a5b..b6f0c434 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -275,8 +275,8 @@ private: public: static std::size_t GetTypeStride(std::string typeName); - static void WriteAttributeData(char* outData, const std::string fieldType, const std::map& attributes); - static void WriteValueData(char* outData, const std::string fieldType, const char* valueData); + static void WriteAttributeData(char* outData, const ComponentInfo::Field_t& field, const std::map& attributes); + static void WriteValueData(char* outData, const ComponentInfo::Field_t& field, const char* valueData); xercesc::XMLGrammarPool* GrammarPool() const { return m_GrammarPool; } diff --git a/src/Engine/Core/EntityFile.cpp b/src/Engine/Core/EntityFile.cpp index b05c9e48..1e381df5 100644 --- a/src/Engine/Core/EntityFile.cpp +++ b/src/Engine/Core/EntityFile.cpp @@ -46,50 +46,50 @@ std::size_t EntityFile::GetTypeStride(std::string typeName) return (it != typeStrides.end()) ? it->second : 0; } -void EntityFile::WriteAttributeData(char* outData, const std::string fieldType, const std::map& attributes) +void EntityFile::WriteAttributeData(char* outData, const ComponentInfo::Field_t& field, const std::map& attributes) { - if (fieldType == "Vector") { + if (field.Type == "Vector") { glm::vec3 vec; vec.x = boost::lexical_cast(attributes.at("X")); vec.y = boost::lexical_cast(attributes.at("Y")); vec.z = boost::lexical_cast(attributes.at("Z")); - memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "Color") { + memcpy(outData, reinterpret_cast(&vec), field.Stride); + } else if (field.Type == "Color") { glm::vec4 vec; vec.r = boost::lexical_cast(attributes.at("R")); vec.g = boost::lexical_cast(attributes.at("G")); vec.b = boost::lexical_cast(attributes.at("B")); vec.a = boost::lexical_cast(attributes.at("A")); - memcpy(outData, reinterpret_cast(&vec), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "Quaternion") { + memcpy(outData, reinterpret_cast(&vec), field.Stride); + } else if (field.Type == "Quaternion") { glm::quat q; q.x = boost::lexical_cast(attributes.at("X")); q.y = boost::lexical_cast(attributes.at("Y")); q.z = boost::lexical_cast(attributes.at("Z")); q.w = boost::lexical_cast(attributes.at("W")); - memcpy(outData, reinterpret_cast(&q), EntityFile::GetTypeStride(fieldType)); + memcpy(outData, reinterpret_cast(&q), field.Stride); } else if (!attributes.empty()) { LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size()); } } -void EntityFile::WriteValueData(char* outData, const std::string fieldType, const char* valueData) +void EntityFile::WriteValueData(char* outData, const ComponentInfo::Field_t& field, const char* valueData) { - if (fieldType == "int") { + if (field.Type == "int") { int value = boost::lexical_cast(valueData); - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "float") { + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "float") { float value = boost::lexical_cast(valueData); - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "double") { + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "double") { double value = boost::lexical_cast(valueData); - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "bool") { + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "bool") { bool value = (valueData[0] == 't'); // Lazy bool evaluation - memcpy(outData, reinterpret_cast(&value), EntityFile::GetTypeStride(fieldType)); - } else if (fieldType == "string") { + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "string") { new (outData) std::string(valueData); } else { - LOG_WARNING("Unknown value data type: %s", fieldType.c_str()); + LOG_WARNING("Unknown value data type: %s", field.Type.c_str()); } } diff --git a/src/Engine/Core/EntityFileParser.cpp b/src/Engine/Core/EntityFileParser.cpp index e14e5508..5e685abe 100644 --- a/src/Engine/Core/EntityFileParser.cpp +++ b/src/Engine/Core/EntityFileParser.cpp @@ -35,24 +35,24 @@ void EntityFileParser::onStartComponentField(EntityID entity, std::string compon { EntityID realEntity = m_EntityIDMapper.at(entity); ComponentWrapper component = m_World->GetComponent(realEntity, componentType); - std::string fieldType = component.Info.FieldTypes.at(fieldName); + auto& field = component.Info.Fields.at(fieldName); - LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), fieldType.c_str()); + LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), field.Type.c_str()); LOG_DEBUG("Attributes:"); for (auto& kv : attributes) { LOG_DEBUG("\t%s = %s", kv.first.c_str(), kv.second.c_str()); } - char* data = component.Data + component.Info.FieldOffsets.at(fieldName); - EntityFile::WriteAttributeData(data, fieldType, attributes); + char* data = component.Data + field.Offset; + EntityFile::WriteAttributeData(data, field, attributes); } void EntityFileParser::onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData) { EntityID realEntity = m_EntityIDMapper.at(entity); ComponentWrapper component = m_World->GetComponent(realEntity, componentType); - std::string fieldType = component.Info.FieldTypes.at(fieldName); + auto& field = component.Info.Fields.at(fieldName); - char* data = component.Data + component.Info.FieldOffsets.at(fieldName); - EntityFile::WriteValueData(data, fieldType, fieldData); + char* data = component.Data + field.Offset; + EntityFile::WriteValueData(data, field, fieldData); } diff --git a/src/Engine/Core/EntityFilePreprocessor.cpp b/src/Engine/Core/EntityFilePreprocessor.cpp index 1f6b36f1..4fd768a1 100644 --- a/src/Engine/Core/EntityFilePreprocessor.cpp +++ b/src/Engine/Core/EntityFilePreprocessor.cpp @@ -19,8 +19,9 @@ EntityFilePreprocessor::EntityFilePreprocessor(const EntityFile* entityFile) LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str()); LOG_DEBUG("Stride: %i", info.Meta.Stride); LOG_DEBUG("Allocation: %i", info.Meta.Allocation); - for (auto& kv : info.FieldTypes) { - LOG_DEBUG("\t%i\t%s %s", info.FieldOffsets[kv.first], kv.second.c_str(), kv.first.c_str()); + for (auto& kv : info.Fields) { + auto& field = kv.second; + LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type, kv.first.c_str()); } } @@ -142,8 +143,10 @@ void EntityFilePreprocessor::parseComponentInfo() continue; } - compInfo.FieldTypes[name] = type; - compInfo.FieldOffsets[name] = fieldOffset; + compInfo.Fields[name].Type = type; + compInfo.Fields[name].Offset = fieldOffset; + compInfo.Fields[name].Stride = stride; + fieldOffset += stride; } @@ -187,8 +190,9 @@ void EntityFilePreprocessor::parseDefaults() auto componentElement = dynamic_cast(rootNodes->item(0)); // Fill the default value buffer with values - for (auto& field : ci.second.FieldOffsets) { - std::string fieldName = field.first; + for (auto& kv : ci.second.Fields) { + std::string fieldName = kv.first; + auto& field = kv.second; auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName)); auto fieldNode = fieldNodes->item(0); if (fieldNode == nullptr) { @@ -197,9 +201,7 @@ void EntityFilePreprocessor::parseDefaults() } auto fieldElement = dynamic_cast(fieldNode); - std::string fieldType = ci.second.FieldTypes.at(fieldName); - unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName); - char* data = ci.second.Defaults.get() + fieldOffset; + char* data = ci.second.Defaults.get() + field.Offset; // Handle potential field attributes if (fieldElement->hasAttributes()) { @@ -209,14 +211,14 @@ void EntityFilePreprocessor::parseDefaults() auto attribItem = attributeMap->item(i); attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue()); } - EntityFile::WriteAttributeData(data, fieldType, attributes); + EntityFile::WriteAttributeData(data, field, attributes); } // Handle potential field values auto childNode = fieldElement->getFirstChild(); if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) { char* cstrValue = XMLString::transcode(childNode->getNodeValue()); - EntityFile::WriteValueData(data, fieldType, cstrValue); + EntityFile::WriteValueData(data, field, cstrValue); XMLString::release(&cstrValue); } } diff --git a/src/Engine/Core/EntityFileWriter.cpp b/src/Engine/Core/EntityFileWriter.cpp index 8207fbe3..f0c3684f 100644 --- a/src/Engine/Core/EntityFileWriter.cpp +++ b/src/Engine/Core/EntityFileWriter.cpp @@ -64,54 +64,53 @@ void EntityFileWriter::appentEntityComponents(xercesc::DOMElement* parentElement DOMElement* componentElement = doc->createElement(X(qualifiedComponentName)); parentElement->appendChild(componentElement); ComponentWrapper c = kv.second->GetByEntity(entity); - for (auto& kv : c.Info.FieldTypes) { + for (auto& kv : c.Info.Fields) { std::string fieldName = kv.first; - std::string fieldType = kv.second; - std::size_t fieldOffset = c.Info.FieldOffsets.at(fieldName); + auto& field = kv.second; // Ignore fields that are equal to the default // HACK: This is probably sloooooow, but it's okay. - if (memcmp(c.Data + fieldOffset, c.Info.Defaults.get() + fieldOffset, EntityFile::GetTypeStride(fieldType)) == 0) { + if (memcmp(c.Data + field.Offset, c.Info.Defaults.get() + field.Offset, field.Stride) == 0) { continue; } DOMElement* fieldElement = doc->createElement(X(fieldName)); componentElement->appendChild(fieldElement); - if (fieldType == "Vector") { + if (field.Type == "Vector") { const glm::vec3& vec = c[fieldName]; fieldElement->setAttribute(X("X"), X(boost::lexical_cast(vec.x))); fieldElement->setAttribute(X("Y"), X(boost::lexical_cast(vec.y))); fieldElement->setAttribute(X("Z"), X(boost::lexical_cast(vec.z))); - } else if (fieldType == "Color") { + } else if (field.Type == "Color") { const glm::vec4& vec = c[fieldName]; fieldElement->setAttribute(X("R"), X(boost::lexical_cast(vec.r))); fieldElement->setAttribute(X("G"), X(boost::lexical_cast(vec.g))); fieldElement->setAttribute(X("B"), X(boost::lexical_cast(vec.b))); fieldElement->setAttribute(X("A"), X(boost::lexical_cast(vec.a))); - } else if (fieldType == "Quaternion") { + } else if (field.Type == "Quaternion") { const glm::quat& q = c[fieldName]; fieldElement->setAttribute(X("X"), X(boost::lexical_cast(q.x))); fieldElement->setAttribute(X("Y"), X(boost::lexical_cast(q.y))); fieldElement->setAttribute(X("Z"), X(boost::lexical_cast(q.z))); fieldElement->setAttribute(X("W"), X(boost::lexical_cast(q.w))); - } else if (fieldType == "int") { + } else if (field.Type == "int") { const int& value = c[fieldName]; fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); - } else if (fieldType == "float") { + } else if (field.Type == "float") { const float& value = c[fieldName]; fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); - } else if (fieldType == "double") { + } else if (field.Type == "double") { const double& value = c[fieldName]; fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); - } else if (fieldType == "bool") { + } else if (field.Type == "bool") { const bool& value = c[fieldName]; if (value) { fieldElement->appendChild(doc->createTextNode(X("true"))); } else { fieldElement->appendChild(doc->createTextNode(X("false"))); } - } else if (fieldType == "string") { + } else if (field.Type == "string") { const std::string& value = c[fieldName]; fieldElement->appendChild(doc->createTextNode(X(value))); } diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index a43ca900..07a9e4e8 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -432,16 +432,16 @@ void EditorSystem::drawUI(World* world, double dt) } auto& component = world->GetComponent(m_Selection, componentType); - for (auto& pair : ci.FieldTypes) { - const std::string& field = pair.first; - const std::string& type = pair.second; + for (auto& kv : ci.Fields) { + const std::string& fieldName = kv.first; + auto& field = kv.second; - ImGui::PushID(field.c_str()); - if (type == "Vector") { - auto& val = component.Property(field); - if (field == "Scale") { + ImGui::PushID(fieldName.c_str()); + if (field.Type == "Vector") { + auto& val = component.Property(fieldName); + if (fieldName == "Scale") { ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits::max()); - } else if (field == "Orientation") { + } else if (fieldName == "Orientation") { glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi())); if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi())) { val = tempVal; @@ -449,16 +449,16 @@ void EditorSystem::drawUI(World* world, double dt) } else { ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits::lowest(), std::numeric_limits::max()); } - } else if (type == "Color") { - auto& val = component.Property(field); + } else if (field.Type == "Color") { + auto& val = component.Property(fieldName); ImGui::ColorEdit4("", glm::value_ptr(val), true); - } else if (type == "string") { - std::string& val = component.Property(field); + } else if (field.Type == "string") { + std::string& val = component.Property(fieldName); char tempString[1024]; memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString))); if (ImGui::InputText("", tempString, sizeof(tempString))) { val = std::string(tempString); - LOG_DEBUG("%s::%s changed!", componentType.c_str(), field.c_str()); + LOG_DEBUG("%s::%s changed!", componentType.c_str(), fieldName.c_str()); } // DROP STUFF if (ImGui::IsItemHovered() && !m_LastDroppedFile.empty()) { @@ -466,21 +466,21 @@ void EditorSystem::drawUI(World* world, double dt) m_LastDroppedFile = ""; } - } else if (type == "double") { - float tempVal = static_cast(component.Property(field)); + } else if (field.Type == "double") { + float tempVal = static_cast(component.Property(fieldName)); if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) { - component.SetProperty(field, static_cast(tempVal)); + component.SetProperty(fieldName, static_cast(tempVal)); } - } else if (type == "bool") { - auto& val = component.Property(field); + } else if (field.Type == "bool") { + auto& val = component.Property(fieldName); ImGui::Checkbox("", &val); } else { - ImGui::TextDisabled(type.c_str()); + ImGui::TextDisabled(field.Type.c_str()); } ImGui::PopID(); ImGui::SameLine(); - ImGui::Text(field.c_str()); + ImGui::Text(fieldName.c_str()); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("field annotation goes here"); } From 11d547cde878a3e6cecf30ce054a3397beebc0b3 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Fri, 8 Jan 2016 12:42:32 +0100 Subject: [PATCH 08/18] Entity 0 is now a real entity in the world, created by the first entity read while importing. This makes the difference between map files and entity files automatic, since consecutive entity file loads should automatically get the world as parent, as long as the world entity has been created earlier. --- include/Engine/Core/EntityFile.h | 2 +- include/Engine/Core/EntityFileWriter.h | 1 + include/Engine/Core/World.h | 2 +- src/Engine/Core/EntityFileParser.cpp | 2 +- src/Engine/Core/EntityFileWriter.cpp | 11 +++++++++-- src/Engine/Core/World.cpp | 9 ++++++--- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index b6f0c434..962e5c4e 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -162,7 +162,7 @@ private: xercesc::SAX2XMLReader* m_Reader; //State m_CurrentScope = State::Unknown; std::stack m_StateStack; - unsigned int m_NextEntityID = 1; + unsigned int m_NextEntityID = 0; std::stack m_EntityStack; std::string m_CurrentComponent; std::string m_CurrentField; diff --git a/include/Engine/Core/EntityFileWriter.h b/include/Engine/Core/EntityFileWriter.h index ba4035e4..e9c7259c 100644 --- a/include/Engine/Core/EntityFileWriter.h +++ b/include/Engine/Core/EntityFileWriter.h @@ -25,6 +25,7 @@ public: } void WriteWorld(World* world); + void WriteEntity(World* world, EntityID entity); private: boost::filesystem::path m_FilePath; diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index b3209ea7..22e684b0 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -39,7 +39,7 @@ public: const std::unordered_multimap& GetEntityChildren() const { return m_EntityChildren; } private: - EntityID m_CurrentEntityID = 1; + EntityID m_CurrentEntityID = 0; std::unordered_map m_EntityParents; // TODO: This should be a more effective structure diff --git a/src/Engine/Core/EntityFileParser.cpp b/src/Engine/Core/EntityFileParser.cpp index 5e685abe..b3310cd4 100644 --- a/src/Engine/Core/EntityFileParser.cpp +++ b/src/Engine/Core/EntityFileParser.cpp @@ -27,7 +27,7 @@ void EntityFileParser::onStartEntity(EntityID entity, EntityID parent) void EntityFileParser::onStartComponent(EntityID entity, std::string component) { EntityID realEntity = m_EntityIDMapper.at(entity); - m_World->AttachComponent(entity, component); + m_World->AttachComponent(realEntity, component); LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity); } diff --git a/src/Engine/Core/EntityFileWriter.cpp b/src/Engine/Core/EntityFileWriter.cpp index f0c3684f..b192c525 100644 --- a/src/Engine/Core/EntityFileWriter.cpp +++ b/src/Engine/Core/EntityFileWriter.cpp @@ -3,6 +3,11 @@ #define X(str) XS::ToXMLCh(str) void EntityFileWriter::WriteWorld(World* world) +{ + WriteEntity(world, 0); +} + +void EntityFileWriter::WriteEntity(World* world, EntityID entity) { using namespace xercesc; DOMDocument* doc = m_DOMImplementation->createDocument(nullptr, X("Entity"), nullptr); @@ -13,16 +18,18 @@ void EntityFileWriter::WriteWorld(World* world) DOMElement* componentsElement = doc->createElement(X("Components")); root->appendChild(componentsElement); - appentEntityComponents(componentsElement, world, 0); + appentEntityComponents(componentsElement, world, entity); DOMElement* childrenElement = doc->createElement(X("Children")); root->appendChild(childrenElement); - appendEntityChildren(childrenElement, world, 0); + appendEntityChildren(childrenElement, world, entity); XMLFormatTarget* target = new StdOutFormatTarget(); DOMLSOutput* output = static_cast(m_DOMImplementation)->createLSOutput(); output->setByteStream(target); m_DOMLSSerializer->write(doc, output); + + doc->release(); } void EntityFileWriter::appendEntityChildren(xercesc::DOMElement* parentElement, const World* world, EntityID entity) diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 4c45d0d5..74efda2e 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -10,12 +10,15 @@ World::~World() EntityID World::CreateEntity(EntityID parent /*= 0*/) { EntityID newEntity = generateEntityID(); - m_EntityParents[newEntity] = parent; - m_EntityChildren.insert(std::make_pair(parent, newEntity)); + if (newEntity != parent) { + m_EntityParents[newEntity] = parent; + m_EntityChildren.insert(std::make_pair(parent, newEntity)); + } else { + LOG_WARNING("Attempted to create an entity with itself as parent! Entity#%i with parent #%i", newEntity, parent); + } return newEntity; } - void World::DeleteEntity(EntityID entity) { // Delete components From ad4e723677c72af09d48caa675fa3ad5af737d7f Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 10:16:06 +0100 Subject: [PATCH 09/18] Added nativefiledialog to README --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7c5f2268..003cb2be 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,17 @@ #### Bundled libraries Libraries bundled along with binaries for Windows (MSVC14), available as a submodule in the *deps* directory of the source tree. -| Project | Version | License | -| ------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **[GLFW](http://www.glfw.org)** | 3.1.2 | [zlib/libpng License](http://www.glfw.org/license.html) | -| **[GLM](http://glm.g-truc.net/0.9.5/index.html)** | 0.9.7.1 | [MIT License](http://glm.g-truc.net/copying.txt) | -| **[GLEW](http://glew.sourceforge.net)** | 1.13.0 | [Modified BSD License](http://glew.sourceforge.net/glew.txt), [Mesa 3-D License](http://glew.sourceforge.net/mesa.txt), [Khronos License](http://glew.sourceforge.net/khronos.txt) | -| **[Assimp](http://assimp.sourceforge.net)** | 3.1.1 | [BSD 3-Clause License](http://assimp.sourceforge.net/main_license.html) | -| **[zlib](http://www.zlib.net)** | 1.28 | [zlib License](http://www.zlib.net/zlib_license.html) | -| **[libpng](http://www.libpng.org/pub/png/libpng.html)** | 1.6.19 | [libpng License](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt) | -| **[Xerces-C++](https://xerces.apache.org/xerces-c)** | 3.1.2 | [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) | -| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) | +| Project | Version | License | +| ---------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **[GLFW](http://www.glfw.org)** | 3.1.2 | [zlib/libpng License](http://www.glfw.org/license.html) | +| **[GLM](http://glm.g-truc.net/0.9.5/index.html)** | 0.9.7.1 | [MIT License](http://glm.g-truc.net/copying.txt) | +| **[GLEW](http://glew.sourceforge.net)** | 1.13.0 | [Modified BSD License](http://glew.sourceforge.net/glew.txt), [Mesa 3-D License](http://glew.sourceforge.net/mesa.txt), [Khronos License](http://glew.sourceforge.net/khronos.txt) | +| **[Assimp](http://assimp.sourceforge.net)** | 3.1.1 | [BSD 3-Clause License](http://assimp.sourceforge.net/main_license.html) | +| **[zlib](http://www.zlib.net)** | 1.28 | [zlib License](http://www.zlib.net/zlib_license.html) | +| **[libpng](http://www.libpng.org/pub/png/libpng.html)** | 1.6.19 | [libpng License](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt) | +| **[Xerces-C++](https://xerces.apache.org/xerces-c)** | 3.1.2 | [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) | +| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) | +| **[nativefiledialog](https://github.com/mlabbe/nativefiledialog) | 2016-01-08 | https://github.com/mlabbe/nativefiledialog/blob/master/LICENSE | #### External libraries Libraries that are too big to be bundled with the project. From 6adc98eea593b7d0ee714eca8c8267ac662cd50f Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 10:21:22 +0100 Subject: [PATCH 10/18] Now writes entities to file instead of stdout... --- include/Engine/Core/EntityFileWriter.h | 2 +- src/Engine/Core/EntityFileWriter.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/Engine/Core/EntityFileWriter.h b/include/Engine/Core/EntityFileWriter.h index e9c7259c..b75bc145 100644 --- a/include/Engine/Core/EntityFileWriter.h +++ b/include/Engine/Core/EntityFileWriter.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include "Util/XercesString.h" #include "EntityFile.h" diff --git a/src/Engine/Core/EntityFileWriter.cpp b/src/Engine/Core/EntityFileWriter.cpp index b192c525..161c3169 100644 --- a/src/Engine/Core/EntityFileWriter.cpp +++ b/src/Engine/Core/EntityFileWriter.cpp @@ -24,10 +24,15 @@ void EntityFileWriter::WriteEntity(World* world, EntityID entity) root->appendChild(childrenElement); appendEntityChildren(childrenElement, world, entity); - XMLFormatTarget* target = new StdOutFormatTarget(); - DOMLSOutput* output = static_cast(m_DOMImplementation)->createLSOutput(); - output->setByteStream(target); - m_DOMLSSerializer->write(doc, output); + try { + LocalFileFormatTarget* target = new LocalFileFormatTarget(X(m_FilePath.string())); + DOMLSOutput* output = static_cast(m_DOMImplementation)->createLSOutput(); + output->setByteStream(target); + m_DOMLSSerializer->write(doc, output); + delete target; + } catch (const std::runtime_error& e) { + LOG_ERROR("Failed to save \"%s\": %s", m_FilePath.c_str(), e.what()); + } doc->release(); } From 39dd27726d680fb3978f774913a2640c44748351 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 10:26:45 +0100 Subject: [PATCH 11/18] MAJOR BREAKING CHANGE: EntityID 0 is now a valid entity. Use EntityID_Invalid as sentry for checks! Also contains inseparable editor changes. --- deps | 2 +- include/Engine/Core/Entity.h | 1 + include/Engine/Editor/EditorSystem.h | 35 ++- include/Engine/Rendering/EPicking.h | 2 +- src/Engine/CMakeLists.txt | 17 +- src/Engine/Core/World.cpp | 10 +- src/Engine/Editor/EditorSystem.cpp | 262 +++++++++++++------- src/Engine/Rendering/RenderQueueFactory.cpp | 20 +- src/Game/Game.cpp | 7 +- 9 files changed, 238 insertions(+), 118 deletions(-) diff --git a/deps b/deps index 1ae6ba5b..bf83f099 160000 --- a/deps +++ b/deps @@ -1 +1 @@ -Subproject commit 1ae6ba5b1297ed71b560aee211b9f0007ba52547 +Subproject commit bf83f099ba16f0a87f9bebe8cfc5fd4e59fee805 diff --git a/include/Engine/Core/Entity.h b/include/Engine/Core/Entity.h index c6a475e3..1f40b9b7 100644 --- a/include/Engine/Core/Entity.h +++ b/include/Engine/Core/Entity.h @@ -2,5 +2,6 @@ #define Entity_h__ typedef unsigned int EntityID; +const static unsigned int EntityID_Invalid = -1; #endif \ No newline at end of file diff --git a/include/Engine/Editor/EditorSystem.h b/include/Engine/Editor/EditorSystem.h index 779b7f97..6b9b7a21 100644 --- a/include/Engine/Editor/EditorSystem.h +++ b/include/Engine/Editor/EditorSystem.h @@ -1,6 +1,7 @@ #include #include #include +#include #include "../Core/System.h" #include "../Core/EMousePress.h" #include "../Core/EMouseRelease.h" @@ -11,6 +12,9 @@ #include "../Rendering/EPicking.h" #include "../Core/EFileDropped.h" #include "../Rendering/RenderQueueFactory.h" +#include "../Core/EntityFilePreprocessor.h" +#include "../Core/EntityFileParser.h" +#include "../Core/EntityFileWriter.h" class EditorSystem : public ImpureSystem { @@ -25,6 +29,8 @@ private: bool m_Enabled; bool m_Visible; + boost::filesystem::path m_DefaultEntityDir; + boost::filesystem::path m_CurrentFile; std::vector m_PickingQueue; enum class WidgetMode @@ -41,22 +47,26 @@ private: Global } m_WidgetSpace = WidgetSpace::Global; - EntityID m_Widget = 0; - EntityID m_WidgetX = 0; - EntityID m_WidgetPlaneX = 0; - EntityID m_WidgetY = 0; - EntityID m_WidgetPlaneY = 0; - EntityID m_WidgetZ = 0; - EntityID m_WidgetPlaneZ = 0; - EntityID m_WidgetOrigin = 0; + EntityID m_Widget = EntityID_Invalid; + EntityID m_WidgetX = EntityID_Invalid; + EntityID m_WidgetPlaneX = EntityID_Invalid; + EntityID m_WidgetY = EntityID_Invalid; + EntityID m_WidgetPlaneY = EntityID_Invalid; + EntityID m_WidgetZ = EntityID_Invalid; + EntityID m_WidgetPlaneZ = EntityID_Invalid; + EntityID m_WidgetOrigin = EntityID_Invalid; glm::vec3 m_WidgetCurrentAxis; float m_WidgetPickingDepth = 0.f; - EntityID m_Selection = 0; - EntityID m_LastSelection = 0; + EntityID m_Selection = EntityID_Invalid; + EntityID m_LastSelection = EntityID_Invalid; + EntityID m_UIDraggingEntity = EntityID_Invalid; glm::vec3 m_Position; std::string m_LastDroppedFile; + static boost::filesystem::path openDialog(boost::filesystem::path defaultPath); + static boost::filesystem::path saveDialog(boost::filesystem::path defaultPath); + EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); EventRelay m_EMouseRelease; @@ -70,10 +80,15 @@ private: EventRelay m_EFileDropped; bool OnFileDropped(const Events::FileDropped& e); + void createWidget(); void updateWidget(); void setWidgetMode(WidgetMode newMode); void setWidgetSpace(WidgetSpace space); void drawUI(World* world, double dt); bool createDeleteButton(std::string componentType); + bool createEntityNode(World* world, EntityID entity); void changeParent(EntityID entity, EntityID newParent); + void fileImport(World* world); + void fileSave(World* world); + void fileSaveAs(World* world); }; \ No newline at end of file diff --git a/include/Engine/Rendering/EPicking.h b/include/Engine/Rendering/EPicking.h index 85c4b629..8ee252f0 100644 --- a/include/Engine/Rendering/EPicking.h +++ b/include/Engine/Rendering/EPicking.h @@ -51,7 +51,7 @@ public: if (it != PickingColorsToEntity->end()) { pickData.Entity = it->second; } else { - pickData.Entity = 0; + pickData.Entity = EntityID_Invalid; } pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, Resolution, ProjectionMatrix, ViewMatrix); diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt index 5d43db8b..5d74d7d7 100644 --- a/src/Engine/CMakeLists.txt +++ b/src/Engine/CMakeLists.txt @@ -90,12 +90,27 @@ set(SOURCE_FILES ${SOURCE_FILES_Rendering} ${SOURCE_FILES_Rendering_Util} ${SOURCE_FILES_Collision} + ${SOURCE_FILES_Editor} ${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui.cpp ${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_draw.cpp ${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_demo.cpp - ${SOURCE_FILES_Editor} + ${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_common.c ) +# nativefiledialog +if(WIN32) + set(SOURCE_FILES ${SOURCE_FILES} + ${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_win.cpp + ) +endif() +if(UNIX) + set(SOURCE_FILES ${SOURCE_FILES} + ${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_gtk.c + # TODO: Link with GTK+ here! + ) +endif() + + set(LIBRARIES ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 74efda2e..ed04f4f3 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -10,12 +10,12 @@ World::~World() EntityID World::CreateEntity(EntityID parent /*= 0*/) { EntityID newEntity = generateEntityID(); - if (newEntity != parent) { - m_EntityParents[newEntity] = parent; - m_EntityChildren.insert(std::make_pair(parent, newEntity)); - } else { - LOG_WARNING("Attempted to create an entity with itself as parent! Entity#%i with parent #%i", newEntity, parent); + if (newEntity == parent) { + LOG_WARNING("Invalid parent #%i of Entity#%i", newEntity, parent); + parent = EntityID_Invalid; } + m_EntityParents[newEntity] = parent; + m_EntityChildren.insert(std::make_pair(parent, newEntity)); return newEntity; } diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 07a9e4e8..efef9ccf 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -9,6 +9,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer) auto config = ResourceManager::Load("Config.ini"); m_Enabled = config->Get("Debug.EditorEnabled", false); m_Visible = m_Enabled; + m_DefaultEntityDir = boost::filesystem::path("Schema") / boost::filesystem::path("Entities"); if (!m_Enabled) { return; @@ -44,6 +45,35 @@ void EditorSystem::Update(World* world, double dt) } } + +boost::filesystem::path EditorSystem::openDialog(boost::filesystem::path defaultPath) +{ + namespace bfs = boost::filesystem; + auto absolutePath = bfs::absolute(defaultPath); + nfdchar_t* outPath = nullptr; + nfdresult_t result = NFD_OpenDialog(NULL, absolutePath.string().c_str(), &outPath); + if (result == NFD_ERROR) { + LOG_ERROR("NFD Error: %s", NFD_GetError()); + return bfs::path(); + } + + return bfs::absolute(outPath); +} + +boost::filesystem::path EditorSystem::saveDialog(boost::filesystem::path defaultPath) +{ + namespace bfs = boost::filesystem; + auto absolutePath = bfs::absolute(defaultPath); + nfdchar_t* outPath = nullptr; + nfdresult_t result = NFD_SaveDialog(NULL, absolutePath.string().c_str(), &outPath); + if (result == NFD_ERROR) { + LOG_ERROR("NFD Error: %s", NFD_GetError()); + return bfs::path(); + } + + return bfs::absolute(outPath); +} + bool EditorSystem::OnInputCommand(const Events::InputCommand& e) { if (e.Command == "ToggleEditor" && e.Value > 0) { @@ -81,17 +111,24 @@ bool EditorSystem::OnMousePress(const Events::MousePress& e) bool EditorSystem::OnMouseMove(const Events::MouseMove& e) { - if (m_Widget == 0) { + if (m_Widget == EntityID_Invalid) { return false; } - + if (m_Selection == EntityID_Invalid) { + return false; + } + if (m_Selection == m_Widget) { + return false; + } + // TODO: No widgets for root entity until widgets reside in thier own world, + // or the widgets will move relative to the root entity being moved, which is WEEEIRD. if (m_Selection == 0) { return false; } + auto widgetTransform = m_World->GetComponent(m_Widget, "Transform"); glm::vec3 widgetOrientation = widgetTransform["Orientation"]; - - glm::quat totalOrientation = m_Renderer->Camera()->Orientation() * glm::inverse(glm::quat(widgetOrientation)); + glm::quat totalOrientation = m_Renderer->Camera()->Orientation() * glm::inverse(glm::quat(widgetOrientation)); int width; int height; @@ -122,9 +159,9 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e) if (m_WidgetSpace == WidgetSpace::Global) { EntityID parent = m_World->GetParent(m_Selection); glm::quat inverseParentOrientation; - if (parent != 0) { + //if (parent != 0) { inverseParentOrientation = glm::inverse(RenderQueueFactory::AbsoluteOrientation(m_World, parent)); - } + //} (glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Position"] += inverseParentOrientation * movement; } else if (m_WidgetSpace == WidgetSpace::Local) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); @@ -138,12 +175,12 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e) if (m_WidgetSpace == WidgetSpace::Global) { EntityID parent = m_World->GetParent(m_Selection); glm::quat parentOrientation; - if (parent != 0) { - parentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, parent); - } + //if (parent != 0) { + // parentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, parent); + //} glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"]; - //glm::quat currentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection); - glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation); + glm::quat currentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection); + //glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation); glm::quat deltaOrientation(finalMovement); selectionOrientation = glm::eulerAngles(glm::inverse(parentOrientation) * (deltaOrientation * currentOrientation)); } else if (m_WidgetSpace == WidgetSpace::Local) { @@ -204,9 +241,10 @@ bool EditorSystem::OnPicking(const Events::Picking& e) auto result = e.Pick(pos); EntityID entity = result.Entity; if (glm::length2(m_WidgetCurrentAxis) > 0.f) { + // ??? } else { LOG_INFO("Selected %i", entity); - if (entity != 0) { + if (entity != EntityID_Invalid) { EntityID parent = m_World->GetParent(entity); if (parent == m_Widget) { m_WidgetCurrentAxis = glm::vec3( @@ -221,11 +259,12 @@ bool EditorSystem::OnPicking(const Events::Picking& e) //widgetTransform["Position"] = (glm::vec3)selectionTransform["Position"]; } else { ImGui::SetActiveID(0, nullptr); - m_Selection = entity; + if (m_WidgetMode == WidgetMode::None) { + m_WidgetMode = WidgetMode::Translate; + } setWidgetMode(m_WidgetMode); + m_Selection = entity; } - } else { - m_Selection = 0; } } } @@ -240,9 +279,10 @@ bool EditorSystem::OnFileDropped(const Events::FileDropped& e) return true; } -void EditorSystem::updateWidget() + +void EditorSystem::createWidget() { - if (m_Widget == 0) { + if (m_Widget == EntityID_Invalid) { m_Widget = m_World->CreateEntity(); m_World->AttachComponent(m_Widget, "Transform"); m_WidgetX = m_World->CreateEntity(m_Widget); @@ -269,10 +309,20 @@ void EditorSystem::updateWidget() m_WidgetOrigin = m_World->CreateEntity(m_Widget); m_World->AttachComponent(m_WidgetOrigin, "Transform"); m_World->AttachComponent(m_WidgetOrigin, "Model"); - setWidgetMode(WidgetMode::Translate); + setWidgetMode(WidgetMode::None); + } +} + +void EditorSystem::updateWidget() +{ + if (m_Widget == EntityID_Invalid) { + return; + } + if (m_Selection == m_Widget) { + return; } - if (m_Selection != 0) { + if (m_Selection != EntityID_Invalid) { auto widgetTransform = m_World->GetComponent(m_Widget, "Transform"); glm::vec3 selectionPosition = RenderQueueFactory::AbsolutePosition(m_World, m_Selection); widgetTransform["Position"] = selectionPosition; @@ -284,7 +334,7 @@ void EditorSystem::updateWidget() void EditorSystem::setWidgetMode(WidgetMode newMode) { - if (m_Widget == 0) { + if (m_Widget == EntityID_Invalid) { return; } @@ -309,7 +359,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = true; m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = true; } - if (m_Selection != 0) { + if (m_Selection != EntityID_Invalid) { if (m_WidgetSpace == WidgetSpace::Local) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection)); @@ -321,7 +371,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/ScaleWidgetZ.obj"; m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = true; m_World->GetComponent(m_WidgetOrigin, "Model")["Resource"] = "Models/ScaleWidgetOrigin.obj"; - if (m_Selection != 0) { + if (m_Selection != EntityID_Invalid) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection)); } @@ -329,7 +379,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/RotationWidgetX.obj"; m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/RotationWidgetY.obj"; m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/RotationWidgetZ.obj"; - if (m_Selection != 0) { + if (m_Selection != EntityID_Invalid) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); if (m_WidgetSpace == WidgetSpace::Local) { widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection)); @@ -339,7 +389,6 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) m_WidgetMode = newMode; } - void EditorSystem::setWidgetSpace(WidgetSpace space) { m_WidgetSpace = space; @@ -348,16 +397,23 @@ void EditorSystem::setWidgetSpace(WidgetSpace space) void EditorSystem::drawUI(World* world, double dt) { + namespace bfs = boost::filesystem; + ImGui::ShowTestWindow(); //ImGui::ShowStyleEditor(); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { - - if (ImGui::MenuItem("New")) { } - if (ImGui::MenuItem("Open", "Ctrl+O")) { } - if (ImGui::MenuItem("Save", "Ctrl+S")) { } - if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) { } + //if (ImGui::MenuItem("New")) { } + if (ImGui::MenuItem("Import", "Ctrl+O")) { + fileImport(world); + } + if (ImGui::MenuItem("Save", "Ctrl+S")) { + fileSave(world); + } + if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) { + fileSaveAs(world); + } ImGui::Separator(); if (ImGui::MenuItem("Close Editor", "F1")) { } @@ -392,7 +448,7 @@ void EditorSystem::drawUI(World* world, double dt) std::string title = std::string("Components #") + std::to_string(m_Selection) + std::string("###Components"); if (ImGui::Begin(title.c_str())) { - if (m_Selection != 0) { + if (m_Selection != EntityID_Invalid) { auto& pools = world->GetComponentPools(); std::vector componentTypes; @@ -492,74 +548,81 @@ void EditorSystem::drawUI(World* world, double dt) } ImGui::End(); - if (ImGui::Begin("Entitites")) { - static EntityID draggingEntity = 0; + if (ImGui::Begin("Entities")) { auto entityChildren = world->GetEntityChildren(); std::function recurse = [&](EntityID parent) { auto range = entityChildren.equal_range(parent); for (auto it = range.first; it != range.second; it++) { - - ImVec2 pos = ImGui::GetCursorScreenPos(); - float width = ImGui::GetContentRegionAvailWidth(); - ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13)); - auto window = ImGui::GetCurrentWindow(); - if (m_Selection == it->second) { - const ImU32 col = window->Color(ImGuiCol_HeaderActive); - window->DrawList->AddRectFilled(bb.Min, bb.Max, col); - } - ImGuiID id = window->GetID((std::string("#SelectButton") + std::to_string(it->second)).c_str()); - bool hovered = false; - bool held = false; - if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) { - m_Selection = it->second; - } - if (held) { - ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0); - if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) { - if (draggingEntity == 0) { - draggingEntity = it->second; - LOG_DEBUG("Started drag of entity %i", draggingEntity); - } - ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0)); - ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings); - ImGui::Text("#%i", draggingEntity); - ImGui::End(); - } - } - - ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once); - if (ImGui::TreeNode((std::string("#") + std::to_string(it->second)).c_str())) { - if (draggingEntity != 0 && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) { - LOG_DEBUG("Changed parent of %i to %i", draggingEntity, it->second); - changeParent(draggingEntity, it->second); - draggingEntity = 0; - } - - if (ImGui::BeginPopupContextItem("item context menu")) { - if (ImGui::Button("Add")) { - EntityID entity = world->CreateEntity(it->second); - world->AttachComponent(entity, "Transform"); - } - ImGui::SameLine(); - if (ImGui::Button("Delete")) { - world->DeleteEntity(it->second); - ImGui::CloseCurrentPopup(); - if (m_Selection == it->second) { - m_Selection = 0; - } - } - ImGui::EndPopup(); - } + if (createEntityNode(world, it->second)) { recurse(it->second); ImGui::TreePop(); } } }; - recurse(0); + recurse(EntityID_Invalid); } ImGui::End(); } +bool EditorSystem::createEntityNode(World* world, EntityID entity) +{ + ImVec2 pos = ImGui::GetCursorScreenPos(); + float width = ImGui::GetContentRegionAvailWidth(); + ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13)); + auto window = ImGui::GetCurrentWindow(); + if (m_Selection == entity) { + const ImU32 col = window->Color(ImGuiCol_HeaderActive); + window->DrawList->AddRectFilled(bb.Min, bb.Max, col); + } + ImGuiID id = window->GetID((std::string("#SelectButton") + std::to_string(entity)).c_str()); + bool hovered = false; + bool held = false; + if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) { + m_Selection = entity; + } + if (held) { + ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0); + if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) { + if (m_UIDraggingEntity == EntityID_Invalid) { + m_UIDraggingEntity = entity; + LOG_DEBUG("Started drag of entity %i", m_UIDraggingEntity); + } + ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0)); + ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings); + ImGui::Text("#%i", m_UIDraggingEntity); + ImGui::End(); + } + } + + ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once); + if (ImGui::TreeNode((std::string("#") + std::to_string(entity)).c_str())) { + if (m_UIDraggingEntity != EntityID_Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) { + LOG_DEBUG("Changed parent of %i to %i", m_UIDraggingEntity, entity); + changeParent(m_UIDraggingEntity, entity); + m_UIDraggingEntity = EntityID_Invalid; + } + + if (ImGui::BeginPopupContextItem("item context menu")) { + if (ImGui::Button("Add")) { + EntityID entity = world->CreateEntity(entity); + world->AttachComponent(entity, "Transform"); + } + ImGui::SameLine(); + if (ImGui::Button("Delete")) { + world->DeleteEntity(entity); + ImGui::CloseCurrentPopup(); + if (m_Selection == entity) { + m_Selection = EntityID_Invalid; + } + } + ImGui::EndPopup(); + } + return true; + } else { + return false; + } +} + bool EditorSystem::createDeleteButton(std::string componentType) { float width = ImGui::GetContentRegionAvailWidth(); @@ -594,3 +657,32 @@ void EditorSystem::changeParent(EntityID entity, EntityID newParent) m_World->SetParent(entity, newParent); } + +void EditorSystem::fileImport(World* world) +{ + m_CurrentFile = openDialog(m_DefaultEntityDir); + auto file = ResourceManager::Load(m_CurrentFile.string()); + //EntityFilePreprocessor fpp(file); + //fpp.RegisterComponents(world); + EntityFileParser fp(file); + fp.MergeEntities(world); + createWidget(); + updateWidget(); +} + +void EditorSystem::fileSave(World* world) +{ + if (boost::filesystem::exists(m_CurrentFile)) { + EntityFileWriter writer(m_CurrentFile.string()); + writer.WriteWorld(world); + } else { + fileSaveAs(world); + } +} + +void EditorSystem::fileSaveAs(World* world) +{ + auto filePath = saveDialog(m_DefaultEntityDir); + EntityFileWriter writer(filePath.string()); + writer.WriteWorld(world); +} diff --git a/src/Engine/Rendering/RenderQueueFactory.cpp b/src/Engine/Rendering/RenderQueueFactory.cpp index 8d5bb420..14e55320 100644 --- a/src/Engine/Rendering/RenderQueueFactory.cpp +++ b/src/Engine/Rendering/RenderQueueFactory.cpp @@ -27,16 +27,16 @@ glm::vec3 RenderQueueFactory::AbsolutePosition(World* world, EntityID entity) { glm::vec3 position; - do { + while (entity != EntityID_Invalid) { ComponentWrapper transform = world->GetComponent(entity, "Transform"); EntityID parent = world->GetParent(entity); - if (parent != 0) { + //if (parent != EntityID_Invalid) { position += AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"]; - } else { - position += (glm::vec3)transform["Position"]; - } + //} else { + // position += (glm::vec3)transform["Position"]; + //} entity = parent; - } while (entity != 0); + } return position; } @@ -45,11 +45,11 @@ glm::quat RenderQueueFactory::AbsoluteOrientation(World* world, EntityID entity) { glm::quat orientation; - do { + while (entity != EntityID_Invalid) { ComponentWrapper transform = world->GetComponent(entity, "Transform"); orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation; entity = world->GetParent(entity); - } while (entity != 0); + } return orientation; } @@ -58,11 +58,11 @@ glm::vec3 RenderQueueFactory::AbsoluteScale(World* world, EntityID entity) { glm::vec3 scale(1.f); - do { + while (entity != EntityID_Invalid) { ComponentWrapper transform = world->GetComponent(entity, "Transform"); scale *= (glm::vec3)transform["Scale"]; entity = world->GetParent(entity); - } while (entity != 0); + } return scale; } diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index c6f2286e..8ac4442f 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -52,11 +52,8 @@ Game::Game(int argc, char* argv[]) auto file = ResourceManager::Load(mapToLoad); EntityFilePreprocessor fpp(file); fpp.RegisterComponents(m_World); - EntityFileParser fp(file); - fp.MergeEntities(m_World); - - EntityFileWriter writer("Testasdasdasd.xml"); - writer.WriteWorld(m_World); + //EntityFileParser fp(file); + //fp.MergeEntities(m_World); } // Create system pipeline From e8e93587e9f97c1b43d8461f8cf829498aa741dc Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 10:38:23 +0100 Subject: [PATCH 12/18] Fixed weird behavior when components would register multiple times (Hint: It should never happen in the first place) --- src/Engine/Core/World.cpp | 4 +++- src/Engine/Editor/EditorSystem.cpp | 4 ++-- src/Game/Game.cpp | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index ed04f4f3..d947d1fd 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -53,7 +53,9 @@ void World::DeleteEntity(EntityID entity) void World::RegisterComponent(ComponentInfo& ci) { - m_ComponentPools[ci.Name] = new ComponentPool(ci); + if (m_ComponentPools.find(ci.Name) == m_ComponentPools.end()) { + m_ComponentPools[ci.Name] = new ComponentPool(ci); + } } ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType) diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index efef9ccf..75217dfe 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -662,8 +662,8 @@ void EditorSystem::fileImport(World* world) { m_CurrentFile = openDialog(m_DefaultEntityDir); auto file = ResourceManager::Load(m_CurrentFile.string()); - //EntityFilePreprocessor fpp(file); - //fpp.RegisterComponents(world); + EntityFilePreprocessor fpp(file); + fpp.RegisterComponents(world); EntityFileParser fp(file); fp.MergeEntities(world); createWidget(); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 8ac4442f..c66d2641 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -52,8 +52,8 @@ Game::Game(int argc, char* argv[]) auto file = ResourceManager::Load(mapToLoad); EntityFilePreprocessor fpp(file); fpp.RegisterComponents(m_World); - //EntityFileParser fp(file); - //fp.MergeEntities(m_World); + EntityFileParser fp(file); + fp.MergeEntities(m_World); } // Create system pipeline From c47ef61ddccf2491eb939347fb1518dae085333c Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 11:20:25 +0100 Subject: [PATCH 13/18] Temporary fix for widgets being annoying entities in the editor --- src/Engine/Editor/EditorSystem.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 75217dfe..20ad2dbd 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -566,6 +566,11 @@ void EditorSystem::drawUI(World* world, double dt) bool EditorSystem::createEntityNode(World* world, EntityID entity) { + // HACK: Don't show the widget entities in the entity tree + if (entity == m_Widget) { + return false; + } + ImVec2 pos = ImGui::GetCursorScreenPos(); float width = ImGui::GetContentRegionAvailWidth(); ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13)); @@ -673,8 +678,14 @@ void EditorSystem::fileImport(World* world) void EditorSystem::fileSave(World* world) { if (boost::filesystem::exists(m_CurrentFile)) { + // HACK: Delete the widgets so they don't appear in the saved file + world->DeleteEntity(m_Widget); + m_Widget = EntityID_Invalid; + EntityFileWriter writer(m_CurrentFile.string()); writer.WriteWorld(world); + + createWidget(); } else { fileSaveAs(world); } @@ -683,6 +694,16 @@ void EditorSystem::fileSave(World* world) void EditorSystem::fileSaveAs(World* world) { auto filePath = saveDialog(m_DefaultEntityDir); + if (filePath.empty()) { + return; + } + + // HACK: Delete the widgets so they don't appear in the saved file + world->DeleteEntity(m_Widget); + m_Widget = EntityID_Invalid; + EntityFileWriter writer(filePath.string()); writer.WriteWorld(world); + + createWidget(); } From 297a3fb49330f5d5203bdab7483278f87ba2be46 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 11:24:00 +0100 Subject: [PATCH 14/18] Support for "name" attribute on entities --- include/Engine/Core/EntityFile.h | 28 +++++++++++++------------- include/Engine/Core/EntityFileParser.h | 8 ++++---- include/Engine/Core/World.h | 5 +++++ src/Engine/Core/EntityFileParser.cpp | 13 +++++++----- src/Engine/Core/EntityFileWriter.cpp | 8 ++++++++ src/Engine/Core/World.cpp | 22 ++++++++++++++++++++ src/Engine/Editor/EditorSystem.cpp | 10 +++++++-- 7 files changed, 69 insertions(+), 25 deletions(-) diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h index 962e5c4e..27115264 100644 --- a/include/Engine/Core/EntityFile.h +++ b/include/Engine/Core/EntityFile.h @@ -36,23 +36,23 @@ class EntityFileHandler public: // @param EntityID The entity found // @param EntityID The parent of the entity - typedef std::function OnStartEntityCallback; + typedef std::function OnStartEntityCallback; void SetStartEntityCallback(OnStartEntityCallback c) { m_OnStartEntityCallback = c; } // @param EntityID The entity the component corresponds to // @param std::string Type name of the component - typedef std::function OnStartComponentCallback; + typedef std::function OnStartComponentCallback; void SetStartComponentCallback(OnStartComponentCallback c) { m_OnStartComponentCallback = c; } // @param EntityID Entity // @param std::string Component name // @param std::string Field name // @param std::map Field attribute names and values - typedef std::function)> OnStartFieldCallback; + typedef std::function&)> OnStartFieldCallback; void SetStartFieldCallback(OnStartFieldCallback c) { m_OnStartFieldCallback = c; } // @param EntityID Entity // @param std::string Component name // @param std::string Field name // @param char* Field data - typedef std::function OnStartFieldDataCallback; + typedef std::function OnStartFieldDataCallback; void SetStartFieldDataCallback(OnStartFieldDataCallback c) { m_OnStartFieldDataCallback = c; } private: @@ -172,13 +172,13 @@ private: { EntityID parent = m_EntityStack.top(); - // TODO: Create entity here - auto xName = attrs.getValue(XS::ToXMLCh("name")); - std::string name = XS::ToString(xName); - //LOG_DEBUG("Entity %i (%i): %s", m_NextEntityID, parent, name.c_str()); - if (m_Handler->m_OnStartEntityCallback) { - m_Handler->m_OnStartEntityCallback(m_NextEntityID, parent); + std::string name; + auto xName = attrs.getValue(XS::ToXMLCh("name")); + if (xName != nullptr) { + name = XS::ToString(xName); + } + m_Handler->m_OnStartEntityCallback(m_NextEntityID, parent, name); } m_EntityStack.push(m_NextEntityID); @@ -198,7 +198,7 @@ private: parser->parse(path.c_str()); delete parser; } - void onStartComponent(std::string name) + void onStartComponent(const std::string& name) { //LOG_DEBUG(" Component: %s", name.c_str()); m_CurrentComponent = name; @@ -206,8 +206,8 @@ private: m_Handler->m_OnStartComponentCallback(m_EntityStack.top(), name); } } - void onEndComponent(std::string name) { } - void onStartComponentField(std::string field, const xercesc::Attributes& attrs) + void onEndComponent(const std::string& name) { } + void onStartComponentField(const std::string& field, const xercesc::Attributes& attrs) { //LOG_DEBUG(" Field: %s", field.c_str()); m_CurrentField = field; @@ -223,7 +223,7 @@ private: m_Handler->m_OnStartFieldCallback(m_EntityStack.top(), m_CurrentComponent, field, m_CurrentAttributes); } } - void onEndComponentField(std::string field) { } + void onEndComponentField(const std::string& field) { } void onFieldData(char* data) { diff --git a/include/Engine/Core/EntityFileParser.h b/include/Engine/Core/EntityFileParser.h index c4c8c221..d46b508b 100644 --- a/include/Engine/Core/EntityFileParser.h +++ b/include/Engine/Core/EntityFileParser.h @@ -19,10 +19,10 @@ private: // created in order to resolve parent-child relationships. std::map m_EntityIDMapper; - void onStartEntity(EntityID entity, EntityID parent); - void onStartComponent(EntityID entity, std::string component); - void onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes); - void onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData); + void onStartEntity(EntityID entity, EntityID parent, const std::string& name); + void onStartComponent(EntityID entity, const std::string& component); + void onStartComponentField(EntityID entity, const std::string& componentType, const std::string& fieldName, const std::map& attributes); + void onFieldData(EntityID entity, const std::string& componentType, const std::string& fieldName, const char* fieldData); }; #endif \ No newline at end of file diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 22e684b0..1ea17a7b 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -37,6 +37,10 @@ public: const std::unordered_map& GetComponentPools() const { return m_ComponentPools; } // Get the entity children map const std::unordered_multimap& GetEntityChildren() const { return m_EntityChildren; } + // Set the textual name of an entity + void SetName(EntityID entity, const std::string& name); + // Get the textual name of an entity + std::string GetName(EntityID entity) const; private: EntityID m_CurrentEntityID = 0; @@ -45,6 +49,7 @@ private: // TODO: This should be a more effective structure std::unordered_multimap m_EntityChildren; std::unordered_map m_ComponentPools; + std::unordered_map m_EntityNames; EntityID generateEntityID(); }; diff --git a/src/Engine/Core/EntityFileParser.cpp b/src/Engine/Core/EntityFileParser.cpp index b3310cd4..541da01d 100644 --- a/src/Engine/Core/EntityFileParser.cpp +++ b/src/Engine/Core/EntityFileParser.cpp @@ -3,7 +3,7 @@ EntityFileParser::EntityFileParser(const EntityFile* entityFile) : m_EntityFile(entityFile) { - m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2)); + m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_Handler.SetStartComponentCallback(std::bind(&EntityFileParser::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); m_Handler.SetStartFieldCallback(std::bind(&EntityFileParser::onStartComponentField, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); @@ -16,22 +16,25 @@ void EntityFileParser::MergeEntities(World* world) m_EntityFile->Parse(&m_Handler); } -void EntityFileParser::onStartEntity(EntityID entity, EntityID parent) +void EntityFileParser::onStartEntity(EntityID entity, EntityID parent, const std::string& name) { EntityID realParent = m_EntityIDMapper.at(parent); EntityID realEntity = m_World->CreateEntity(realParent); + if (!name.empty()) { + m_World->SetName(realEntity, name); + } m_EntityIDMapper[entity] = realEntity; LOG_DEBUG("Created entity #%i (%i) with parent %i (%i)", entity, realEntity, parent, realParent); } -void EntityFileParser::onStartComponent(EntityID entity, std::string component) +void EntityFileParser::onStartComponent(EntityID entity, const std::string& component) { EntityID realEntity = m_EntityIDMapper.at(entity); m_World->AttachComponent(realEntity, component); LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity); } -void EntityFileParser::onStartComponentField(EntityID entity, std::string componentType, std::string fieldName, std::map attributes) +void EntityFileParser::onStartComponentField(EntityID entity, const std::string& componentType, const std::string& fieldName, const std::map& attributes) { EntityID realEntity = m_EntityIDMapper.at(entity); ComponentWrapper component = m_World->GetComponent(realEntity, componentType); @@ -47,7 +50,7 @@ void EntityFileParser::onStartComponentField(EntityID entity, std::string compon EntityFile::WriteAttributeData(data, field, attributes); } -void EntityFileParser::onFieldData(EntityID entity, std::string componentType, std::string fieldName, const char* fieldData) +void EntityFileParser::onFieldData(EntityID entity, const std::string& componentType, const std::string& fieldName, const char* fieldData) { EntityID realEntity = m_EntityIDMapper.at(entity); ComponentWrapper component = m_World->GetComponent(realEntity, componentType); diff --git a/src/Engine/Core/EntityFileWriter.cpp b/src/Engine/Core/EntityFileWriter.cpp index 161c3169..5b6c466d 100644 --- a/src/Engine/Core/EntityFileWriter.cpp +++ b/src/Engine/Core/EntityFileWriter.cpp @@ -15,6 +15,10 @@ void EntityFileWriter::WriteEntity(World* world, EntityID entity) root->setAttribute(X("xmlns:xsi"), X("http://www.w3.org/2001/XMLSchema-instance")); root->setAttribute(X("xsi:noNamespaceSchemaLocation"), X("../Types/Entity.xsd")); root->setAttribute(X("xmlns:c"), X("components")); + const std::string& name = world->GetName(entity); + if (!name.empty()) { + root->setAttribute(X("name"), X(name)); + } DOMElement* componentsElement = doc->createElement(X("Components")); root->appendChild(componentsElement); @@ -47,6 +51,10 @@ void EntityFileWriter::appendEntityChildren(xercesc::DOMElement* parentElement, EntityID childEntity = it->second; DOMElement* entityElement = doc->createElement(X("Entity")); + const std::string& name = world->GetName(childEntity); + if (!name.empty()) { + entityElement->setAttribute(X("name"), X(name)); + } parentElement->appendChild(entityElement); DOMElement* componentsElement = doc->createElement(X("Components")); diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index d947d1fd..9202be12 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -49,6 +49,9 @@ void World::DeleteEntity(EntityID entity) break; } } + + // Erase potential name + m_EntityNames.erase(entity); } void World::RegisterComponent(ComponentInfo& ci) @@ -121,6 +124,25 @@ void World::SetParent(EntityID entity, EntityID parent) m_EntityChildren.insert(std::make_pair(parent, entity)); } +void World::SetName(EntityID entity, const std::string& name) +{ + m_EntityNames[entity] = name; +} + +std::string World::GetName(EntityID entity) const +{ + if (entity == EntityID_Invalid) { + return "EntityID_Invalid"; + } + + auto it = m_EntityNames.find(entity); + if (it != m_EntityNames.end()) { + return it->second; + } else { + return std::string(); + } +} + EntityID World::generateEntityID() { // TODO: Make EntityID generation smarter diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 20ad2dbd..4ceba0fd 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -279,7 +279,6 @@ bool EditorSystem::OnFileDropped(const Events::FileDropped& e) return true; } - void EditorSystem::createWidget() { if (m_Widget == EntityID_Invalid) { @@ -600,7 +599,14 @@ bool EditorSystem::createEntityNode(World* world, EntityID entity) } ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once); - if (ImGui::TreeNode((std::string("#") + std::to_string(entity)).c_str())) { + std::string nodeTitle; + const std::string& entityName = world->GetName(entity); + if (!entityName.empty()) { + nodeTitle = entityName; + } else { + nodeTitle = std::string("#") + std::to_string(entity); + } + if (ImGui::TreeNode(nodeTitle.c_str())) { if (m_UIDraggingEntity != EntityID_Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) { LOG_DEBUG("Changed parent of %i to %i", m_UIDraggingEntity, entity); changeParent(m_UIDraggingEntity, entity); From 21aea6f31d772a354a1de061075effd8d42aa75a Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 13:57:25 +0100 Subject: [PATCH 15/18] Added World::ValidEntity for checking if an entity exists or not --- include/Engine/Core/World.h | 3 ++- src/Engine/Core/World.cpp | 9 +++++---- src/Engine/Editor/EditorSystem.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 1ea17a7b..0a121728 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -16,7 +16,8 @@ public: EntityID CreateEntity(EntityID parent = 0); // Delete entity and all components within void DeleteEntity(EntityID entity); - + // Check if an entity exists + bool ValidEntity(EntityID entity) const; // Register a component type and allocate space for it void RegisterComponent(ComponentInfo& ci); // Attach a component to an entity and fill it with default values diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 9202be12..c94cb8b3 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -54,6 +54,11 @@ void World::DeleteEntity(EntityID entity) m_EntityNames.erase(entity); } +bool World::ValidEntity(EntityID entity) const +{ + return m_EntityParents.find(entity) != m_EntityParents.end(); +} + void World::RegisterComponent(ComponentInfo& ci) { if (m_ComponentPools.find(ci.Name) == m_ComponentPools.end()) { @@ -75,7 +80,6 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy return c; } - bool World::HasComponent(EntityID entity, std::string componentType) const { ComponentPool* pool = m_ComponentPools.at(componentType); @@ -88,7 +92,6 @@ ComponentWrapper World::GetComponent(EntityID entity, std::string componentType) return pool->GetByEntity(entity); } - void World::DeleteComponent(EntityID entity, std::string componentType) { ComponentPool* pool = m_ComponentPools.at(componentType); @@ -102,13 +105,11 @@ const ComponentPool* World::GetComponents(std::string componentType) return (it != m_ComponentPools.end()) ? it->second : nullptr; } - EntityID World::GetParent(EntityID entity) { return m_EntityParents.at(entity); } - void World::SetParent(EntityID entity, EntityID parent) { EntityID lastParent = m_EntityParents.at(entity); diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 4ceba0fd..65d9c52b 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -622,7 +622,7 @@ bool EditorSystem::createEntityNode(World* world, EntityID entity) if (ImGui::Button("Delete")) { world->DeleteEntity(entity); ImGui::CloseCurrentPopup(); - if (m_Selection == entity) { + if (!world->ValidEntity(m_Selection)) { m_Selection = EntityID_Invalid; } } From 4a516a4d86c989ee7a54031b26b0c4e611bceb61 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 14:56:53 +0100 Subject: [PATCH 16/18] Added ComponentInfo::FieldsInOrder to be able to loop through component fields in a consistent order based on the component XSD definition --- include/Engine/Core/ComponentInfo.h | 1 + src/Engine/Core/EntityFilePreprocessor.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h index 909224e2..abf4ff15 100644 --- a/include/Engine/Core/ComponentInfo.h +++ b/include/Engine/Core/ComponentInfo.h @@ -21,6 +21,7 @@ struct ComponentInfo std::string Name; std::unordered_map Fields; + std::vector FieldsInOrder; Meta_t Meta; std::shared_ptr Defaults = nullptr; }; diff --git a/src/Engine/Core/EntityFilePreprocessor.cpp b/src/Engine/Core/EntityFilePreprocessor.cpp index 4fd768a1..83e2fe28 100644 --- a/src/Engine/Core/EntityFilePreprocessor.cpp +++ b/src/Engine/Core/EntityFilePreprocessor.cpp @@ -143,9 +143,11 @@ void EntityFilePreprocessor::parseComponentInfo() continue; } - compInfo.Fields[name].Type = type; - compInfo.Fields[name].Offset = fieldOffset; - compInfo.Fields[name].Stride = stride; + auto& field = compInfo.Fields[name]; + field.Type = type; + field.Offset = fieldOffset; + field.Stride = stride; + compInfo.FieldsInOrder.push_back(&field); fieldOffset += stride; } From 2a5e0420c80c3e10e219eda06356f38bbe6e5e74 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 16:40:26 +0100 Subject: [PATCH 17/18] Added template entity "Empty.xml" for use as root entity when creating a new map. --- resources/Schema/Entities/Empty.xml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 resources/Schema/Entities/Empty.xml diff --git a/resources/Schema/Entities/Empty.xml b/resources/Schema/Entities/Empty.xml new file mode 100644 index 00000000..70581847 --- /dev/null +++ b/resources/Schema/Entities/Empty.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 349fa9d7fae3702dc9de2e2a9090136e2f621c8a Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 11 Jan 2016 16:47:30 +0100 Subject: [PATCH 18/18] Fixed crash when creating new entities --- src/Engine/Editor/EditorSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 65d9c52b..b49cf555 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -615,8 +615,8 @@ bool EditorSystem::createEntityNode(World* world, EntityID entity) if (ImGui::BeginPopupContextItem("item context menu")) { if (ImGui::Button("Add")) { - EntityID entity = world->CreateEntity(entity); - world->AttachComponent(entity, "Transform"); + EntityID newEntity = world->CreateEntity(entity); + world->AttachComponent(newEntity, "Transform"); } ImGui::SameLine(); if (ImGui::Button("Delete")) {