Merge branch 'master' into AsyncResourceLoad
# Conflicts: # include/Engine/Rendering/RawModel.h # src/Engine/Rendering/RenderQueueFactory.cpp
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#include "Core/EntityFile.h"
|
||||
|
||||
EntityFile::EntityFile(boost::filesystem::path path)
|
||||
: m_FilePath(path)
|
||||
{
|
||||
using namespace xercesc;
|
||||
XMLPlatformUtils::Initialize();
|
||||
m_GrammarPool = new XMLGrammarPoolImpl();
|
||||
m_SAX2XMLReader = XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
|
||||
}
|
||||
|
||||
EntityFile::~EntityFile()
|
||||
{
|
||||
delete m_SAX2XMLReader;
|
||||
delete m_GrammarPool;
|
||||
xercesc::XMLPlatformUtils::Terminate();
|
||||
}
|
||||
|
||||
void EntityFile::Parse(const EntityFileHandler* handler) const
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
EntityFileSAXHandler saxHandler(handler, nullptr);
|
||||
m_SAX2XMLReader->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true);
|
||||
m_SAX2XMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
|
||||
m_SAX2XMLReader->setContentHandler(&saxHandler);
|
||||
m_SAX2XMLReader->setErrorHandler(&saxHandler);
|
||||
m_SAX2XMLReader->setDeclarationHandler(&saxHandler);
|
||||
m_SAX2XMLReader->parse(m_FilePath.string().c_str());
|
||||
}
|
||||
|
||||
std::size_t EntityFile::GetTypeStride(std::string typeName)
|
||||
{
|
||||
std::map<std::string, size_t> typeStrides{
|
||||
{ "bool", sizeof(bool) },
|
||||
{ "int", sizeof(int) },
|
||||
{ "float", sizeof(float) },
|
||||
{ "double", sizeof(double) },
|
||||
{ "string", sizeof(std::string) },
|
||||
{ "Vector", sizeof(glm::vec3) },
|
||||
{ "Quaternion", sizeof(glm::quat) },
|
||||
{ "Color", sizeof(glm::vec4) }
|
||||
};
|
||||
|
||||
auto it = typeStrides.find(typeName);
|
||||
return (it != typeStrides.end()) ? it->second : 0;
|
||||
}
|
||||
|
||||
void EntityFile::WriteAttributeData(char* outData, const ComponentInfo::Field_t& field, const std::map<std::string, std::string>& attributes)
|
||||
{
|
||||
if (field.Type == "Vector") {
|
||||
glm::vec3 vec;
|
||||
vec.x = boost::lexical_cast<float>(attributes.at("X"));
|
||||
vec.y = boost::lexical_cast<float>(attributes.at("Y"));
|
||||
vec.z = boost::lexical_cast<float>(attributes.at("Z"));
|
||||
memcpy(outData, reinterpret_cast<char*>(&vec), field.Stride);
|
||||
} else if (field.Type == "Color") {
|
||||
glm::vec4 vec;
|
||||
vec.r = boost::lexical_cast<float>(attributes.at("R"));
|
||||
vec.g = boost::lexical_cast<float>(attributes.at("G"));
|
||||
vec.b = boost::lexical_cast<float>(attributes.at("B"));
|
||||
vec.a = boost::lexical_cast<float>(attributes.at("A"));
|
||||
memcpy(outData, reinterpret_cast<char*>(&vec), field.Stride);
|
||||
} else if (field.Type == "Quaternion") {
|
||||
glm::quat q;
|
||||
q.x = boost::lexical_cast<float>(attributes.at("X"));
|
||||
q.y = boost::lexical_cast<float>(attributes.at("Y"));
|
||||
q.z = boost::lexical_cast<float>(attributes.at("Z"));
|
||||
q.w = boost::lexical_cast<float>(attributes.at("W"));
|
||||
memcpy(outData, reinterpret_cast<char*>(&q), field.Stride);
|
||||
} else if (!attributes.empty()) {
|
||||
LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size());
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFile::WriteValueData(char* outData, const ComponentInfo::Field_t& field, const char* valueData)
|
||||
{
|
||||
if (field.Type == "int") {
|
||||
int value = boost::lexical_cast<int>(valueData);
|
||||
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
|
||||
} else if (field.Type == "float") {
|
||||
float value = boost::lexical_cast<float>(valueData);
|
||||
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
|
||||
} else if (field.Type == "double") {
|
||||
double value = boost::lexical_cast<double>(valueData);
|
||||
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
|
||||
} else if (field.Type == "bool") {
|
||||
bool value = (valueData[0] == 't'); // Lazy bool evaluation
|
||||
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
|
||||
} else if (field.Type == "string") {
|
||||
new (outData) std::string(valueData);
|
||||
} else {
|
||||
LOG_WARNING("Unknown value data type: %s", field.Type.c_str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "Core/EntityFileParser.h"
|
||||
|
||||
EntityFileParser::EntityFileParser(const EntityFile* entityFile)
|
||||
: m_EntityFile(entityFile)
|
||||
{
|
||||
m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_Handler.SetStartComponentCallback(std::bind(&EntityFileParser::onStartComponent, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_Handler.SetStartFieldCallback(std::bind(&EntityFileParser::onStartComponentField, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
|
||||
void EntityFileParser::MergeEntities(World* world)
|
||||
{
|
||||
m_World = world;
|
||||
m_EntityIDMapper[0] = 0;
|
||||
m_EntityFile->Parse(&m_Handler);
|
||||
}
|
||||
|
||||
void EntityFileParser::onStartEntity(EntityID entity, EntityID parent, const std::string& name)
|
||||
{
|
||||
EntityID realParent = m_EntityIDMapper.at(parent);
|
||||
EntityID realEntity = m_World->CreateEntity(realParent);
|
||||
if (!name.empty()) {
|
||||
m_World->SetName(realEntity, name);
|
||||
}
|
||||
m_EntityIDMapper[entity] = realEntity;
|
||||
LOG_DEBUG("Created entity #%i (%i) with parent %i (%i)", entity, realEntity, parent, realParent);
|
||||
}
|
||||
|
||||
void EntityFileParser::onStartComponent(EntityID entity, const std::string& component)
|
||||
{
|
||||
EntityID realEntity = m_EntityIDMapper.at(entity);
|
||||
m_World->AttachComponent(realEntity, component);
|
||||
LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity);
|
||||
}
|
||||
|
||||
void EntityFileParser::onStartComponentField(EntityID entity, const std::string& componentType, const std::string& fieldName, const std::map<std::string, std::string>& attributes)
|
||||
{
|
||||
EntityID realEntity = m_EntityIDMapper.at(entity);
|
||||
ComponentWrapper component = m_World->GetComponent(realEntity, componentType);
|
||||
auto& field = component.Info.Fields.at(fieldName);
|
||||
|
||||
LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), field.Type.c_str());
|
||||
LOG_DEBUG("Attributes:");
|
||||
for (auto& kv : attributes) {
|
||||
LOG_DEBUG("\t%s = %s", kv.first.c_str(), kv.second.c_str());
|
||||
}
|
||||
|
||||
char* data = component.Data + field.Offset;
|
||||
EntityFile::WriteAttributeData(data, field, attributes);
|
||||
}
|
||||
|
||||
void EntityFileParser::onFieldData(EntityID entity, const std::string& componentType, const std::string& fieldName, const char* fieldData)
|
||||
{
|
||||
EntityID realEntity = m_EntityIDMapper.at(entity);
|
||||
ComponentWrapper component = m_World->GetComponent(realEntity, componentType);
|
||||
auto& field = component.Info.Fields.at(fieldName);
|
||||
|
||||
char* data = component.Data + field.Offset;
|
||||
EntityFile::WriteValueData(data, field, fieldData);
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
#include "Core/EntityFilePreprocessor.h"
|
||||
|
||||
EntityFilePreprocessor::EntityFilePreprocessor(const EntityFile* entityFile)
|
||||
: m_EntityFile(entityFile)
|
||||
{
|
||||
EntityFileHandler handler;
|
||||
handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_EntityFile->Parse(&handler);
|
||||
|
||||
LOG_DEBUG("___ COMPONENT DEFINITIONS ___");
|
||||
for (auto& kv : m_ComponentCounts) {
|
||||
LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second);
|
||||
}
|
||||
|
||||
parseComponentInfo();
|
||||
|
||||
for (auto& kv : m_ComponentInfo) {
|
||||
auto& info = kv.second;
|
||||
LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str());
|
||||
LOG_DEBUG("Stride: %i", info.Meta.Stride);
|
||||
LOG_DEBUG("Allocation: %i", info.Meta.Allocation);
|
||||
for (auto& kv : info.Fields) {
|
||||
auto& field = kv.second;
|
||||
LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type, kv.first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
parseDefaults();
|
||||
}
|
||||
|
||||
void EntityFilePreprocessor::RegisterComponents(World* world)
|
||||
{
|
||||
for (auto& kv : m_ComponentInfo) {
|
||||
world->RegisterComponent(kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFilePreprocessor::onStartComponent(EntityID entity, std::string type)
|
||||
{
|
||||
//LOG_DEBUG("Component: %s", type.c_str());
|
||||
m_ComponentCounts[type]++;
|
||||
}
|
||||
|
||||
void EntityFilePreprocessor::parseComponentInfo()
|
||||
{
|
||||
using namespace xercesc;
|
||||
EntityFileXMLErrorHandler errorHandler;
|
||||
auto grammarPool = m_EntityFile->GrammarPool();
|
||||
bool whateverTheFuckThisIs;
|
||||
auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs);
|
||||
|
||||
// Find component xsd element declarations
|
||||
std::cout << "Enumerating components..." << std::endl;
|
||||
// <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 = XS::ToString(element->getNamespace());
|
||||
if (nameSpace != "components") {
|
||||
continue;
|
||||
}
|
||||
|
||||
ComponentInfo compInfo;
|
||||
|
||||
// Name
|
||||
compInfo.Name = XS::ToString(element->getName());
|
||||
// Known allocation
|
||||
compInfo.Meta.Allocation = m_ComponentCounts[compInfo.Name];
|
||||
// Annotation
|
||||
auto componentAnnotation = element->getAnnotation();
|
||||
if (componentAnnotation != nullptr) {
|
||||
// Parse annotation XML
|
||||
char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString());
|
||||
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool);
|
||||
parser.setErrorHandler(&errorHandler);
|
||||
parser.parse(annotationInput);
|
||||
XMLString::release(&annotationString);
|
||||
auto doc = parser.getDocument();
|
||||
|
||||
// TODO: Add allocation estimations from external file on map-to-map basis
|
||||
// Add allocation estimation(s)
|
||||
//auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation"));
|
||||
//for (int i = 0; i < allocationTags->getLength(); ++i) {
|
||||
// auto allocation = dynamic_cast<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(XS::ToXMLCh("xs:documentation"));
|
||||
if (documentationTags->getLength() != 0) {
|
||||
auto child = documentationTags->item(0)->getFirstChild();
|
||||
if (child != nullptr) {
|
||||
compInfo.Meta.Annotation = XS::ToString(child->getNodeValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING("Component is missing an annotation!");
|
||||
}
|
||||
|
||||
// <xs:complexType>
|
||||
auto typeDefinition = element->getTypeDefinition();
|
||||
if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) {
|
||||
LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping.");
|
||||
continue;
|
||||
}
|
||||
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
|
||||
|
||||
// <xs:all>
|
||||
auto modelGroupParticle = complexTypeDefinition->getParticle();
|
||||
if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
|
||||
LOG_ERROR("Model group particle wasn't TERM_MODELGROUP! Skipping.");
|
||||
continue;
|
||||
}
|
||||
auto modelGroup = modelGroupParticle->getModelGroupTerm();
|
||||
|
||||
// <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) {
|
||||
LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping.");
|
||||
continue;
|
||||
}
|
||||
auto elementDeclaration = particle->getElementTerm();
|
||||
|
||||
std::string name = XS::ToString(elementDeclaration->getName());
|
||||
std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName());
|
||||
|
||||
size_t stride = EntityFile::GetTypeStride(type);
|
||||
if (stride == 0) {
|
||||
std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& field = compInfo.Fields[name];
|
||||
field.Name = name;
|
||||
field.Type = type;
|
||||
field.Offset = fieldOffset;
|
||||
field.Stride = stride;
|
||||
compInfo.FieldsInOrder.push_back(name);
|
||||
|
||||
fieldOffset += stride;
|
||||
}
|
||||
|
||||
compInfo.Meta.Stride = fieldOffset;
|
||||
m_ComponentInfo[compInfo.Name] = compInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFilePreprocessor::parseDefaults()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
EntityFileXMLErrorHandler errorHandler;
|
||||
|
||||
for (auto& ci : m_ComponentInfo) {
|
||||
// Allocate memory for default values
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Meta.Stride]);
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride);
|
||||
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
|
||||
parser.setErrorHandler(&errorHandler);
|
||||
|
||||
std::string componentName = ci.first;
|
||||
LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
|
||||
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
|
||||
|
||||
parser.parse(defaultsFile.string().c_str());
|
||||
auto doc = parser.getDocument();
|
||||
if (doc == nullptr) {
|
||||
LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the node in the components namespace matching the component name
|
||||
std::string tagName = "c:" + componentName;
|
||||
auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName));
|
||||
if (rootNodes->getLength() == 0) {
|
||||
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
|
||||
continue;
|
||||
}
|
||||
auto componentElement = dynamic_cast<DOMElement*>(rootNodes->item(0));
|
||||
|
||||
// Fill the default value buffer with values
|
||||
for (auto& kv : ci.second.Fields) {
|
||||
std::string fieldName = kv.first;
|
||||
auto& field = kv.second;
|
||||
auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName));
|
||||
auto fieldNode = fieldNodes->item(0);
|
||||
if (fieldNode == nullptr) {
|
||||
LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str());
|
||||
continue;
|
||||
}
|
||||
auto fieldElement = dynamic_cast<DOMElement*>(fieldNode);
|
||||
|
||||
char* data = ci.second.Defaults.get() + field.Offset;
|
||||
|
||||
// Handle potential field attributes
|
||||
if (fieldElement->hasAttributes()) {
|
||||
std::map<std::string, std::string> attributes;
|
||||
auto attributeMap = fieldElement->getAttributes();
|
||||
for (int i = 0; i < attributeMap->getLength(); ++i) {
|
||||
auto attribItem = attributeMap->item(i);
|
||||
attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue());
|
||||
}
|
||||
EntityFile::WriteAttributeData(data, field, attributes);
|
||||
}
|
||||
|
||||
// Handle potential field values
|
||||
auto childNode = fieldElement->getFirstChild();
|
||||
if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) {
|
||||
char* cstrValue = XMLString::transcode(childNode->getNodeValue());
|
||||
EntityFile::WriteValueData(data, field, cstrValue);
|
||||
XMLString::release(&cstrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "Core/EntityFileWriter.h"
|
||||
|
||||
#define X(str) XS::ToXMLCh(str)
|
||||
|
||||
void EntityFileWriter::WriteWorld(World* world)
|
||||
{
|
||||
WriteEntity(world, 0);
|
||||
}
|
||||
|
||||
void EntityFileWriter::WriteEntity(World* world, EntityID entity)
|
||||
{
|
||||
using namespace xercesc;
|
||||
DOMDocument* doc = m_DOMImplementation->createDocument(nullptr, X("Entity"), nullptr);
|
||||
DOMElement* root = doc->getDocumentElement();
|
||||
root->setAttribute(X("xmlns:xsi"), X("http://www.w3.org/2001/XMLSchema-instance"));
|
||||
root->setAttribute(X("xsi:noNamespaceSchemaLocation"), X("../Types/Entity.xsd"));
|
||||
root->setAttribute(X("xmlns:c"), X("components"));
|
||||
const std::string& name = world->GetName(entity);
|
||||
if (!name.empty()) {
|
||||
root->setAttribute(X("name"), X(name));
|
||||
}
|
||||
|
||||
DOMElement* componentsElement = doc->createElement(X("Components"));
|
||||
root->appendChild(componentsElement);
|
||||
appentEntityComponents(componentsElement, world, entity);
|
||||
|
||||
DOMElement* childrenElement = doc->createElement(X("Children"));
|
||||
root->appendChild(childrenElement);
|
||||
appendEntityChildren(childrenElement, world, entity);
|
||||
|
||||
try {
|
||||
LocalFileFormatTarget* target = new LocalFileFormatTarget(X(m_FilePath.string()));
|
||||
DOMLSOutput* output = static_cast<DOMImplementationLS*>(m_DOMImplementation)->createLSOutput();
|
||||
output->setByteStream(target);
|
||||
m_DOMLSSerializer->write(doc, output);
|
||||
delete target;
|
||||
} catch (const std::runtime_error& e) {
|
||||
LOG_ERROR("Failed to save \"%s\": %s", m_FilePath.c_str(), e.what());
|
||||
}
|
||||
|
||||
doc->release();
|
||||
}
|
||||
|
||||
void EntityFileWriter::appendEntityChildren(xercesc::DOMElement* parentElement, const World* world, EntityID entity)
|
||||
{
|
||||
using namespace xercesc;
|
||||
DOMDocument* doc = parentElement->getOwnerDocument();
|
||||
|
||||
auto childrenRange = world->GetEntityChildren().equal_range(entity);
|
||||
for (auto it = childrenRange.first; it != childrenRange.second; ++it) {
|
||||
EntityID childEntity = it->second;
|
||||
|
||||
DOMElement* entityElement = doc->createElement(X("Entity"));
|
||||
const std::string& name = world->GetName(childEntity);
|
||||
if (!name.empty()) {
|
||||
entityElement->setAttribute(X("name"), X(name));
|
||||
}
|
||||
parentElement->appendChild(entityElement);
|
||||
|
||||
DOMElement* componentsElement = doc->createElement(X("Components"));
|
||||
entityElement->appendChild(componentsElement);
|
||||
appentEntityComponents(componentsElement, world, childEntity);
|
||||
|
||||
DOMElement* childrenElement = doc->createElement(X("Children"));
|
||||
entityElement->appendChild(childrenElement);
|
||||
appendEntityChildren(childrenElement, world, childEntity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFileWriter::appentEntityComponents(xercesc::DOMElement* parentElement, const World* world, EntityID entity)
|
||||
{
|
||||
using namespace xercesc;
|
||||
DOMDocument* doc = parentElement->getOwnerDocument();
|
||||
auto& componentPools = world->GetComponentPools();
|
||||
|
||||
// Step through all component pools to get an entity's components
|
||||
// HACK: This is sloooow.
|
||||
for (auto& kv : componentPools) {
|
||||
const std::string& componentName = kv.first;
|
||||
if (!world->HasComponent(entity, componentName)) {
|
||||
continue;
|
||||
}
|
||||
std::string qualifiedComponentName = "c:" + componentName;
|
||||
DOMElement* componentElement = doc->createElement(X(qualifiedComponentName));
|
||||
parentElement->appendChild(componentElement);
|
||||
ComponentWrapper c = kv.second->GetByEntity(entity);
|
||||
for (auto& kv : c.Info.Fields) {
|
||||
std::string fieldName = kv.first;
|
||||
auto& field = kv.second;
|
||||
|
||||
// Ignore fields that are equal to the default
|
||||
// HACK: This is probably sloooooow, but it's okay.
|
||||
if (memcmp(c.Data + field.Offset, c.Info.Defaults.get() + field.Offset, field.Stride) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DOMElement* fieldElement = doc->createElement(X(fieldName));
|
||||
componentElement->appendChild(fieldElement);
|
||||
|
||||
if (field.Type == "Vector") {
|
||||
const glm::vec3& vec = c[fieldName];
|
||||
fieldElement->setAttribute(X("X"), X(boost::lexical_cast<std::string>(vec.x)));
|
||||
fieldElement->setAttribute(X("Y"), X(boost::lexical_cast<std::string>(vec.y)));
|
||||
fieldElement->setAttribute(X("Z"), X(boost::lexical_cast<std::string>(vec.z)));
|
||||
} else if (field.Type == "Color") {
|
||||
const glm::vec4& vec = c[fieldName];
|
||||
fieldElement->setAttribute(X("R"), X(boost::lexical_cast<std::string>(vec.r)));
|
||||
fieldElement->setAttribute(X("G"), X(boost::lexical_cast<std::string>(vec.g)));
|
||||
fieldElement->setAttribute(X("B"), X(boost::lexical_cast<std::string>(vec.b)));
|
||||
fieldElement->setAttribute(X("A"), X(boost::lexical_cast<std::string>(vec.a)));
|
||||
} else if (field.Type == "Quaternion") {
|
||||
const glm::quat& q = c[fieldName];
|
||||
fieldElement->setAttribute(X("X"), X(boost::lexical_cast<std::string>(q.x)));
|
||||
fieldElement->setAttribute(X("Y"), X(boost::lexical_cast<std::string>(q.y)));
|
||||
fieldElement->setAttribute(X("Z"), X(boost::lexical_cast<std::string>(q.z)));
|
||||
fieldElement->setAttribute(X("W"), X(boost::lexical_cast<std::string>(q.w)));
|
||||
} else if (field.Type == "int") {
|
||||
const int& value = c[fieldName];
|
||||
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
|
||||
} else if (field.Type == "float") {
|
||||
const float& value = c[fieldName];
|
||||
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
|
||||
} else if (field.Type == "double") {
|
||||
const double& value = c[fieldName];
|
||||
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
|
||||
} else if (field.Type == "bool") {
|
||||
const bool& value = c[fieldName];
|
||||
if (value) {
|
||||
fieldElement->appendChild(doc->createTextNode(X("true")));
|
||||
} else {
|
||||
fieldElement->appendChild(doc->createTextNode(X("false")));
|
||||
}
|
||||
} else if (field.Type == "string") {
|
||||
const std::string& value = c[fieldName];
|
||||
fieldElement->appendChild(doc->createTextNode(X(value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,495 +0,0 @@
|
||||
#include "Core/EntityXMLFile.h"
|
||||
#include "Core/World.h"
|
||||
|
||||
unsigned int EntityXMLFile::InstanceCount = 0;
|
||||
|
||||
EntityXMLFile::EntityXMLFile(std::string path)
|
||||
: m_EntityFile(path)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
if (InstanceCount == 0) {
|
||||
XMLPlatformUtils::Initialize();
|
||||
}
|
||||
InstanceCount++;
|
||||
|
||||
m_GrammarPool = new XMLGrammarPoolImpl();
|
||||
m_ErrorHandler = new EntityParserXMLErrorHandler();
|
||||
m_DOMParser = new XercesDOMParser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
|
||||
m_DOMParser->setErrorHandler(m_ErrorHandler);
|
||||
m_DOMParser->setDoNamespaces(true);
|
||||
m_DOMParser->setDoXInclude(true);
|
||||
m_DOMParser->setDoSchema(true);
|
||||
m_DOMParser->setValidationSchemaFullChecking(true);
|
||||
m_DOMParser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto);
|
||||
m_DOMParser->setValidationSchemaFullChecking(true);
|
||||
m_DOMParser->setValidationConstraintFatal(false);
|
||||
m_DOMParser->setIncludeIgnorableWhitespace(false);
|
||||
// Make sure schema grammar is kept after validation
|
||||
m_DOMParser->cacheGrammarFromParse(true);
|
||||
|
||||
// HACK: Use Sax2 parser instead so the entire DOM doesn't have to reside in memory
|
||||
m_DOMParser->parse(m_EntityFile.c_str());
|
||||
m_DOMDocument = m_DOMParser->getDocument();
|
||||
|
||||
// 1. Fill in ComponentInfo name, fields, default values and metadata from PSVI
|
||||
parseComponentInfo();
|
||||
// 2. Parse default value files for those components
|
||||
parseDefaults();
|
||||
// 3. Allocate component structures
|
||||
predictComponentAllocation();
|
||||
}
|
||||
|
||||
EntityXMLFile::~EntityXMLFile()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
if (m_DOMParser != nullptr) {
|
||||
delete m_DOMParser;
|
||||
}
|
||||
if (m_ErrorHandler != nullptr) {
|
||||
delete m_ErrorHandler;
|
||||
}
|
||||
if (m_GrammarPool != nullptr) {
|
||||
delete m_GrammarPool;
|
||||
}
|
||||
|
||||
InstanceCount--;
|
||||
if (InstanceCount == 0) {
|
||||
XMLPlatformUtils::Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::PopulateWorld(World* world)
|
||||
{
|
||||
for (auto& pair : m_ComponentInfo) {
|
||||
world->RegisterComponent(pair.second);
|
||||
}
|
||||
|
||||
// 4. Parse entity hierarchy
|
||||
auto root = m_DOMDocument->getDocumentElement();
|
||||
parseEntityGraph(world, root, 0);
|
||||
}
|
||||
|
||||
|
||||
void EntityXMLFile::preprocess(std::string inPath, std::string outPath)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
static const XMLCh gLS[] = { 'L', 'S', '\0' };
|
||||
DOMImplementationLS* di = static_cast<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.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.c_str());
|
||||
// TODO: MemBufFormatTarget* formatTarget = new MemBufFormatTarget()
|
||||
output->setByteStream(formatTarget);
|
||||
writer->write(doc, output);
|
||||
|
||||
delete formatTarget;
|
||||
output->release();
|
||||
writer->release();
|
||||
parser->release();
|
||||
}
|
||||
|
||||
void EntityXMLFile::parseComponentInfo()
|
||||
{
|
||||
using namespace xercesc;
|
||||
bool wasChanged;
|
||||
XSModel* xsModel = m_GrammarPool->getXSModel(wasChanged);
|
||||
|
||||
// Find component xsd element declarations
|
||||
std::cout << "Enumerating components..." << std::endl;
|
||||
// <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 EntityXMLFile::parseDefaults()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
for (auto& ci : m_ComponentInfo) {
|
||||
// Allocate memory for default values
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Meta.Stride]);
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride);
|
||||
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
|
||||
parser.setErrorHandler(m_ErrorHandler);
|
||||
|
||||
std::string componentName = ci.first;
|
||||
LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
|
||||
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
|
||||
|
||||
parser.parse(defaultsFile.string().c_str());
|
||||
auto doc = parser.getDocument();
|
||||
if (doc == nullptr) {
|
||||
LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the node in the components namespace matching the component name
|
||||
std::string tagName = "c:" + componentName;
|
||||
auto rootNodes = doc->getElementsByTagName(XSTR(tagName.c_str()));
|
||||
if (rootNodes->getLength() == 0) {
|
||||
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
|
||||
continue;
|
||||
}
|
||||
auto componentElement = dynamic_cast<DOMElement*>(rootNodes->item(0));
|
||||
|
||||
// Fill the default value buffer with values
|
||||
for (auto& field : ci.second.FieldOffsets) {
|
||||
std::string fieldName = field.first;
|
||||
auto fieldNodes = componentElement->getElementsByTagName(XSTR(fieldName.c_str()));
|
||||
auto fieldNode = fieldNodes->item(0);
|
||||
if (fieldNode == nullptr) {
|
||||
LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str());
|
||||
continue;
|
||||
}
|
||||
auto fieldElement = dynamic_cast<DOMElement*>(fieldNode);
|
||||
|
||||
std::string fieldType = ci.second.FieldTypes.at(fieldName);
|
||||
unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName);
|
||||
writeData(fieldElement, fieldType, ci.second.Defaults.get() + fieldOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::predictComponentAllocation()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
auto root = m_DOMDocument->getDocumentElement();
|
||||
|
||||
// Count static instances of components present in entity hierarchy
|
||||
auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*"));
|
||||
for (int i = 0; i < components->getLength(); ++i) {
|
||||
auto component = dynamic_cast<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
|
||||
std::size_t stride = 0;
|
||||
// Add size of fields
|
||||
for (auto& field : ci.FieldTypes) {
|
||||
std::cout << " " << field.second << " " << field.first << " (" << getTypeStride(field.second) << " byte)" << std::endl;
|
||||
stride += getTypeStride(field.second);
|
||||
}
|
||||
std::cout << " Stride: " << ci.Meta.Stride << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::parseEntityGraph(World* world, xercesc::DOMElement* element, EntityID parentEntity)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
// Create entity
|
||||
EntityID entity = world->CreateEntity(parentEntity);
|
||||
LOG_DEBUG("Created entity %i, parent %i", entity, parentEntity);
|
||||
|
||||
// Add components
|
||||
auto components = m_DOMDocument->evaluate(XSTR("Components/*"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
|
||||
for (int i = 0; i < components->getSnapshotLength(); i++) {
|
||||
components->snapshotItem(i);
|
||||
auto componentElement = dynamic_cast<DOMElement*>(components->getNodeValue());
|
||||
std::string componentName = XSTR(componentElement->getLocalName());
|
||||
auto& ci = m_ComponentInfo.at(componentName);
|
||||
|
||||
// Attach the component
|
||||
auto c = world->AttachComponent(entity, componentName);
|
||||
LOG_DEBUG("Attached %s component", componentName.c_str());
|
||||
|
||||
// Write field data
|
||||
auto fields = componentElement->getChildNodes();
|
||||
for (int j = 0; j < fields->getLength(); ++j) {
|
||||
auto fieldNode = fields->item(j);
|
||||
auto nodeType = fieldNode->getNodeType();
|
||||
if (nodeType != DOMNode::ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
auto field = dynamic_cast<DOMElement*>(fields->item(j));
|
||||
//const XMLCh* value = fields->item(j)->getTextContent();
|
||||
std::string fieldName(XSTR(field->getLocalName()));
|
||||
if (ci.FieldTypes.find(fieldName) == ci.FieldTypes.end()) {
|
||||
std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string fieldType = ci.FieldTypes.at(fieldName);
|
||||
unsigned int fieldOffset = ci.FieldOffsets.at(fieldName);
|
||||
std::string fieldValue(XSTR(field->getTextContent()));
|
||||
LOG_DEBUG(" %s %s = %s", fieldType.c_str(), fieldName.c_str(), fieldValue.c_str());
|
||||
writeData(field, fieldType, c.Data + fieldOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse children
|
||||
auto children = m_DOMDocument->evaluate(XSTR("Children/Entity"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
|
||||
for (int i = 0; i < children->getSnapshotLength(); i++) {
|
||||
children->snapshotItem(i);
|
||||
parseEntityGraph(world, dynamic_cast<DOMElement*>(children->getNodeValue()), entity);
|
||||
}
|
||||
|
||||
//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;
|
||||
//}
|
||||
}
|
||||
|
||||
std::size_t EntityXMLFile::getTypeStride(std::string typeName)
|
||||
{
|
||||
std::map<std::string, size_t> typeStrides{
|
||||
{ "bool", sizeof(bool) },
|
||||
{ "int", sizeof(int) },
|
||||
{ "float", sizeof(float) },
|
||||
{ "double", sizeof(double) },
|
||||
{ "string", sizeof(std::string) },
|
||||
{ "Vector", sizeof(glm::vec3) },
|
||||
{ "Quaternion", sizeof(glm::quat) },
|
||||
{ "Color", sizeof(glm::vec4) }
|
||||
};
|
||||
|
||||
auto it = typeStrides.find(typeName);
|
||||
return (it != typeStrides.end()) ? it->second : 0;
|
||||
}
|
||||
|
||||
float EntityXMLFile::getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) const
|
||||
{
|
||||
using namespace xercesc;
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(element->getAttribute(XSTR(attribute)), xercesc::XSValue::DataType::dt_double, status);
|
||||
if (val == nullptr) {
|
||||
LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", XSTR(element->getTagName()), attribute);
|
||||
return 0.f;
|
||||
} else {
|
||||
return static_cast<float>(val->fData.fValue.f_double);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string typeName, char* outData)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
if (typeName == "Vector") {
|
||||
glm::vec3 vec;
|
||||
vec.x = getFloatAttribute(element, "X");
|
||||
vec.y = getFloatAttribute(element, "Y");
|
||||
vec.z = getFloatAttribute(element, "Z");
|
||||
memcpy(outData, reinterpret_cast<char*>(&vec), getTypeStride(typeName));
|
||||
} else if (typeName == "Color") {
|
||||
glm::vec4 vec;
|
||||
vec.r = getFloatAttribute(element, "R");
|
||||
vec.g = getFloatAttribute(element, "G");
|
||||
vec.b = getFloatAttribute(element, "B");
|
||||
vec.a = getFloatAttribute(element, "A");
|
||||
memcpy(outData, reinterpret_cast<char*>(&vec), getTypeStride(typeName));
|
||||
} else if (typeName == "Quaternion") {
|
||||
glm::quat q;
|
||||
q.x = getFloatAttribute(element, "X");
|
||||
q.y = getFloatAttribute(element, "Y");
|
||||
q.z = getFloatAttribute(element, "Z");
|
||||
q.w = getFloatAttribute(element, "W");
|
||||
memcpy(outData, reinterpret_cast<char*>(&q), getTypeStride(typeName));
|
||||
} else if (typeName == "float") {
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_float, status);
|
||||
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue.f_float), getTypeStride(typeName));
|
||||
} else if (typeName == "double") {
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_double, status);
|
||||
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue.f_double), getTypeStride(typeName));
|
||||
} else if (typeName == "bool") {
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_boolean, status);
|
||||
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue.f_bool), getTypeStride(typeName));
|
||||
} else {
|
||||
XSValue::DataType dataType = XSValue::getDataType(XSTR(typeName.c_str()));
|
||||
if (dataType == XSValue::DataType::dt_string) {
|
||||
char* str = XMLString::transcode(element->getTextContent());
|
||||
std::string standardString(str);
|
||||
new (outData) std::string(str);
|
||||
XMLString::release(&str);
|
||||
//memcpy(outData, reinterpret_cast<char*>(&standardString), getTypeStride(typeName));
|
||||
} else {
|
||||
//XSValue::Status status;
|
||||
//XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status);
|
||||
//memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue), getTypeStride(typeName));
|
||||
LOG_WARNING("Unknown native data type: %s", typeName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
BaseEventRelay::~BaseEventRelay()
|
||||
{
|
||||
if (m_Broker != nullptr) {
|
||||
m_Broker->Unsubscribe(*this);
|
||||
m_Broker->Unsubscribe(*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "Core/Transform.h"
|
||||
|
||||
glm::vec3 Transform::AbsolutePosition(World* world, EntityID entity)
|
||||
{
|
||||
glm::vec3 position;
|
||||
|
||||
while (entity != EntityID_Invalid) {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
EntityID parent = world->GetParent(entity);
|
||||
position += Transform::AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"];
|
||||
entity = parent;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
glm::quat Transform::AbsoluteOrientation(World* world, EntityID entity)
|
||||
{
|
||||
glm::quat orientation;
|
||||
|
||||
while (entity != EntityID_Invalid) {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation;
|
||||
entity = world->GetParent(entity);
|
||||
}
|
||||
|
||||
return orientation;
|
||||
}
|
||||
|
||||
glm::vec3 Transform::AbsoluteScale(World* world, EntityID entity)
|
||||
{
|
||||
glm::vec3 scale(1.f);
|
||||
|
||||
while (entity != EntityID_Invalid) {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
scale *= (glm::vec3)transform["Scale"];
|
||||
entity = world->GetParent(entity);
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
glm::mat4 Transform::ModelMatrix(EntityID entity, World* world)
|
||||
{
|
||||
glm::vec3 position = Transform::AbsolutePosition(world, entity);
|
||||
glm::quat orientation = Transform::AbsoluteOrientation(world, entity);
|
||||
glm::vec3 scale = Transform::AbsoluteScale(world, entity);
|
||||
|
||||
glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(orientation) * glm::scale(scale);
|
||||
return modelMatrix;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,15 @@ World::~World()
|
||||
EntityID World::CreateEntity(EntityID parent /*= 0*/)
|
||||
{
|
||||
EntityID newEntity = generateEntityID();
|
||||
if (newEntity == parent) {
|
||||
LOG_WARNING("Invalid parent #%i of Entity#%i", newEntity, parent);
|
||||
parent = EntityID_Invalid;
|
||||
}
|
||||
m_EntityParents[newEntity] = parent;
|
||||
m_EntityChildren.insert(std::make_pair(parent, newEntity));
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
|
||||
void World::DeleteEntity(EntityID entity)
|
||||
{
|
||||
// Delete components
|
||||
@@ -46,15 +49,26 @@ void World::DeleteEntity(EntityID entity)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase potential name
|
||||
m_EntityNames.erase(entity);
|
||||
}
|
||||
|
||||
bool World::ValidEntity(EntityID entity) const
|
||||
{
|
||||
return m_EntityParents.find(entity) != m_EntityParents.end();
|
||||
}
|
||||
|
||||
void World::RegisterComponent(ComponentInfo& ci)
|
||||
{
|
||||
m_ComponentPools[ci.Name] = new ComponentPool(ci);
|
||||
if (m_ComponentPools.find(ci.Name) == m_ComponentPools.end()) {
|
||||
m_ComponentPools[ci.Name] = new ComponentPool(ci);
|
||||
}
|
||||
}
|
||||
|
||||
ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType)
|
||||
{
|
||||
// TODO: Allocate dynamic pool if component isn't registered
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
const ComponentInfo& ci = pool->ComponentInfo();
|
||||
|
||||
@@ -66,8 +80,7 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
bool World::HasComponent(EntityID entity, std::string componentType)
|
||||
bool World::HasComponent(EntityID entity, std::string componentType) const
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
return pool->KnowsEntity(entity);
|
||||
@@ -79,7 +92,6 @@ ComponentWrapper World::GetComponent(EntityID entity, std::string componentType)
|
||||
return pool->GetByEntity(entity);
|
||||
}
|
||||
|
||||
|
||||
void World::DeleteComponent(EntityID entity, std::string componentType)
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
@@ -93,13 +105,11 @@ const ComponentPool* World::GetComponents(std::string componentType)
|
||||
return (it != m_ComponentPools.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityID World::GetParent(EntityID entity)
|
||||
{
|
||||
return m_EntityParents.at(entity);
|
||||
}
|
||||
|
||||
|
||||
void World::SetParent(EntityID entity, EntityID parent)
|
||||
{
|
||||
EntityID lastParent = m_EntityParents.at(entity);
|
||||
@@ -115,6 +125,25 @@ void World::SetParent(EntityID entity, EntityID parent)
|
||||
m_EntityChildren.insert(std::make_pair(parent, entity));
|
||||
}
|
||||
|
||||
void World::SetName(EntityID entity, const std::string& name)
|
||||
{
|
||||
m_EntityNames[entity] = name;
|
||||
}
|
||||
|
||||
std::string World::GetName(EntityID entity) const
|
||||
{
|
||||
if (entity == EntityID_Invalid) {
|
||||
return "EntityID_Invalid";
|
||||
}
|
||||
|
||||
auto it = m_EntityNames.find(entity);
|
||||
if (it != m_EntityNames.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
EntityID World::generateEntityID()
|
||||
{
|
||||
// TODO: Make EntityID generation smarter
|
||||
|
||||
Reference in New Issue
Block a user