From 6ded34d1605614e2776f2609b7335dde200df322 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 1 Dec 2015 09:51:15 +0100 Subject: [PATCH 01/10] Made Rendering/Util visible to CMake project --- src/Engine/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt index 861573a3..7bc15041 100644 --- a/src/Engine/CMakeLists.txt +++ b/src/Engine/CMakeLists.txt @@ -48,7 +48,12 @@ file(GLOB SOURCE_FILES_Rendering "${INCLUDE_PATH}/Rendering/*.h" "Rendering/*.cpp" ) +file(GLOB SOURCE_FILES_Rendering_Util + "${INCLUDE_PATH}/Rendering/Util/*.h" + "Rendering/Util/*.cpp" +) source_group(Rendering FILES ${SOURCE_FILES_Rendering}) +source_group(Rendering\\Util FILES ${SOURCE_FILES_Rendering_Util}) file(GLOB SOURCE_FILES_GUI "${INCLUDE_PATH}/GUI/*.h" From 24ef915248002034167e282d47e69df2fd6ea11f Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 1 Dec 2015 10:40:33 +0100 Subject: [PATCH 02/10] Simplified CMake for Game --- src/Game/CMakeLists.txt | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index 2fceae1e..b513fa92 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -1,31 +1,13 @@ project(TacticalZ-Game) -find_package(OpenGL REQUIRED) -find_package(GLEW REQUIRED) -find_package(GLFW REQUIRED) find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono) -find_package(assimp REQUIRED) -find_package(ZLIB REQUIRED) -find_package(PNG REQUIRED) -# Because FindOpenAL is retarded -#set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${CMAKE_SOURCE_DIR}/deps/include/AL") -#find_package(OpenAL REQUIRED) -if(UNIX) - find_package(X11 REQUIRED) -endif() set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include/Game) include_directories( ${CMAKE_SOURCE_DIR}/include/Engine + ${CMAKE_SOURCE_DIR}/deps/include ${INCLUDE_PATH} - ${OPENGL_INCLUDE_DIR} - ${GLEW_INCLUDE_DIRS} - ${GLFW_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} - ${assimp_INCLUDE_DIRS} - ${PNG_INCLUDE_DIRS} - ${OPENAL_INCLUDE_DIR} - ${X11_INCLUDE_DIRS} ) file(GLOB SOURCE_FILES From 521de592867dcf52f14fec2c5e4dc151adf88ef9 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 1 Dec 2015 11:21:33 +0100 Subject: [PATCH 03/10] Made deploy create a bin folder so it doesn't fail if it's missing --- tools/deploy.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/deploy.bat b/tools/deploy.bat index b3f51c07..cdab1c20 100755 --- a/tools/deploy.bat +++ b/tools/deploy.bat @@ -1,6 +1,7 @@ @ECHO off SET DeployLocation=bin\ +MKDIR %DeployLocation% ECHO Deploying assets to %DeployLocation% :: Asset folders From 6ca1977e786daf49ea29519130228df2f2b16f26 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 1 Dec 2015 10:42:08 +0100 Subject: [PATCH 04/10] Added Xerces dependency --- README.md | 1 + cmake/FindXerces.cmake | 23 +++++++++++++++++++++++ deps | 2 +- src/Engine/CMakeLists.txt | 3 +++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 cmake/FindXerces.cmake diff --git a/README.md b/README.md index 59f61f1b..217cb69e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Libraries bundled along with binaries for Windows (MSVC14), available as a submo | **[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) | #### External libraries Libraries that are too big to be bundled with the project. diff --git a/cmake/FindXerces.cmake b/cmake/FindXerces.cmake new file mode 100644 index 00000000..8ea0fb1d --- /dev/null +++ b/cmake/FindXerces.cmake @@ -0,0 +1,23 @@ +# XERCES_FOUND +# XERCES_INCLUDE_DIRS +# XERCES_LIBRARIES + +find_path(XERCES_INCLUDE_DIR xercesc/dom/dom.hpp + /usr/local/include + /usr/include +) + +find_library(XERCES_LIBRARY + NAMES + xerces-c_3 + xerces-c_3D + PATHS + /usr/local/lib + /usr/lib +) + +set(XERCES_INCLUDE_DIRS ${XERCES_INCLUDE_DIR}) +set(XERCES_LIBRARIES ${XERCES_LIBRARY}) + +find_package_handle_standard_args(Xerces DEFAULT_MSG XERCES_LIBRARY XERCES_INCLUDE_DIR) +mark_as_advanced(Xerces_FOUND XERCES_INCLUDE_DIR XERCES_LIBRARY) \ No newline at end of file diff --git a/deps b/deps index 65d3f3bc..1b478d31 160000 --- a/deps +++ b/deps @@ -1 +1 @@ -Subproject commit 65d3f3bc314357b8ec67a99f9f69a3fb73b813c1 +Subproject commit 1b478d3159f12273059a684ee8e187f4a25c89f0 diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt index 7bc15041..396a34b4 100644 --- a/src/Engine/CMakeLists.txt +++ b/src/Engine/CMakeLists.txt @@ -7,6 +7,7 @@ find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono) find_package(assimp REQUIRED) find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) +find_package(Xerces REQUIRED) # Because FindOpenAL is retarded #set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${CMAKE_SOURCE_DIR}/deps/include/AL") #find_package(OpenAL REQUIRED) @@ -23,6 +24,7 @@ include_directories( ${Boost_INCLUDE_DIRS} ${assimp_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} + ${Xerces_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ${X11_INCLUDE_DIRS} ) @@ -76,6 +78,7 @@ set(LIBRARIES ${Boost_LIBRARIES} ${assimp_LIBRARIES} ${PNG_LIBRARIES} + ${Xerces_LIBRARIES} ${OPENAL_LIBRARY} ${X11_LIBRARIES} ) From a5affd2dc2da8ac0eea009a41ee1db53adcb2688 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 1 Dec 2015 16:47:54 +0100 Subject: [PATCH 05/10] Added barebones ECS prototype --- assets | 2 +- include/Engine/Core/Component.h | 63 ++++ include/Engine/Core/ComponentInfo.h | 15 + include/Engine/Core/ComponentPool.h | 7 + include/Engine/Core/Entity.h | 12 + include/Engine/Core/EntityParser.h | 479 ++++++++++++++++++++++++++++ include/Engine/Core/World.h | 11 + 7 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 include/Engine/Core/Component.h create mode 100644 include/Engine/Core/ComponentInfo.h create mode 100644 include/Engine/Core/ComponentPool.h create mode 100644 include/Engine/Core/Entity.h create mode 100644 include/Engine/Core/EntityParser.h create mode 100644 include/Engine/Core/World.h diff --git a/assets b/assets index 6b30aa83..0bcd7bc2 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6b30aa83cfe6bc7df6453e17dbb63da91100b7ec +Subproject commit 0bcd7bc2b87d3c19ee794960ebd5e8ee5159273c diff --git a/include/Engine/Core/Component.h b/include/Engine/Core/Component.h new file mode 100644 index 00000000..3093f35f --- /dev/null +++ b/include/Engine/Core/Component.h @@ -0,0 +1,63 @@ +#ifndef Component_h__ +#define Component_h__ + +#include "../Common.h" +#include "ComponentInfo.h" + +struct Component +{ + Component(const unsigned int entityID, const ComponentInfo* componentInfo, char* data) + : EntityID(entityID) + , Info(componentInfo) + , Data(data) + { } + + const unsigned int EntityID; + const ComponentInfo* Info; + char* Data; + + template + T& Property(std::string name) + { + unsigned int offset=Info->FieldOffsets.at(name); + return *reinterpret_cast(&Data[offset]); + } + + template + void SetProperty(std::string name, T& value) { Property(name)=value; } + template + void SetProperty(std::string name, const T value) { Property(name)=value; } + + // Specialization for string literals + template + void SetProperty(std::string name, const char(&value)[N]) { Property(name)=std::string(value); } + + struct SubscriptProxy + { + friend struct Component; + private: + SubscriptProxy(Component* component, std::string& propertyName) + : m_Component(component) + , m_PropertyName(propertyName) + { } + + Component* m_Component; + std::string& m_PropertyName; + + public: + template + operator T&() { return m_Component->Property(m_PropertyName); } + + template + void operator=(const T val) { m_Component->SetProperty(m_PropertyName, val); } + template + void operator=(T& val) { m_Component->SetProperty(m_PropertyName, val); } + + // Specialization for string literals + template + void operator=(const char(&val)[N]) { m_Component->SetProperty(m_PropertyName, val); } + }; + SubscriptProxy operator[](std::string propertyName) { return SubscriptProxy(this, propertyName); } +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h new file mode 100644 index 00000000..42ed7957 --- /dev/null +++ b/include/Engine/Core/ComponentInfo.h @@ -0,0 +1,15 @@ +#include "../Common.h" + +struct ComponentInfo +{ + struct Meta_t + { + std::string Annotation; + int Allocation = 0; + }; + + std::string Name; + std::unordered_map FieldTypes; + std::unordered_map FieldOffsets; + Meta_t Meta; +}; \ No newline at end of file diff --git a/include/Engine/Core/ComponentPool.h b/include/Engine/Core/ComponentPool.h new file mode 100644 index 00000000..d3f108dc --- /dev/null +++ b/include/Engine/Core/ComponentPool.h @@ -0,0 +1,7 @@ +class ComponentPool +{ +public: + ComponentPool() { } + + +}; \ No newline at end of file diff --git a/include/Engine/Core/Entity.h b/include/Engine/Core/Entity.h new file mode 100644 index 00000000..111ea117 --- /dev/null +++ b/include/Engine/Core/Entity.h @@ -0,0 +1,12 @@ +#ifndef Entity_h__ +#define Entity_h__ + +typedef unsigned int EntityID; + +struct Entity +{ + unsigned int EntityID = 0; + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityParser.h b/include/Engine/Core/EntityParser.h new file mode 100644 index 00000000..cfce9bc9 --- /dev/null +++ b/include/Engine/Core/EntityParser.h @@ -0,0 +1,479 @@ +#include "../Common.h" +#include +#include "../GLM.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Entity.h" +#include "Component.h" + +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; +}; + +struct ComponentPool +{ + std::string ComponentName; + unsigned int Size = 0; + unsigned int Stride = 0; + ComponentInfo Info; + char* Data = nullptr; + + // TODO: Iterators + Component at(unsigned int index) + { + // TODO: EntityID + return Component(0, &Info, Data + (index*Stride)); + } +}; + +class EntityParser +{ +public: + EntityParser(std::string entityFile) + : m_EntityFile(entityFile) + { + 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); + } + + ~EntityParser() + { + 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 Preprocess(boost::filesystem::path inPath, boost::filesystem::path 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.string().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.string().c_str()); + // TODO: MemBufFormatTarget* formatTarget = new MemBufFormatTarget() + output->setByteStream(formatTarget); + writer->write(doc, output); + + delete formatTarget; + output->release(); + writer->release(); + parser->release(); + } + void Parse() + { + // HACK: Use Sax2 parser instead so the whole 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 + AllocateComponentStore(); + // 4. Parse entity hierarchy + ParseEntityGraph(); + } + +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; +public: + std::map m_ComponentStore; + std::vector m_Entities; +private: + + /* + Preprocess an XML file and output a new one + where xsi:includes are processed, since apparently + Xerces can't handle processing includes before validating schema. + */ + + void 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); + } + + m_ComponentInfo[compInfo.Name] = compInfo; + } + } + + void ParseDefaults() + { + + } + + void AllocateComponentStore() + { + 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 + unsigned int stride = 0; + // Reserve space for Entity pointer + stride += sizeof(Entity*); + std::cout << " Entity " << " (" << sizeof(Entity*) << " byte)" << std::endl; + // 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: " << stride << std::endl; + + 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 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; + } + } + + size_t 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; + } +}; + +unsigned int EntityParser::InstanceCount = 0; \ No newline at end of file diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h new file mode 100644 index 00000000..7a349d24 --- /dev/null +++ b/include/Engine/Core/World.h @@ -0,0 +1,11 @@ + +class World +{ +public: + World() + { + + } + +private: +}; \ No newline at end of file From 8aa72735e7fdf6d19a2d21067cb4c5bf47e60720 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Wed, 2 Dec 2015 15:02:18 +0100 Subject: [PATCH 06/10] Fixed Tests running the game when it really shouldn't. --- src/Game/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index b513fa92..41cc30eb 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -12,12 +12,13 @@ include_directories( file(GLOB SOURCE_FILES "${INCLUDE_PATH}/*.h" - "*.cpp" + #"*.cpp" ) #source_group(Core FILES ${SOURCE_FILES}) set(SOURCE_FILES ${SOURCE_FILES} + "Game.cpp" ) set(LIBRARIES From b3e68c6c34118b9e22181141f9283382c52bee04 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Wed, 2 Dec 2015 15:07:48 +0100 Subject: [PATCH 07/10] Implemented ComponentPool and initial tests for it --- include/Engine/Core/ComponentInfo.h | 11 +- include/Engine/Core/ComponentPool.h | 122 +++++++++++++++++- .../Core/{Component.h => ComponentWrapper.h} | 19 +-- .../Core/{EntityParser.h => EntityFactory.h} | 15 ++- include/Engine/Core/EntityFile.h | 12 ++ include/Engine/Core/MemoryPool.h | 1 + include/Engine/Core/World.h | 24 +++- src/Tests/ComponentPoolTest.cpp | 29 +++++ 8 files changed, 212 insertions(+), 21 deletions(-) rename include/Engine/Core/{Component.h => ComponentWrapper.h} (77%) rename include/Engine/Core/{EntityParser.h => EntityFactory.h} (98%) create mode 100644 include/Engine/Core/EntityFile.h create mode 100644 src/Tests/ComponentPoolTest.cpp diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h index 42ed7957..6453bf32 100644 --- a/include/Engine/Core/ComponentInfo.h +++ b/include/Engine/Core/ComponentInfo.h @@ -1,3 +1,6 @@ +#ifndef ComponentInfo_h__ +#define ComponentInfo_h__ + #include "../Common.h" struct ComponentInfo @@ -5,11 +8,15 @@ struct ComponentInfo struct Meta_t { std::string Annotation; - int Allocation = 0; + unsigned int Allocation = 0; + unsigned int Stride = 0; }; std::string Name; std::unordered_map FieldTypes; std::unordered_map FieldOffsets; Meta_t Meta; -}; \ No newline at end of file + std::shared_ptr Defaults = nullptr; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/ComponentPool.h b/include/Engine/Core/ComponentPool.h index d3f108dc..fe1c00db 100644 --- a/include/Engine/Core/ComponentPool.h +++ b/include/Engine/Core/ComponentPool.h @@ -1,7 +1,125 @@ +#ifndef ComponentPool_h__ +#define ComponentPool_h__ + +#include "MemoryPool.h" +#include "ComponentInfo.h" +#include "ComponentWrapper.h" + +class ComponentPoolForwardIterator + : public std::iterator +{ +public: + ComponentPoolForwardIterator(const ComponentInfo& componentInfo, const MemoryPool::iterator begin, const MemoryPool::iterator end) + : m_ComponentInfo(componentInfo) + , m_MemoryPoolIterator(begin) + , m_MemoryPoolEnd(end) + { } + + ComponentPoolForwardIterator(const ComponentPoolForwardIterator& other) = default; + ComponentPoolForwardIterator(ComponentPoolForwardIterator&& other) = default; + ~ComponentPoolForwardIterator() = default; + ComponentPoolForwardIterator& operator= (const ComponentPoolForwardIterator& other) = default; + + ComponentPoolForwardIterator& operator++() + { + ++m_MemoryPoolIterator; + return *this; + } + + ComponentPoolForwardIterator& operator++(int) + { + ComponentPoolForwardIterator copyIter(*this); + operator++(); + return copyIter; + } + + bool operator!= (const ComponentPoolForwardIterator& other) const + { + return m_MemoryPoolIterator != other.m_MemoryPoolIterator; + } + + bool operator== (const ComponentPoolForwardIterator& other) const + { + return m_MemoryPoolIterator == other.m_MemoryPoolIterator; + } + + ComponentWrapper operator* () const + { + char* data = &(*m_MemoryPoolIterator); + ComponentWrapper wrapper(m_ComponentInfo, data); + return wrapper; + } + + //ComponentWrapper* operator-> () const + //{ + // return &(*m_MemoryPoolIterator); + //} + +private: + const ComponentInfo& m_ComponentInfo; + MemoryPool::iterator m_MemoryPoolIterator; + const MemoryPool::iterator m_MemoryPoolEnd; +}; + class ComponentPool { public: - ComponentPool() { } + typedef ComponentPoolForwardIterator iterator; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef ComponentWrapper value_type; + typedef ComponentWrapper* pointer; + typedef ComponentWrapper& reference; + ComponentPool(const ComponentInfo& ci) + : m_ComponentInfo(ci) + , m_Pool(ci.Meta.Allocation, ci.Meta.Stride) + { } -}; \ No newline at end of file + ComponentPool(const ComponentPool& other) = delete; + ComponentPool(const ComponentPool&& other) = delete; + + ComponentWrapper New() + { + char* data = m_Pool.Allocate(); + return ComponentWrapper(m_ComponentInfo, data); + } + + void Delete(ComponentWrapper& component) + { + m_Pool.Free(component.Data); + } + + iterator begin() const + { + return iterator(m_ComponentInfo, m_Pool.begin(), m_Pool.end()); + } + + iterator end() const + { + return iterator(m_ComponentInfo, m_Pool.end(), m_Pool.end()); + } + + //Dumps information about what the pool memory looks like right now + //into an output stream (e.g. file/std::cout, anything that has an operator<<) + //Interpret the data in the memory as InterpretType. + template + void Dump(OutStream& out) const + { + m_Pool.Dump(out); + } + + //Dumps information about what the pool memory looks like right now + //into std::cout. Interpret the data in the memory as InterpretType. + template + void Dump() const + { + m_Pool.Dump(); + } + +private: + const ComponentInfo m_ComponentInfo; + MemoryPool m_Pool; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/Component.h b/include/Engine/Core/ComponentWrapper.h similarity index 77% rename from include/Engine/Core/Component.h rename to include/Engine/Core/ComponentWrapper.h index 3093f35f..3694336b 100644 --- a/include/Engine/Core/Component.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -2,24 +2,25 @@ #define Component_h__ #include "../Common.h" +#include "Entity.h" #include "ComponentInfo.h" -struct Component +struct ComponentWrapper { - Component(const unsigned int entityID, const ComponentInfo* componentInfo, char* data) - : EntityID(entityID) + ComponentWrapper(const ComponentInfo& componentInfo, char* data) + : EntityID(*reinterpret_cast<::EntityID*>(data)) , Info(componentInfo) , Data(data) { } - const unsigned int EntityID; - const ComponentInfo* Info; + ::EntityID& EntityID; + const ComponentInfo& Info; char* Data; template T& Property(std::string name) { - unsigned int offset=Info->FieldOffsets.at(name); + unsigned int offset = Info.FieldOffsets.at(name); return *reinterpret_cast(&Data[offset]); } @@ -34,14 +35,14 @@ struct Component struct SubscriptProxy { - friend struct Component; + friend struct ComponentWrapper; private: - SubscriptProxy(Component* component, std::string& propertyName) + SubscriptProxy(ComponentWrapper* component, std::string& propertyName) : m_Component(component) , m_PropertyName(propertyName) { } - Component* m_Component; + ComponentWrapper* m_Component; std::string& m_PropertyName; public: diff --git a/include/Engine/Core/EntityParser.h b/include/Engine/Core/EntityFactory.h similarity index 98% rename from include/Engine/Core/EntityParser.h rename to include/Engine/Core/EntityFactory.h index cfce9bc9..39a9ab02 100644 --- a/include/Engine/Core/EntityParser.h +++ b/include/Engine/Core/EntityFactory.h @@ -28,7 +28,7 @@ #include #include "Entity.h" -#include "Component.h" +#include "ComponentWrapper.h" class EntityPreprocessorXMLErrorHandler : public xercesc::DOMErrorHandler { @@ -111,17 +111,17 @@ struct ComponentPool char* Data = nullptr; // TODO: Iterators - Component at(unsigned int index) + ComponentWrapper at(unsigned int index) { // TODO: EntityID - return Component(0, &Info, Data + (index*Stride)); + return ComponentWrapper(0, &Info, Data + (index*Stride)); } }; -class EntityParser +class EntityFactory { public: - EntityParser(std::string entityFile) + EntityFactory(std::string entityFile) : m_EntityFile(entityFile) { using namespace xercesc; @@ -147,7 +147,7 @@ public: m_DOMParser->cacheGrammarFromParse(true); } - ~EntityParser() + ~EntityFactory() { using namespace xercesc; @@ -344,6 +344,7 @@ private: fieldOffset += getTypeStride(type); } + compInfo.Meta.Stride = fieldOffset; m_ComponentInfo[compInfo.Name] = compInfo; } } @@ -476,4 +477,4 @@ private: } }; -unsigned int EntityParser::InstanceCount = 0; \ No newline at end of file +unsigned int EntityFactory::InstanceCount = 0; \ No newline at end of file diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h new file mode 100644 index 00000000..7fa423d6 --- /dev/null +++ b/include/Engine/Core/EntityFile.h @@ -0,0 +1,12 @@ +#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/MemoryPool.h b/include/Engine/Core/MemoryPool.h index 8fba0845..f0579ded 100644 --- a/include/Engine/Core/MemoryPool.h +++ b/include/Engine/Core/MemoryPool.h @@ -32,6 +32,7 @@ public: typedef T value_type; typedef T* pointer; typedef T& reference; + MemoryPool() : m_StartAddress(nullptr) , m_SlotIsAllocated() diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 7a349d24..0eac4550 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -1,3 +1,9 @@ +#ifndef World_h__ +#define World_h__ + +#include "../Common.h" +#include "Entity.h" +#include "ComponentPool.h" class World { @@ -7,5 +13,21 @@ public: } + ~World() + { + // foreach (m_ComponentPools... + } + + void AllocateComponentPool(ComponentInfo& ci) + { + auto pool = new ComponentPool(ci); + m_ComponentPools[ci.Name] = pool; + } + + void AddComponent(EntityID entity, std::string componentType); + private: -}; \ No newline at end of file + std::unordered_map m_ComponentPools; +}; + +#endif \ No newline at end of file diff --git a/src/Tests/ComponentPoolTest.cpp b/src/Tests/ComponentPoolTest.cpp new file mode 100644 index 00000000..b4fca331 --- /dev/null +++ b/src/Tests/ComponentPoolTest.cpp @@ -0,0 +1,29 @@ +#include + +#include "Core/ComponentPool.h" + +BOOST_AUTO_TEST_CASE(ComponentPoolTest) +{ + ComponentInfo ci; + ci.Name = "Test"; + ci.FieldTypes["Field"] = "int"; + ci.FieldOffsets["Field"] = sizeof(EntityID); + ci.Meta.Allocation = 4; + ci.Meta.Stride = sizeof(EntityID) + sizeof(int); + + ComponentPool pool(ci); + for (int i = 0; i < 3; i++) { + ComponentWrapper c = pool.New(); + c.EntityID = i; + unsigned int offset = c.Info.FieldOffsets.at("Field"); + memcpy(&c.Data[offset], &i, sizeof(int)); + } + + int i = 0; + for (auto& c : pool) { + BOOST_CHECK(c.EntityID == i); + int field = c.Property("Field"); + BOOST_CHECK(field == i); + i++; + } +} \ No newline at end of file From c7430a9338c96c081aad0e27714d0bde69bed8e5 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Wed, 2 Dec 2015 15:37:20 +0100 Subject: [PATCH 08/10] Fixed ComponentWrapper string scope error --- include/Engine/Core/ComponentWrapper.h | 4 ++-- src/Tests/ComponentPoolTest.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 3694336b..5efef105 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -37,13 +37,13 @@ struct ComponentWrapper { friend struct ComponentWrapper; private: - SubscriptProxy(ComponentWrapper* component, std::string& propertyName) + SubscriptProxy(ComponentWrapper* component, std::string propertyName) : m_Component(component) , m_PropertyName(propertyName) { } ComponentWrapper* m_Component; - std::string& m_PropertyName; + std::string m_PropertyName; public: template diff --git a/src/Tests/ComponentPoolTest.cpp b/src/Tests/ComponentPoolTest.cpp index b4fca331..0656d9aa 100644 --- a/src/Tests/ComponentPoolTest.cpp +++ b/src/Tests/ComponentPoolTest.cpp @@ -22,8 +22,7 @@ BOOST_AUTO_TEST_CASE(ComponentPoolTest) int i = 0; for (auto& c : pool) { BOOST_CHECK(c.EntityID == i); - int field = c.Property("Field"); - BOOST_CHECK(field == i); + BOOST_CHECK((int)c["Field"] == i); i++; } } \ No newline at end of file From d3d2e594fefe1846ce6278639b4e32b43e60da8d Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 3 Dec 2015 15:07:35 +0100 Subject: [PATCH 09/10] Basic and working entity system with a really ugly test case --- include/Engine/Core/ComponentInfo.h | 11 ++- include/Engine/Core/ComponentPool.h | 93 +++++++------------------- include/Engine/Core/ComponentWrapper.h | 29 ++++---- include/Engine/Core/Entity.h | 12 ---- include/Engine/Core/EntityFactory.h | 8 +-- include/Engine/Core/EntityWrapper.h | 15 +++++ include/Engine/Core/World.h | 35 +++++----- src/Engine/Core/ComponentPool.cpp | 79 ++++++++++++++++++++++ src/Engine/Core/World.cpp | 54 +++++++++++++++ src/Tests/ComponentPoolTest.cpp | 44 ++++++------ src/Tests/WorldTest.cpp | 41 ++++++++++++ 11 files changed, 286 insertions(+), 135 deletions(-) delete mode 100644 include/Engine/Core/Entity.h create mode 100644 include/Engine/Core/EntityWrapper.h create mode 100644 src/Engine/Core/ComponentPool.cpp create mode 100644 src/Engine/Core/World.cpp create mode 100644 src/Tests/WorldTest.cpp diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h index 6453bf32..ee5f1a11 100644 --- a/include/Engine/Core/ComponentInfo.h +++ b/include/Engine/Core/ComponentInfo.h @@ -16,7 +16,16 @@ struct ComponentInfo std::unordered_map FieldTypes; std::unordered_map FieldOffsets; Meta_t Meta; - std::shared_ptr Defaults = nullptr; + std::shared_ptr Defaults = nullptr; +}; + +template<> +struct std::hash +{ + inline std::size_t operator()(const ComponentInfo& v) const + { + return std::hash()(v.Name); + } }; #endif \ No newline at end of file diff --git a/include/Engine/Core/ComponentPool.h b/include/Engine/Core/ComponentPool.h index fe1c00db..f0d53864 100644 --- a/include/Engine/Core/ComponentPool.h +++ b/include/Engine/Core/ComponentPool.h @@ -9,51 +9,21 @@ class ComponentPoolForwardIterator : public std::iterator { public: - ComponentPoolForwardIterator(const ComponentInfo& componentInfo, const MemoryPool::iterator begin, const MemoryPool::iterator end) - : m_ComponentInfo(componentInfo) + ComponentPoolForwardIterator(const ComponentInfo& componentInfo, const MemoryPool::iterator begin, const MemoryPool::iterator end) + : m_ComponentInfo(componentInfo) , m_MemoryPoolIterator(begin) , m_MemoryPoolEnd(end) - { } + { } ComponentPoolForwardIterator(const ComponentPoolForwardIterator& other) = default; ComponentPoolForwardIterator(ComponentPoolForwardIterator&& other) = default; ~ComponentPoolForwardIterator() = default; - ComponentPoolForwardIterator& operator= (const ComponentPoolForwardIterator& other) = default; - - ComponentPoolForwardIterator& operator++() - { - ++m_MemoryPoolIterator; - return *this; - } - - ComponentPoolForwardIterator& operator++(int) - { - ComponentPoolForwardIterator copyIter(*this); - operator++(); - return copyIter; - } - - bool operator!= (const ComponentPoolForwardIterator& other) const - { - return m_MemoryPoolIterator != other.m_MemoryPoolIterator; - } - - bool operator== (const ComponentPoolForwardIterator& other) const - { - return m_MemoryPoolIterator == other.m_MemoryPoolIterator; - } - - ComponentWrapper operator* () const - { - char* data = &(*m_MemoryPoolIterator); - ComponentWrapper wrapper(m_ComponentInfo, data); - return wrapper; - } - - //ComponentWrapper* operator-> () const - //{ - // return &(*m_MemoryPoolIterator); - //} + ComponentPoolForwardIterator& operator=(const ComponentPoolForwardIterator& other) = default; + ComponentPoolForwardIterator& operator++(); + ComponentPoolForwardIterator& operator++(int); + bool operator!=(const ComponentPoolForwardIterator& other) const; + bool operator==(const ComponentPoolForwardIterator& other) const; + ComponentWrapper operator*() const; private: const ComponentInfo& m_ComponentInfo; @@ -71,55 +41,40 @@ public: typedef ComponentWrapper* pointer; typedef ComponentWrapper& reference; - ComponentPool(const ComponentInfo& ci) + ComponentPool(const ::ComponentInfo& ci) : m_ComponentInfo(ci) - , m_Pool(ci.Meta.Allocation, ci.Meta.Stride) + , m_Pool(ci.Meta.Allocation, sizeof(EntityID) + ci.Meta.Stride) { } - ComponentPool(const ComponentPool& other) = delete; ComponentPool(const ComponentPool&& other) = delete; - ComponentWrapper New() - { - char* data = m_Pool.Allocate(); - return ComponentWrapper(m_ComponentInfo, data); - } + const ::ComponentInfo& ComponentInfo() const { return m_ComponentInfo; } - void Delete(ComponentWrapper& component) - { - m_Pool.Free(component.Data); - } + // Allocate space for a component and store which entity it belongs to in internal structure + ComponentWrapper Allocate(EntityID entity); + // Get the component belonging to a specific entity + ComponentWrapper GetByEntity(EntityID ent); + // Delete a component and free its memory + void Delete(ComponentWrapper& wrapper); - iterator begin() const - { - return iterator(m_ComponentInfo, m_Pool.begin(), m_Pool.end()); - } - - iterator end() const - { - return iterator(m_ComponentInfo, m_Pool.end(), m_Pool.end()); - } + iterator begin() const; + iterator end() const; //Dumps information about what the pool memory looks like right now //into an output stream (e.g. file/std::cout, anything that has an operator<<) //Interpret the data in the memory as InterpretType. template - void Dump(OutStream& out) const - { - m_Pool.Dump(out); - } + void Dump(OutStream& out) const; //Dumps information about what the pool memory looks like right now //into std::cout. Interpret the data in the memory as InterpretType. template - void Dump() const - { - m_Pool.Dump(); - } + void Dump() const; private: - const ComponentInfo m_ComponentInfo; + ::ComponentInfo m_ComponentInfo; MemoryPool m_Pool; + std::unordered_map m_EntityToComponent; }; #endif \ No newline at end of file diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 5efef105..7b388267 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -1,20 +1,20 @@ -#ifndef Component_h__ -#define Component_h__ +#ifndef ComponentWrapper_h__ +#define ComponentWrapper_h__ #include "../Common.h" -#include "Entity.h" +#include "EntityWrapper.h" #include "ComponentInfo.h" struct ComponentWrapper { ComponentWrapper(const ComponentInfo& componentInfo, char* data) - : EntityID(*reinterpret_cast<::EntityID*>(data)) - , Info(componentInfo) - , Data(data) + : Info(componentInfo) + , EntityID(*reinterpret_cast<::EntityID*>(data)) + , Data(data + sizeof(EntityID)) { } - ::EntityID& EntityID; const ComponentInfo& Info; + const ::EntityID EntityID; char* Data; template @@ -25,13 +25,13 @@ struct ComponentWrapper } template - void SetProperty(std::string name, T& value) { Property(name)=value; } - template - void SetProperty(std::string name, const T value) { Property(name)=value; } + void SetProperty(std::string name, const T value) { Property(name) = value; } + //template + //void SetProperty(std::string name, T& value) { Property(name) = value; } // Specialization for string literals template - void SetProperty(std::string name, const char(&value)[N]) { Property(name)=std::string(value); } + void SetProperty(std::string name, const char(&value)[N]) { Property(name) = std::string(value); } struct SubscriptProxy { @@ -51,8 +51,9 @@ struct ComponentWrapper template void operator=(const T val) { m_Component->SetProperty(m_PropertyName, val); } - template - void operator=(T& val) { m_Component->SetProperty(m_PropertyName, val); } + // TODO: Pass by reference and rvalue (universal reference?) + //template + //void operator=(T& val) { m_Component->SetProperty(m_PropertyName, val); } // Specialization for string literals template @@ -61,4 +62,4 @@ struct ComponentWrapper SubscriptProxy operator[](std::string propertyName) { return SubscriptProxy(this, propertyName); } }; -#endif \ No newline at end of file +#endif diff --git a/include/Engine/Core/Entity.h b/include/Engine/Core/Entity.h deleted file mode 100644 index 111ea117..00000000 --- a/include/Engine/Core/Entity.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef Entity_h__ -#define Entity_h__ - -typedef unsigned int EntityID; - -struct Entity -{ - unsigned int EntityID = 0; - -}; - -#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFactory.h b/include/Engine/Core/EntityFactory.h index 39a9ab02..d1dbce03 100644 --- a/include/Engine/Core/EntityFactory.h +++ b/include/Engine/Core/EntityFactory.h @@ -27,7 +27,7 @@ #include #include -#include "Entity.h" +#include "EntityWrapper.h" #include "ComponentWrapper.h" class EntityPreprocessorXMLErrorHandler : public xercesc::DOMErrorHandler @@ -231,7 +231,7 @@ private: std::map m_ComponentInfo; public: std::map m_ComponentStore; - std::vector m_Entities; + std::vector m_Entities; private: /* @@ -380,8 +380,8 @@ private: // Calculate component size unsigned int stride = 0; // Reserve space for Entity pointer - stride += sizeof(Entity*); - std::cout << " Entity " << " (" << sizeof(Entity*) << " byte)" << std::endl; + stride += sizeof(EntityWrapper*); + std::cout << " Entity " << " (" << sizeof(EntityWrapper*) << " byte)" << std::endl; // Add size of fields for (auto& field : ci.FieldTypes) { std::cout << " " << field.second << " " << field.first << " (" << getTypeStride(field.second) << " byte)" << std::endl; diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h new file mode 100644 index 00000000..d5e723ca --- /dev/null +++ b/include/Engine/Core/EntityWrapper.h @@ -0,0 +1,15 @@ +#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/World.h b/include/Engine/Core/World.h index 0eac4550..20bbe288 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -2,32 +2,35 @@ #define World_h__ #include "../Common.h" -#include "Entity.h" +#include "EntityWrapper.h" +#include "ObjectPool.h" #include "ComponentPool.h" class World { public: - World() - { + World() = default; + ~World(); - } + EntityID CreateEntity(EntityID parent = 0); - ~World() - { - // foreach (m_ComponentPools... - } - - void AllocateComponentPool(ComponentInfo& ci) - { - auto pool = new ComponentPool(ci); - m_ComponentPools[ci.Name] = pool; - } - - void AddComponent(EntityID entity, std::string componentType); + // 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 + ComponentWrapper AttachComponent(EntityID entity, std::string componentType); + // Get a component of an entity + ComponentWrapper GetComponent(EntityID entity, std::string componentType); + // Get all components of the specified type + const ComponentPool& GetComponents(std::string componentType); private: + EntityID m_CurrentEntityID = 0; + + std::unordered_map m_EntityParents; + std::unordered_multimap m_EntityChildren; std::unordered_map m_ComponentPools; + + EntityID generateEntityID(); }; #endif \ No newline at end of file diff --git a/src/Engine/Core/ComponentPool.cpp b/src/Engine/Core/ComponentPool.cpp new file mode 100644 index 00000000..146b33f6 --- /dev/null +++ b/src/Engine/Core/ComponentPool.cpp @@ -0,0 +1,79 @@ +#include "Core/ComponentPool.h" + + + +ComponentWrapper ComponentPoolForwardIterator::operator*() const +{ + char* data = &(*m_MemoryPoolIterator); + ComponentWrapper wrapper(m_ComponentInfo, data); + return wrapper; +} + +bool ComponentPoolForwardIterator::operator==(const ComponentPoolForwardIterator& other) const +{ + return m_MemoryPoolIterator == other.m_MemoryPoolIterator; +} + +bool ComponentPoolForwardIterator::operator!=(const ComponentPoolForwardIterator& other) const +{ + return m_MemoryPoolIterator != other.m_MemoryPoolIterator; +} + +ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++(int) +{ + ComponentPoolForwardIterator copyIter(*this); + operator++(); + return copyIter; +} + +ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++() +{ + ++m_MemoryPoolIterator; + return *this; +} + +//const ::ComponentInfo& ComponentPool::ComponentInfo() const +//{ +// return m_ComponentInfo; +//} + +ComponentWrapper ComponentPool::Allocate(EntityID entity) +{ + char* data = m_Pool.Allocate(); + memcpy(data, &entity, sizeof(EntityID)); + m_EntityToComponent[entity] = data; + return ComponentWrapper(m_ComponentInfo, data); +} + +ComponentWrapper ComponentPool::GetByEntity(EntityID ent) +{ + return ComponentWrapper(m_ComponentInfo, m_EntityToComponent.at(ent)); +} + +void ComponentPool::Delete(ComponentWrapper& wrapper) +{ + m_EntityToComponent.erase(wrapper.EntityID); + m_Pool.Free(wrapper.Data); +} + +ComponentPool::iterator ComponentPool::begin() const +{ + return iterator(m_ComponentInfo, m_Pool.begin(), m_Pool.end()); +} + +ComponentPool::iterator ComponentPool::end() const +{ + return iterator(m_ComponentInfo, m_Pool.end(), m_Pool.end()); +} + +template +void ComponentPool::Dump() const +{ + m_Pool.Dump(); +} + +template +void ComponentPool::Dump(OutStream& out) const +{ + m_Pool.Dump(out); +} diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp new file mode 100644 index 00000000..26d3f1e4 --- /dev/null +++ b/src/Engine/Core/World.cpp @@ -0,0 +1,54 @@ +#include "Core/World.h" + +World::~World() +{ + for (auto& pool : m_ComponentPools) { + delete pool.second; + } +} + +EntityID World::CreateEntity(EntityID parent /*= 0*/) +{ + EntityID newEntity = generateEntityID(); + m_EntityParents[newEntity] = parent; + if (parent != 0) { + m_EntityChildren.insert(std::make_pair(parent, newEntity)); + } + return newEntity; +} + +void World::RegisterComponent(ComponentInfo& ci) +{ + m_ComponentPools[ci.Name] = new ComponentPool(ci); +} + +ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType) +{ + ComponentPool* pool = m_ComponentPools.at(componentType); + const ComponentInfo& ci = pool->ComponentInfo(); + + // Allocate space for the component + ComponentWrapper c = pool->Allocate(entity); + // Write default values + memcpy(c.Data, ci.Defaults.get(), ci.Meta.Stride); + + return c; +} + +ComponentWrapper World::GetComponent(EntityID entity, std::string componentType) +{ + ComponentPool* pool = m_ComponentPools.at(componentType); + return pool->GetByEntity(entity); +} + +const ComponentPool& World::GetComponents(std::string componentType) +{ + return *m_ComponentPools.at(componentType); +} + +EntityID World::generateEntityID() +{ + // TODO: Make EntityID generation smarter + return m_CurrentEntityID++; +} + diff --git a/src/Tests/ComponentPoolTest.cpp b/src/Tests/ComponentPoolTest.cpp index 0656d9aa..25bdf1d3 100644 --- a/src/Tests/ComponentPoolTest.cpp +++ b/src/Tests/ComponentPoolTest.cpp @@ -4,25 +4,31 @@ BOOST_AUTO_TEST_CASE(ComponentPoolTest) { - ComponentInfo ci; - ci.Name = "Test"; - ci.FieldTypes["Field"] = "int"; - ci.FieldOffsets["Field"] = sizeof(EntityID); - ci.Meta.Allocation = 4; - ci.Meta.Stride = sizeof(EntityID) + sizeof(int); + // TODO: Write an updated test for component pool + BOOST_CHECK(false); + //ComponentInfo ci; + //ci.Name = "Test"; + //ci.FieldTypes["Field"] = "int"; + //ci.FieldOffsets["Field"] = 0; + //ci.Meta.Allocation = 3; + //ci.Meta.Stride = sizeof(EntityID) + sizeof(int); - ComponentPool pool(ci); - for (int i = 0; i < 3; i++) { - ComponentWrapper c = pool.New(); - c.EntityID = i; - unsigned int offset = c.Info.FieldOffsets.at("Field"); - memcpy(&c.Data[offset], &i, sizeof(int)); - } + //std::vector wrappers; + //ComponentPool pool(ci); + //for (int i = 0; i < 4; i++) { + // ComponentWrapper c = pool.New(); + // c.EntityID = i; + // unsigned int offset = c.Info.FieldOffsets.at("Field"); + // memcpy(&c.Data[offset], &i, sizeof(int)); + // wrappers.push_back(c); + //} - int i = 0; - for (auto& c : pool) { - BOOST_CHECK(c.EntityID == i); - BOOST_CHECK((int)c["Field"] == i); - i++; - } + //int i = 0; + //for (auto& c : pool) { + // BOOST_CHECK(c.EntityID == i); + // BOOST_CHECK((int)c["Field"] == i); + // i++; + //} + + //pool.Delete(wrappers[1]); } \ No newline at end of file diff --git a/src/Tests/WorldTest.cpp b/src/Tests/WorldTest.cpp new file mode 100644 index 00000000..663c243f --- /dev/null +++ b/src/Tests/WorldTest.cpp @@ -0,0 +1,41 @@ +#include + +#include "Common.h" +#include "Core/World.h" + +BOOST_AUTO_TEST_CASE(WorldTest) +{ + ComponentInfo ci; + ci.Name = "Test"; + ci.FieldTypes["Field"] = "int"; + ci.FieldOffsets["Field"] = 0; + ci.Meta.Stride = sizeof(int); + ci.Meta.Allocation = 3; + + ci.Defaults = std::shared_ptr(new char[ci.Meta.Stride]); + int default_Field = 1337; + memcpy(ci.Defaults.get(), &default_Field, ci.Meta.Stride); + + World w; + w.RegisterComponent(ci); + + std::vector ids; + for (int i = 0; i < 6; i++) { + EntityID e = w.CreateEntity(); + ids.push_back(e); + w.AttachComponent(e, "Test"); + ComponentWrapper c = w.GetComponent(e, "Test"); + BOOST_CHECK(c.EntityID == e); + BOOST_CHECK((int)c["Field"] == 1337); + c.SetProperty("Field", i); + BOOST_CHECK((int)c["Field"] == i); + } + + int i = 0; + for (auto& c : w.GetComponents("Test")) { + EntityID e = ids.at(i); + BOOST_CHECK(c.EntityID == e); + BOOST_CHECK((int)c["Field"] == i); + i++; + } +} From 032b384f3ff2289490c7c465e52c3d955b02e595 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 3 Dec 2015 16:45:03 +0100 Subject: [PATCH 10/10] Created framework for a hardcoded world for TESTING PURPOSES --- include/Game/Game.h | 6 +- include/Game/HardcodedTestWorld.h | 124 ++++++++++++++++++++++++++++++ src/Game/Game.cpp | 4 + 3 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 include/Game/HardcodedTestWorld.h diff --git a/include/Game/Game.h b/include/Game/Game.h index 6a942164..e63e657f 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -3,12 +3,11 @@ #include "Core/ResourceManager.h" #include "Core/ConfigFile.h" - #include "Core/EventBroker.h" #include "Rendering/DummyRenderer.h" #include "Core/InputManager.h" - #include "GUI/Frame.h" +#include "Core/World.h" class Game { @@ -26,6 +25,7 @@ private: IRenderer* m_Renderer; InputManager* m_InputManager; GUI::Frame* m_FrameStack; + World* m_World; }; -#endif \ No newline at end of file +#endif diff --git a/include/Game/HardcodedTestWorld.h b/include/Game/HardcodedTestWorld.h new file mode 100644 index 00000000..47586137 --- /dev/null +++ b/include/Game/HardcodedTestWorld.h @@ -0,0 +1,124 @@ +#include +#include +#include +#include "GLM.h" +#include "Core/World.h" + +struct any +{ + any() { } + + template + any(const T& value) + { + Buffer = std::shared_ptr(new char[sizeof(T)]); + memcpy(Buffer.get(), &value, sizeof(T)); + } + + template + any(T&& value) + { + Buffer = std::shared_ptr(new char[sizeof(T)]); + memcpy(Buffer.get(), &value, sizeof(T)); + } + + template + any& operator=(const T& value) + { + return any(value); + } + + template + any& operator=(T&& value) + { + return any(value); + } + + std::shared_ptr Buffer = nullptr; +}; + +class HardcodedTestWorld : public World +{ +public: + HardcodedTestWorld() + : World() + { + registerTestComponents(); + createTestEntities(); + } + +private: + void registerTestComponents() + { + std::unordered_map>> components + { + { + "Test", + { + std::make_tuple(sizeof(int), "TestInteger", 1337), + std::make_tuple(sizeof(float), "TestFloat", 13.37f) + } + }, + { + "Debug", + { + std::make_tuple(sizeof(std::string), "Name", std::string("Unnamed")), + std::make_tuple(sizeof(glm::vec3), "PickingColor", glm::vec3(0.f)) + } + }, + { + "Transform", + { + std::make_tuple(sizeof(glm::vec3), "Position", glm::vec3(0.f, 0.f, 0.f)), + std::make_tuple(sizeof(glm::quat), "Orientation", glm::quat()), + std::make_tuple(sizeof(glm::vec3), "Scale", glm::vec3(1.f, 1.f, 1.f)) + } + } + }; + + for (auto& c : components) { + ComponentInfo ci; + ci.Name = c.first; + + // Fields + unsigned int stride = 0; + for (auto& f : c.second) { + stride += std::get<0>(f); + } + ci.Meta.Stride = stride; + ci.Defaults = std::shared_ptr(new char[stride]); + unsigned int offset = 0; + for (auto& f : c.second) { + std::size_t size; + std::string fieldName; + any defaultValue; + std::tie(size, fieldName, defaultValue) = f; + + ci.FieldOffsets[fieldName] = offset; + ci.FieldTypes[fieldName] = "undefined"; + memcpy(ci.Defaults.get() + offset, defaultValue.Buffer.get(), size); + offset += size; + } + + RegisterComponent(ci); + } + } + + void createTestEntities() + { + EntityID e = CreateEntity(); + AttachComponent(e, "Test"); + AttachComponent(e, "Debug"); + AttachComponent(e, "Transform"); + ComponentWrapper testComponent = GetComponent(e, "Test"); + int testValue = testComponent["TestInteger"]; + float testFloat = testComponent["TestFloat"]; + ComponentWrapper debugComponent = GetComponent(e, "Debug"); + std::string name = debugComponent["Name"]; + glm::vec3 pickingColor = debugComponent["PickingColor"]; + ComponentWrapper testTransform = GetComponent(e, "Transform"); + glm::vec3 pos = testTransform["Position"]; + glm::quat ori = testTransform["Orientation"]; + glm::vec3 scale = testTransform["Scale"]; + } +}; \ No newline at end of file diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index c710e218..f6421547 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,4 +1,5 @@ #include "Game.h" +#include "HardcodedTestWorld.h" Game::Game(int argc, char* argv[]) { @@ -30,6 +31,9 @@ Game::Game(int argc, char* argv[]) m_FrameStack->Width = m_Renderer->Resolution().Width; m_FrameStack->Height = m_Renderer->Resolution().Height; + // Create a TEST WORLD + m_World = new HardcodedTestWorld(); + m_LastTime = glfwGetTime(); }