From d7de4fc6940a9d371bcea95da59a82c50d263934 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 8 Dec 2015 18:07:32 +0100 Subject: [PATCH] Component definitions now loaded from schema. No default values yet. --- include/Engine/Core/ComponentWrapper.h | 4 +- include/Engine/Core/Entity.h | 6 + include/Engine/Core/EntityFile.h | 12 - include/Engine/Core/EntityWrapper.h | 15 -- include/Engine/Core/EntityXMLFile.h | 139 ++++++++++ include/Engine/Core/World.h | 3 +- include/Game/HardcodedTestWorld.h | 23 +- src/Engine/Core/EntityXMLFile.cpp | 342 +++++++++++++++++++++++++ src/Engine/Core/World.cpp | 6 +- 9 files changed, 504 insertions(+), 46 deletions(-) create mode 100644 include/Engine/Core/Entity.h delete mode 100644 include/Engine/Core/EntityFile.h delete mode 100644 include/Engine/Core/EntityWrapper.h create mode 100644 include/Engine/Core/EntityXMLFile.h create mode 100644 src/Engine/Core/EntityXMLFile.cpp diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 95a31240..56ae943f 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -2,7 +2,7 @@ #define ComponentWrapper_h__ #include "../Common.h" -#include "EntityWrapper.h" +#include "Entity.h" #include "ComponentInfo.h" #include "Util/Any.h" @@ -11,7 +11,7 @@ struct ComponentWrapper ComponentWrapper(const ComponentInfo& componentInfo, char* data) : Info(componentInfo) , EntityID(*reinterpret_cast<::EntityID*>(data)) - , Data(data + sizeof(EntityID)) + , Data(data + sizeof(::EntityID)) { } const ComponentInfo& Info; diff --git a/include/Engine/Core/Entity.h b/include/Engine/Core/Entity.h new file mode 100644 index 00000000..c6a475e3 --- /dev/null +++ b/include/Engine/Core/Entity.h @@ -0,0 +1,6 @@ +#ifndef Entity_h__ +#define Entity_h__ + +typedef unsigned int EntityID; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h deleted file mode 100644 index 7fa423d6..00000000 --- a/include/Engine/Core/EntityFile.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "ResourceManager.h" - -class EntityFile : public Resource -{ - friend class ResourceManager; - -private: - EntityFile(std::string path); - -public: - -}; \ No newline at end of file diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h deleted file mode 100644 index d5e723ca..00000000 --- a/include/Engine/Core/EntityWrapper.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef Entity_h__ -#define Entity_h__ - -typedef unsigned int EntityID; - -struct EntityWrapper -{ - EntityWrapper(EntityID entityID) - : ID(entityID) - { } - - EntityID ID; -}; - -#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityXMLFile.h b/include/Engine/Core/EntityXMLFile.h new file mode 100644 index 00000000..5217e555 --- /dev/null +++ b/include/Engine/Core/EntityXMLFile.h @@ -0,0 +1,139 @@ +#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(); + std::size_t getTypeStride(std::string typeName); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 20bbe288..ab6f43fa 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -2,7 +2,7 @@ #define World_h__ #include "../Common.h" -#include "EntityWrapper.h" +#include "Entity.h" #include "ObjectPool.h" #include "ComponentPool.h" @@ -12,6 +12,7 @@ public: World() = default; ~World(); + // Create empty entity EntityID CreateEntity(EntityID parent = 0); // Register a component type and allocate space for it diff --git a/include/Game/HardcodedTestWorld.h b/include/Game/HardcodedTestWorld.h index 1176402d..bf3f2f07 100644 --- a/include/Game/HardcodedTestWorld.h +++ b/include/Game/HardcodedTestWorld.h @@ -4,6 +4,7 @@ #include "GLM.h" #include "Core/World.h" #include "Core/Util/Any.h" +#include "Core/EntityXMLFile.h" class HardcodedTestWorld : public World { @@ -30,11 +31,11 @@ private: f.AddProperty("Name", std::string("Unnamed")); RegisterComponent(f); - f = ComponentWrapperFactory("Transform"); - f.AddProperty("Position", glm::vec3(0.f, 0.f, 0.f)); - f.AddProperty("Orientation", glm::quat()); - f.AddProperty("Scale", glm::vec3(1.f, 1.f, 1.f)); - RegisterComponent(f); + //f = ComponentWrapperFactory("Transform"); + //f.AddProperty("Position", glm::vec3(0.f, 0.f, 0.f)); + //f.AddProperty("Orientation", glm::quat()); + //f.AddProperty("Scale", glm::vec3(1.f, 1.f, 1.f)); + //RegisterComponent(f); f = ComponentWrapperFactory("Model"); f.AddProperty("Resource", std::string()); @@ -45,16 +46,14 @@ private: void createTestEntities() { + ResourceManager::RegisterType("EntityXMLFile"); + ResourceManager::Load("Schema/Entities/Test.xml")->PopulateWorld(this); + World& world = *this; // Create an entity EntityID e = world.CreateEntity(); - // Attach a Debug component - ComponentWrapper debug = world.AttachComponent(e, "Debug"); - // Set the Name field of the Debug component using subscript operator - debug["Name"] = "Carlito"; - // Attach a Transform component world.AttachComponent(e, "Transform"); // Fetch the component based on EntityID and component type @@ -74,10 +73,6 @@ private: std::cout << "Position: " << pos.x << " " << pos.y << " " << pos.z << std::endl; glm::vec3 scale = transform["Scale"]; std::cout << "Scale: " << scale.x << " " << scale.y << " " << scale.z << std::endl; - - // Fetch the Debug component also present in this entity - ComponentWrapper debug = world.GetComponent(transform.EntityID, "Debug"); - std::cout << "Name: " << (std::string)debug["Name"] << std::endl; } } }; \ No newline at end of file diff --git a/src/Engine/Core/EntityXMLFile.cpp b/src/Engine/Core/EntityXMLFile.cpp new file mode 100644 index 00000000..450a8c13 --- /dev/null +++ b/src/Engine/Core/EntityXMLFile.cpp @@ -0,0 +1,342 @@ +#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 + //parseEntityGraph(); +} + + +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 = 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() +{ + +} + +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; + + // TODO: ALLOCATE HERE + /*ComponentPool cs; + cs.ComponentName = ci.Name; + cs.Stride = stride; + cs.Info = ci; + cs.Data = new char[stride*ci.Meta.Allocation]; + m_ComponentStore[cs.ComponentName] = cs;*/ + } +} + +void EntityXMLFile::parseEntityGraph() +{ + //using namespace xercesc; + + //auto root = m_DOMDocument->getDocumentElement(); + + //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& 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::size_t EntityXMLFile::getTypeStride(std::string typeName) +{ + std::map typeStrides{ + { "int", sizeof(int) }, + { "double", sizeof(double) }, + { "string", sizeof(std::string) }, + { "Vector", sizeof(glm::vec3) }, + { "Quaternion", sizeof(glm::quat) }, + }; + + auto it = typeStrides.find(typeName); + return (it != typeStrides.end()) ? it->second : 0; +} diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 26d3f1e4..7f9fd43a 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -29,8 +29,10 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy // Allocate space for the component ComponentWrapper c = pool->Allocate(entity); - // Write default values - memcpy(c.Data, ci.Defaults.get(), ci.Meta.Stride); + // TODO: Write default values + if (ci.Defaults != nullptr) { + //memcpy(c.Data, ci.Defaults.get(), ci.Meta.Stride); + } return c; }