Merge remote-tracking branch 'origin/master' into Rendering
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
+1
-1
Submodule deps updated: 65d3f3bc31...1b478d3159
@@ -0,0 +1,31 @@
|
||||
#ifndef ComponentInfo_h__
|
||||
#define ComponentInfo_h__
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
struct ComponentInfo
|
||||
{
|
||||
struct Meta_t
|
||||
{
|
||||
std::string Annotation;
|
||||
unsigned int Allocation = 0;
|
||||
unsigned int Stride = 0;
|
||||
};
|
||||
|
||||
std::string Name;
|
||||
std::unordered_map<std::string, std::string> FieldTypes;
|
||||
std::unordered_map<std::string, unsigned int> FieldOffsets;
|
||||
Meta_t Meta;
|
||||
std::shared_ptr<char> Defaults = nullptr;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<ComponentInfo>
|
||||
{
|
||||
inline std::size_t operator()(const ComponentInfo& v) const
|
||||
{
|
||||
return std::hash<std::string>()(v.Name);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef ComponentPool_h__
|
||||
#define ComponentPool_h__
|
||||
|
||||
#include "MemoryPool.h"
|
||||
#include "ComponentInfo.h"
|
||||
#include "ComponentWrapper.h"
|
||||
|
||||
class ComponentPoolForwardIterator
|
||||
: public std::iterator<std::forward_iterator_tag, ComponentWrapper>
|
||||
{
|
||||
public:
|
||||
ComponentPoolForwardIterator(const ComponentInfo& componentInfo, const MemoryPool<char>::iterator begin, const MemoryPool<char>::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++();
|
||||
ComponentPoolForwardIterator& operator++(int);
|
||||
bool operator!=(const ComponentPoolForwardIterator& other) const;
|
||||
bool operator==(const ComponentPoolForwardIterator& other) const;
|
||||
ComponentWrapper operator*() const;
|
||||
|
||||
private:
|
||||
const ComponentInfo& m_ComponentInfo;
|
||||
MemoryPool<char>::iterator m_MemoryPoolIterator;
|
||||
const MemoryPool<char>::iterator m_MemoryPoolEnd;
|
||||
};
|
||||
|
||||
class ComponentPool
|
||||
{
|
||||
public:
|
||||
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, sizeof(EntityID) + ci.Meta.Stride)
|
||||
{ }
|
||||
ComponentPool(const ComponentPool& other) = delete;
|
||||
ComponentPool(const ComponentPool&& other) = delete;
|
||||
|
||||
const ::ComponentInfo& ComponentInfo() const { return m_ComponentInfo; }
|
||||
|
||||
// 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;
|
||||
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 <typename InterpretType = char, typename OutStream>
|
||||
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 <typename InterpretType = char>
|
||||
void Dump() const;
|
||||
|
||||
private:
|
||||
::ComponentInfo m_ComponentInfo;
|
||||
MemoryPool<char> m_Pool;
|
||||
std::unordered_map<EntityID, char*> m_EntityToComponent;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef ComponentWrapper_h__
|
||||
#define ComponentWrapper_h__
|
||||
|
||||
#include "../Common.h"
|
||||
#include "EntityWrapper.h"
|
||||
#include "ComponentInfo.h"
|
||||
|
||||
struct ComponentWrapper
|
||||
{
|
||||
ComponentWrapper(const ComponentInfo& componentInfo, char* data)
|
||||
: Info(componentInfo)
|
||||
, EntityID(*reinterpret_cast<::EntityID*>(data))
|
||||
, Data(data + sizeof(EntityID))
|
||||
{ }
|
||||
|
||||
const ComponentInfo& Info;
|
||||
const ::EntityID EntityID;
|
||||
char* Data;
|
||||
|
||||
template <typename T>
|
||||
T& Property(std::string name)
|
||||
{
|
||||
unsigned int offset = Info.FieldOffsets.at(name);
|
||||
return *reinterpret_cast<T*>(&Data[offset]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetProperty(std::string name, const T value) { Property<T>(name) = value; }
|
||||
//template <typename T>
|
||||
//void SetProperty(std::string name, T& value) { Property<T>(name) = value; }
|
||||
|
||||
// Specialization for string literals
|
||||
template <std::size_t N>
|
||||
void SetProperty(std::string name, const char(&value)[N]) { Property<std::string>(name) = std::string(value); }
|
||||
|
||||
struct SubscriptProxy
|
||||
{
|
||||
friend struct ComponentWrapper;
|
||||
private:
|
||||
SubscriptProxy(ComponentWrapper* component, std::string propertyName)
|
||||
: m_Component(component)
|
||||
, m_PropertyName(propertyName)
|
||||
{ }
|
||||
|
||||
ComponentWrapper* m_Component;
|
||||
std::string m_PropertyName;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
operator T&() { return m_Component->Property<T>(m_PropertyName); }
|
||||
|
||||
template <typename T>
|
||||
void operator=(const T val) { m_Component->SetProperty<T>(m_PropertyName, val); }
|
||||
// TODO: Pass by reference and rvalue (universal reference?)
|
||||
//template <typename T>
|
||||
//void operator=(T& val) { m_Component->SetProperty<T>(m_PropertyName, val); }
|
||||
|
||||
// Specialization for string literals
|
||||
template<std::size_t N>
|
||||
void operator=(const char(&val)[N]) { m_Component->SetProperty<N>(m_PropertyName, val); }
|
||||
};
|
||||
SubscriptProxy operator[](std::string propertyName) { return SubscriptProxy(this, propertyName); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,480 @@
|
||||
#include "../Common.h"
|
||||
#include <sstream>
|
||||
#include "../GLM.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/dom/DOMLSInput.hpp>
|
||||
#include <xercesc/dom/DOMElement.hpp>
|
||||
#include <xercesc/framework/Wrapper4InputSource.hpp>
|
||||
#include <xercesc/framework/LocalFileInputSource.hpp>
|
||||
#include <xercesc/framework/LocalFileFormatTarget.hpp>
|
||||
#include <xercesc/framework/MemBufFormatTarget.hpp>
|
||||
#include <xercesc/framework/MemBufInputSource.hpp>
|
||||
|
||||
#include <xercesc/sax/HandlerBase.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/util/XMLFloat.hpp>
|
||||
#include <xercesc/framework/XMLGrammarPoolImpl.hpp>
|
||||
#include <xercesc/parsers/XercesDOMParser.hpp>
|
||||
#include <xercesc/framework/psvi/XSElementDeclaration.hpp>
|
||||
#include <xercesc/framework/psvi/XSComplexTypeDefinition.hpp>
|
||||
#include <xercesc/framework/psvi/XSParticle.hpp>
|
||||
#include <xercesc/framework/psvi/XSModelGroup.hpp>
|
||||
#include <xercesc/framework/psvi/XSAnnotation.hpp>
|
||||
#include <xercesc/framework/psvi/XSValue.hpp>
|
||||
#include <xercesc/framework/XMLValidator.hpp>
|
||||
|
||||
#include "EntityWrapper.h"
|
||||
#include "ComponentWrapper.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
|
||||
ComponentWrapper at(unsigned int index)
|
||||
{
|
||||
// TODO: EntityID
|
||||
return ComponentWrapper(0, &Info, Data + (index*Stride));
|
||||
}
|
||||
};
|
||||
|
||||
class EntityFactory
|
||||
{
|
||||
public:
|
||||
EntityFactory(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);
|
||||
}
|
||||
|
||||
~EntityFactory()
|
||||
{
|
||||
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<DOMImplementationLS*>(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<DOMLSInput*>(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<std::string, ComponentInfo> m_ComponentInfo;
|
||||
public:
|
||||
std::map<std::string, ComponentPool> m_ComponentStore;
|
||||
std::vector<EntityWrapper*> 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;
|
||||
// <xs:element name="ComponentName">
|
||||
auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION);
|
||||
for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) {
|
||||
auto element = static_cast<XSElementDeclaration*>(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<const XMLByte*>(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<DOMElement*>(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;
|
||||
}
|
||||
|
||||
// <xs:complexType>
|
||||
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<XSComplexTypeDefinition*>(typeDefinition);
|
||||
|
||||
// <xs:all>
|
||||
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();
|
||||
|
||||
// <xs:element...
|
||||
// <xs:attribute...
|
||||
unsigned int fieldOffset = 0;
|
||||
auto particles = modelGroup->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 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<DOMElement*>(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(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;
|
||||
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<DOMElement*>(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<DOMElement*>(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<char*>(&standardString), getTypeStride(fieldType));
|
||||
} else {
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(field->getTextContent(), dataType, status);
|
||||
memcpy(&data[fieldOffset], reinterpret_cast<char*>(&val->fData.fValue), getTypeStride(fieldType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto entities = m_DOMDocument->getElementsByTagName(XSTR("Entity"));
|
||||
for (int i = 0; i < entities->getLength(); ++i) {
|
||||
auto entity = dynamic_cast<DOMElement*>(entities->item(i));
|
||||
|
||||
|
||||
//entity->setIdAttribute()
|
||||
|
||||
std::cout << "ENTITY " << i + 1 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
size_t getTypeStride(std::string typeName)
|
||||
{
|
||||
std::map<std::string, size_t> 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 EntityFactory::InstanceCount = 0;
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "ResourceManager.h"
|
||||
|
||||
class EntityFile : public Resource
|
||||
{
|
||||
friend class ResourceManager;
|
||||
|
||||
private:
|
||||
EntityFile(std::string path);
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef Entity_h__
|
||||
#define Entity_h__
|
||||
|
||||
typedef unsigned int EntityID;
|
||||
|
||||
struct EntityWrapper
|
||||
{
|
||||
EntityWrapper(EntityID entityID)
|
||||
: ID(entityID)
|
||||
{ }
|
||||
|
||||
EntityID ID;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
|
||||
MemoryPool()
|
||||
: m_StartAddress(nullptr)
|
||||
, m_SlotIsAllocated()
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef World_h__
|
||||
#define World_h__
|
||||
|
||||
#include "../Common.h"
|
||||
#include "EntityWrapper.h"
|
||||
#include "ObjectPool.h"
|
||||
#include "ComponentPool.h"
|
||||
|
||||
class World
|
||||
{
|
||||
public:
|
||||
World() = default;
|
||||
~World();
|
||||
|
||||
EntityID CreateEntity(EntityID parent = 0);
|
||||
|
||||
// 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<EntityID, EntityID> m_EntityParents;
|
||||
std::unordered_multimap<EntityID, EntityID> m_EntityChildren;
|
||||
std::unordered_map<std::string, ComponentPool*> m_ComponentPools;
|
||||
|
||||
EntityID generateEntityID();
|
||||
};
|
||||
|
||||
#endif
|
||||
+3
-3
@@ -3,12 +3,11 @@
|
||||
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "Core/ConfigFile.h"
|
||||
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Rendering/Renderer.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
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
#include <boost/any.hpp>
|
||||
#include "GLM.h"
|
||||
#include "Core/World.h"
|
||||
|
||||
struct any
|
||||
{
|
||||
any() { }
|
||||
|
||||
template <typename T>
|
||||
any(const T& value)
|
||||
{
|
||||
Buffer = std::shared_ptr<char>(new char[sizeof(T)]);
|
||||
memcpy(Buffer.get(), &value, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
any(T&& value)
|
||||
{
|
||||
Buffer = std::shared_ptr<char>(new char[sizeof(T)]);
|
||||
memcpy(Buffer.get(), &value, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
any& operator=(const T& value)
|
||||
{
|
||||
return any(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
any& operator=(T&& value)
|
||||
{
|
||||
return any(value);
|
||||
}
|
||||
|
||||
std::shared_ptr<char> Buffer = nullptr;
|
||||
};
|
||||
|
||||
class HardcodedTestWorld : public World
|
||||
{
|
||||
public:
|
||||
HardcodedTestWorld()
|
||||
: World()
|
||||
{
|
||||
registerTestComponents();
|
||||
createTestEntities();
|
||||
}
|
||||
|
||||
private:
|
||||
void registerTestComponents()
|
||||
{
|
||||
std::unordered_map<std::string, std::list<std::tuple<std::size_t, std::string, any>>> 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<char>(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"];
|
||||
}
|
||||
};
|
||||
@@ -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}
|
||||
)
|
||||
@@ -78,6 +80,7 @@ set(LIBRARIES
|
||||
${Boost_LIBRARIES}
|
||||
${assimp_LIBRARIES}
|
||||
${PNG_LIBRARIES}
|
||||
${Xerces_LIBRARIES}
|
||||
${OPENAL_LIBRARY}
|
||||
${X11_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -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 <typename InterpretType /*= char*/>
|
||||
void ComponentPool::Dump() const
|
||||
{
|
||||
m_Pool.Dump<InterpretType>();
|
||||
}
|
||||
|
||||
template <typename InterpretType /*= char*/, typename OutStream>
|
||||
void ComponentPool::Dump(OutStream& out) const
|
||||
{
|
||||
m_Pool.Dump<InterpretType>(out);
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Game.h"
|
||||
#include "HardcodedTestWorld.h"
|
||||
|
||||
Game::Game(int argc, char* argv[])
|
||||
{
|
||||
@@ -32,6 +33,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "Core/ComponentPool.h"
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ComponentPoolTest)
|
||||
{
|
||||
// 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);
|
||||
|
||||
//std::vector<ComponentWrapper> 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++;
|
||||
//}
|
||||
|
||||
//pool.Delete(wrappers[1]);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#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<char>(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<EntityID> 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++;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user