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