Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f6528e0c7 | |||
| 0a3d3f5392 | |||
| c0b31dc92c | |||
| c5b4ea84a0 | |||
| b5605c839d | |||
| 84818f6fcb | |||
| 349fa9d7fa | |||
| 2a5e0420c8 | |||
| 4a516a4d86 | |||
| 21aea6f31d | |||
| 297a3fb493 | |||
| c47ef61ddc | |||
| e8e93587e9 | |||
| 39dd27726d | |||
| 6adc98eea5 | |||
| ad4e723677 | |||
| 11d547cde8 | |||
| 24b9e951c2 | |||
| 81024b4a1d | |||
| 23e78c4947 | |||
| 3e7f27e1fe | |||
| ee83394678 | |||
| 03456ad731 | |||
| 0bcbcda8b8 | |||
| c6655ce0d3 | |||
| b908a9f9c6 | |||
| dc7ed2e0ba | |||
| f71bfca142 | |||
| dbb44fb4e4 | |||
| 6e6460a000 | |||
| 9f871bbe7c | |||
| 2bd60be741 | |||
| dab4ba2164 | |||
| 25a99a04c5 | |||
| 13ade6d7f2 | |||
| 592776e783 | |||
| ff44dccb33 | |||
| e7bb8954bf | |||
| e510f2b88c | |||
| eb69e03e82 | |||
| d38f58165f | |||
| 069a6756b9 | |||
| ef4cbfece8 | |||
| ab6af30a28 | |||
| 3636ad45af | |||
| 6cf7c12d34 | |||
| 4a817613e3 | |||
| 6a3c14540d | |||
| 5602b32507 | |||
| 1431bd1a11 | |||
| 1e370bac13 | |||
| 7c714d1f98 |
@@ -1,16 +1,17 @@
|
||||
#### Bundled libraries
|
||||
Libraries bundled along with binaries for Windows (MSVC14), available as a submodule in the *deps* directory of the source tree.
|
||||
|
||||
| Project | Version | License |
|
||||
| ------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **[GLFW](http://www.glfw.org)** | 3.1.2 | [zlib/libpng License](http://www.glfw.org/license.html) |
|
||||
| **[GLM](http://glm.g-truc.net/0.9.5/index.html)** | 0.9.7.1 | [MIT License](http://glm.g-truc.net/copying.txt) |
|
||||
| **[GLEW](http://glew.sourceforge.net)** | 1.13.0 | [Modified BSD License](http://glew.sourceforge.net/glew.txt), [Mesa 3-D License](http://glew.sourceforge.net/mesa.txt), [Khronos License](http://glew.sourceforge.net/khronos.txt) |
|
||||
| **[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) |
|
||||
| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) |
|
||||
| Project | Version | License |
|
||||
| ---------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **[GLFW](http://www.glfw.org)** | 3.1.2 | [zlib/libpng License](http://www.glfw.org/license.html) |
|
||||
| **[GLM](http://glm.g-truc.net/0.9.5/index.html)** | 0.9.7.1 | [MIT License](http://glm.g-truc.net/copying.txt) |
|
||||
| **[GLEW](http://glew.sourceforge.net)** | 1.13.0 | [Modified BSD License](http://glew.sourceforge.net/glew.txt), [Mesa 3-D License](http://glew.sourceforge.net/mesa.txt), [Khronos License](http://glew.sourceforge.net/khronos.txt) |
|
||||
| **[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) |
|
||||
| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) |
|
||||
| **[nativefiledialog](https://github.com/mlabbe/nativefiledialog) | 2016-01-08 | https://github.com/mlabbe/nativefiledialog/blob/master/LICENSE |
|
||||
|
||||
#### External libraries
|
||||
Libraries that are too big to be bundled with the project.
|
||||
|
||||
+1
-1
Submodule assets updated: 673d4a4e4c...c8e631f449
+1
-1
Submodule deps updated: 1ae6ba5b12...bf83f099ba
@@ -12,9 +12,16 @@ struct ComponentInfo
|
||||
unsigned int Stride = 0;
|
||||
};
|
||||
|
||||
struct Field_t
|
||||
{
|
||||
std::string Type;
|
||||
unsigned int Offset;
|
||||
unsigned int Stride;
|
||||
};
|
||||
|
||||
std::string Name;
|
||||
std::unordered_map<std::string, std::string> FieldTypes;
|
||||
std::unordered_map<std::string, unsigned int> FieldOffsets;
|
||||
std::unordered_map<std::string, Field_t> Fields;
|
||||
std::vector<const Field_t*> FieldsInOrder;
|
||||
Meta_t Meta;
|
||||
std::shared_ptr<char> Defaults = nullptr;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ struct ComponentWrapper
|
||||
template <typename T>
|
||||
T& Property(std::string name)
|
||||
{
|
||||
unsigned int offset = Info.FieldOffsets.at(name);
|
||||
unsigned int offset = Info.Fields.at(name).Offset;
|
||||
return *reinterpret_cast<T*>(&Data[offset]);
|
||||
}
|
||||
|
||||
@@ -78,8 +78,9 @@ public:
|
||||
void AddProperty(std::string fieldName, T defaultValue)
|
||||
{
|
||||
m_DefaultValues.push_back(defaultValue);
|
||||
m_ComponentInfo.FieldTypes[fieldName] = typeid(T).name();
|
||||
m_ComponentInfo.FieldOffsets[fieldName] = m_ComponentInfo.Meta.Stride;
|
||||
m_ComponentInfo.Fields[fieldName].Name = typeid(T).name();
|
||||
m_ComponentInfo.Fields[fieldName].Offset = m_ComponentInfo.Meta.Stride;
|
||||
m_ComponentInfo.Fields[fieldName].Stride = sizeof(T);
|
||||
m_ComponentInfo.Meta.Stride += sizeof(T);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptors.hpp>
|
||||
#include <ini_file/ini_file.hpp>
|
||||
#include "../Common.h"
|
||||
#include "ResourceManager.h"
|
||||
|
||||
@@ -20,46 +20,52 @@ private:
|
||||
public:
|
||||
template <typename T>
|
||||
T Get(std::string key, T defaultValue);
|
||||
template <typename T>
|
||||
std::vector<std::pair<std::string, T>> GetAll(std::string key);
|
||||
template <typename T>
|
||||
void Set(std::string key, T value);
|
||||
|
||||
const ini_file::section* GetSection(std::string section);
|
||||
const ini_file::section_map& GetSections() { return m_Merged; }
|
||||
const ini_file::param* GetParam(std::string key);
|
||||
|
||||
void SaveToDisk();
|
||||
|
||||
private:
|
||||
boost::filesystem::path m_Path;
|
||||
boost::property_tree::ptree m_PTreeDefaults;
|
||||
boost::property_tree::ptree m_PTreeOverrides;
|
||||
boost::property_tree::ptree m_PTreeMerged;
|
||||
ini_file::section_map m_Defaults;
|
||||
ini_file::section_map m_Overrides;
|
||||
ini_file::section_map m_Merged;
|
||||
|
||||
// Merge ini file section map b into a
|
||||
void mergeINI(ini_file::section_map& a, const ini_file::section_map& b);
|
||||
// Convert an ini key delimited by periods to a section and a param
|
||||
boost::optional<std::pair<std::string, std::string>> tokenizeKey(std::string key);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T ConfigFile::Get(std::string key, T defaultValue)
|
||||
{
|
||||
return m_PTreeMerged.get<T>(key, defaultValue);
|
||||
}
|
||||
auto param = GetParam(key);
|
||||
if (param == nullptr) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<std::pair<std::string, T>> ConfigFile::GetAll(std::string key)
|
||||
{
|
||||
std::vector<std::pair<std::string, T>> out;
|
||||
auto parent = m_PTreeMerged.find(key);
|
||||
if (parent == m_PTreeMerged.not_found()) {
|
||||
return out;
|
||||
}
|
||||
for (auto& child : parent->second) {
|
||||
T value = boost::lexical_cast<T>(child.second.data());
|
||||
out.push_back(std::make_pair(child.first, value));
|
||||
}
|
||||
return out;
|
||||
return boost::lexical_cast<T>(param->get_value());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ConfigFile::Set(std::string key, T value)
|
||||
{
|
||||
m_PTreeOverrides.put<T>(key, value);
|
||||
m_PTreeMerged.put<T>(key, value);
|
||||
std::string section;
|
||||
std::string param;
|
||||
if (auto tokens = tokenizeKey(key)) {
|
||||
std::tie(section, param) = *tokens;
|
||||
} else {
|
||||
LOG_WARNING("%s: Malformed config key \"%s\"", m_Path.string().c_str(), key.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
m_Overrides[section][param] = boost::lexical_cast<std::string>(value);
|
||||
m_Merged[section][param] = boost::lexical_cast<std::string>(value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#ifndef DeveloperConsole_h__
|
||||
#define DeveloperConsole_h__
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include "../Common.h"
|
||||
#include "ConfigFile.h"
|
||||
|
||||
class DeveloperConsole
|
||||
{
|
||||
public:
|
||||
DeveloperConsole() = delete;
|
||||
|
||||
static void MergeConfig(std::string configFile)
|
||||
{
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
LOG_DEBUG("Debug.LogLevel %i", config->Get<int>("Debug.LogLevel", -1));
|
||||
config->Set("Debug.LogLevel", 8);
|
||||
LOG_DEBUG("Debug.LogLevel %i", config->Get<int>("Debug.LogLevel", -1));
|
||||
config->Set("Test.TesTest.Testies", "Hello");
|
||||
config->SaveToDisk();
|
||||
for (auto& section : config->GetSections()) {
|
||||
for (auto& param : *section.second) {
|
||||
std::cout << "// " << param.second->get_comment() << std::endl;
|
||||
std::cout << section.first << "." << param.first << " " << param.second->get_value() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& section : config->GetSections()) {
|
||||
for (auto& param : *section.second) {
|
||||
std::string key = section.first + "." + param.first;
|
||||
m_VariableBindingSetters[key] = [config, key](std::string value) {
|
||||
config->Set<std::string>(key, value);
|
||||
};
|
||||
m_VariableBindingGetters[key] = [config, key]() {
|
||||
return config->Get<std::string>(key, "");
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static typename std::enable_if<std::is_enum<T>::value, void>::type
|
||||
BindVariable(std::string path, T& variable)
|
||||
{
|
||||
BindVariable<T, std::underlying_type<T>::type>(path, variable);
|
||||
}
|
||||
|
||||
template <typename T, typename R = T>
|
||||
static typename std::enable_if<!std::is_enum<R>::value, void>::type
|
||||
BindVariable(std::string path, T& variable)
|
||||
{
|
||||
m_VariableBindingSetters[path] = [&variable](std::string value) {
|
||||
variable = static_cast<T>(boost::lexical_cast<R>(value));
|
||||
};
|
||||
|
||||
m_VariableBindingGetters[path] = [&variable]() {
|
||||
return boost::lexical_cast<std::string>(static_cast<R>(variable));
|
||||
};
|
||||
}
|
||||
|
||||
static void Consume(const std::string& command)
|
||||
{
|
||||
if (command.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::char_separator<char> argumentSeparator(" ");
|
||||
tokenizer tokens(command, argumentSeparator);
|
||||
|
||||
for (tokenizer::const_iterator it = tokens.begin(); it != tokens.end(); it++) {
|
||||
consumeToken(it, tokens.end());
|
||||
}
|
||||
}
|
||||
|
||||
static void Consume(std::istream& stream)
|
||||
{
|
||||
std::string input;
|
||||
std::getline(stream, input);
|
||||
Consume(input);
|
||||
|
||||
/*if (stream.peek() == '\n') {
|
||||
printValue(key);
|
||||
} else {
|
||||
std::string newValue;
|
||||
stream >> newValue;
|
||||
|
||||
if (m_VariableBindingSetters.find(key) != m_VariableBindingSetters.end()) {
|
||||
m_VariableBindingSetters.at(key)(newValue);
|
||||
} else {
|
||||
std::cout << "Unknown path: " << key << std::endl;
|
||||
}
|
||||
return;
|
||||
|
||||
for (auto& config : m_ConfigFiles) {
|
||||
config->Set(key, newValue);
|
||||
config->SaveToDisk();
|
||||
LOG_DEBUG("Key %s set to new value %s and saved to disk.", key.c_str(), newValue.c_str());
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
private:
|
||||
typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
|
||||
|
||||
static std::map<std::string, std::function<std::string()>> m_VariableBindingGetters;
|
||||
static std::map<std::string, std::function<void(std::string)>> m_VariableBindingSetters;
|
||||
static std::map<std::string, std::function<std::string(std::string)>> m_VariableBindingMappers;
|
||||
static std::map<std::string, std::string> m_VariableDocumentation;
|
||||
|
||||
static void consumeToken(tokenizer::iterator& token, const tokenizer::iterator end)
|
||||
{
|
||||
tokenizer::iterator next = token;
|
||||
next++;
|
||||
|
||||
if (next == end) {
|
||||
printValue(token);
|
||||
} else {
|
||||
setValue(token, end);
|
||||
}
|
||||
}
|
||||
|
||||
static void printValue(tokenizer::iterator token)
|
||||
{
|
||||
std::string key = *token;
|
||||
std::string value;
|
||||
|
||||
if (m_VariableBindingGetters.find(key) != m_VariableBindingGetters.end()) {
|
||||
std::cout << key << " = " << m_VariableBindingGetters.at(key)() << std::endl;
|
||||
} else {
|
||||
std::cout << "Unknown path: " << key << std::endl;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
if (!value.empty()) {
|
||||
std::cout << key << " = " << value << std::endl;
|
||||
} else {
|
||||
std::cerr << "Unknown path: " << key << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static void setValue(tokenizer::iterator& token, const tokenizer::iterator end)
|
||||
{
|
||||
std::string key = *token;
|
||||
std::string value = *(++token);
|
||||
|
||||
if (m_VariableBindingSetters.find(key) != m_VariableBindingSetters.end()) {
|
||||
m_VariableBindingSetters.at(key)(value);
|
||||
} else {
|
||||
std::cout << "Unknown path: " << key << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -2,5 +2,6 @@
|
||||
#define Entity_h__
|
||||
|
||||
typedef unsigned int EntityID;
|
||||
const static unsigned int EntityID_Invalid = -1;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,293 @@
|
||||
#ifndef EntityFile_h__
|
||||
#define EntityFile_h__
|
||||
|
||||
#include <stack>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <xercesc/util/XercesDefs.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/sax2/SAX2XMLReader.hpp>
|
||||
#include <xercesc/sax2/XMLReaderFactory.hpp>
|
||||
#include <xercesc/sax2/DefaultHandler.hpp>
|
||||
#include <xercesc/sax2/Attributes.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/util/XMLChar.hpp>
|
||||
#include <xercesc/sax2/DefaultHandler.hpp>
|
||||
#include <xercesc/framework/XMLDocumentHandler.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/XMLGrammarPoolImpl.hpp>
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/dom/DOMLSInput.hpp>
|
||||
#include <xercesc/dom/DOMElement.hpp>
|
||||
|
||||
#include "../GLM.h"
|
||||
#include "Util/XercesString.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "Entity.h"
|
||||
#include "ComponentInfo.h"
|
||||
|
||||
class EntityFileHandler
|
||||
{
|
||||
friend class EntityFileSAXHandler;
|
||||
public:
|
||||
// @param EntityID The entity found
|
||||
// @param EntityID The parent of the entity
|
||||
typedef std::function<void(EntityID, EntityID, const std::string&)> OnStartEntityCallback;
|
||||
void SetStartEntityCallback(OnStartEntityCallback c) { m_OnStartEntityCallback = c; }
|
||||
// @param EntityID The entity the component corresponds to
|
||||
// @param std::string Type name of the component
|
||||
typedef std::function<void(EntityID, const std::string&)> OnStartComponentCallback;
|
||||
void SetStartComponentCallback(OnStartComponentCallback c) { m_OnStartComponentCallback = c; }
|
||||
// @param EntityID Entity
|
||||
// @param std::string Component name
|
||||
// @param std::string Field name
|
||||
// @param std::map<std::string, std::string> Field attribute names and values
|
||||
typedef std::function<void(EntityID, const std::string&, const std::string&, const std::map<std::string, std::string>&)> OnStartFieldCallback;
|
||||
void SetStartFieldCallback(OnStartFieldCallback c) { m_OnStartFieldCallback = c; }
|
||||
// @param EntityID Entity
|
||||
// @param std::string Component name
|
||||
// @param std::string Field name
|
||||
// @param char* Field data
|
||||
typedef std::function<void(EntityID, const std::string&, const std::string&, const char*)> OnStartFieldDataCallback;
|
||||
void SetStartFieldDataCallback(OnStartFieldDataCallback c) { m_OnStartFieldDataCallback = c; }
|
||||
|
||||
private:
|
||||
OnStartEntityCallback m_OnStartEntityCallback = nullptr;
|
||||
OnStartComponentCallback m_OnStartComponentCallback = nullptr;
|
||||
OnStartFieldCallback m_OnStartFieldCallback = nullptr;
|
||||
OnStartFieldDataCallback m_OnStartFieldDataCallback = nullptr;
|
||||
};
|
||||
|
||||
class EntityFileSAXHandler : public xercesc::DefaultHandler
|
||||
{
|
||||
public:
|
||||
enum class State
|
||||
{
|
||||
Unknown,
|
||||
Entity,
|
||||
Component,
|
||||
ComponentField
|
||||
};
|
||||
|
||||
EntityFileSAXHandler(const EntityFileHandler* handler, xercesc::SAX2XMLReader* reader)
|
||||
: m_Handler(handler)
|
||||
, m_Reader(reader)
|
||||
{
|
||||
// 0 is imaginary world entity
|
||||
m_EntityStack.push(0);
|
||||
m_StateStack.push(State::Unknown);
|
||||
}
|
||||
|
||||
void startElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname, const xercesc::Attributes& attrs) override
|
||||
{
|
||||
std::string name = XS::ToString(_localName);
|
||||
|
||||
if (m_StateStack.top() == State::Unknown || m_StateStack.top() == State::Entity) {
|
||||
if (name == "Entity") {
|
||||
m_StateStack.push(State::Entity);
|
||||
onStartEntity(attrs);
|
||||
return;
|
||||
}
|
||||
if (name == "EntityRef") {
|
||||
onStartEntityRef(attrs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string uri = XS::ToString(_uri);
|
||||
if (m_StateStack.top() == State::Entity) {
|
||||
if (uri == "components") {
|
||||
m_StateStack.push(State::Component);
|
||||
onStartComponent(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_StateStack.top() == State::Component) {
|
||||
m_StateStack.push(State::ComponentField);
|
||||
onStartComponentField(name, attrs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void characters(const XMLCh* const chars, const XMLSize_t length) override
|
||||
{
|
||||
if (m_StateStack.top() == State::ComponentField) {
|
||||
char* transcoded = xercesc::XMLString::transcode(chars);
|
||||
onFieldData(transcoded);
|
||||
}
|
||||
}
|
||||
|
||||
void endElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname) override
|
||||
{
|
||||
std::string name = XS::ToString(_localName);
|
||||
if (m_StateStack.top() == State::Entity) {
|
||||
if (name == "Entity") {
|
||||
m_StateStack.pop();
|
||||
onEndEntity();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string uri = XS::ToString(_uri);
|
||||
if (m_StateStack.top() == State::Component) {
|
||||
//if (uri == "components") {
|
||||
m_StateStack.pop();
|
||||
onEndComponent(name);
|
||||
return;
|
||||
//}
|
||||
}
|
||||
|
||||
if (m_StateStack.top() == State::ComponentField) {
|
||||
m_StateStack.pop();
|
||||
onEndComponentField(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void fatalError(const xercesc::SAXParseException& e)
|
||||
{
|
||||
XS::ToString s(e.getMessage());
|
||||
LOG_ERROR("SAXParseException: %s", ((std::string)s).c_str());
|
||||
//throw e;
|
||||
}
|
||||
|
||||
private:
|
||||
const EntityFileHandler* m_Handler;
|
||||
xercesc::XMLGrammarPool* m_GrammarPool;
|
||||
xercesc::SAX2XMLReader* m_Reader;
|
||||
//State m_CurrentScope = State::Unknown;
|
||||
std::stack<State> m_StateStack;
|
||||
unsigned int m_NextEntityID = 0;
|
||||
std::stack<EntityID> m_EntityStack;
|
||||
std::string m_CurrentComponent;
|
||||
std::string m_CurrentField;
|
||||
std::map<std::string, std::string> m_CurrentAttributes;
|
||||
|
||||
void onStartEntity(const xercesc::Attributes& attrs)
|
||||
{
|
||||
EntityID parent = m_EntityStack.top();
|
||||
|
||||
if (m_Handler->m_OnStartEntityCallback) {
|
||||
std::string name;
|
||||
auto xName = attrs.getValue(XS::ToXMLCh("name"));
|
||||
if (xName != nullptr) {
|
||||
name = XS::ToString(xName);
|
||||
}
|
||||
m_Handler->m_OnStartEntityCallback(m_NextEntityID, parent, name);
|
||||
}
|
||||
|
||||
m_EntityStack.push(m_NextEntityID);
|
||||
m_NextEntityID++;
|
||||
}
|
||||
void onEndEntity()
|
||||
{
|
||||
m_EntityStack.pop();
|
||||
}
|
||||
void onStartEntityRef(const xercesc::Attributes& attrs)
|
||||
{
|
||||
std::string path = XS::ToString(attrs.getValue(XS::ToXMLCh("file")));
|
||||
|
||||
xercesc::SAX2XMLReader* parser = xercesc::XMLReaderFactory::createXMLReader();
|
||||
parser->setContentHandler(this);
|
||||
parser->setErrorHandler(this);
|
||||
parser->parse(path.c_str());
|
||||
delete parser;
|
||||
}
|
||||
void onStartComponent(const std::string& name)
|
||||
{
|
||||
//LOG_DEBUG(" Component: %s", name.c_str());
|
||||
m_CurrentComponent = name;
|
||||
if (m_Handler->m_OnStartComponentCallback) {
|
||||
m_Handler->m_OnStartComponentCallback(m_EntityStack.top(), name);
|
||||
}
|
||||
}
|
||||
void onEndComponent(const std::string& name) { }
|
||||
void onStartComponentField(const std::string& field, const xercesc::Attributes& attrs)
|
||||
{
|
||||
//LOG_DEBUG(" Field: %s", field.c_str());
|
||||
m_CurrentField = field;
|
||||
m_CurrentAttributes.clear();
|
||||
for (int i = 0; i < attrs.getLength(); i++) {
|
||||
auto name = attrs.getQName(i);
|
||||
auto value = attrs.getValue(name);
|
||||
//LOG_DEBUG(" %s = %s", (char*)XS::ToString(name), (char*)XS::ToString(value));
|
||||
m_CurrentAttributes[XS::ToString(name).operator std::string()] = XS::ToString(value).operator std::string();
|
||||
}
|
||||
|
||||
if (m_Handler->m_OnStartFieldCallback) {
|
||||
m_Handler->m_OnStartFieldCallback(m_EntityStack.top(), m_CurrentComponent, field, m_CurrentAttributes);
|
||||
}
|
||||
}
|
||||
void onEndComponentField(const std::string& field) { }
|
||||
|
||||
void onFieldData(char* data)
|
||||
{
|
||||
//LOG_DEBUG(" Data: %s", data);
|
||||
|
||||
if (m_Handler->m_OnStartFieldDataCallback) {
|
||||
m_Handler->m_OnStartFieldDataCallback(m_EntityStack.top(), m_CurrentComponent, m_CurrentField, data);
|
||||
}
|
||||
|
||||
xercesc::XMLString::release(&data);
|
||||
}
|
||||
};
|
||||
|
||||
class EntityFileXMLErrorHandler : 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 EntityFile : public Resource
|
||||
{
|
||||
friend class ResourceManager;
|
||||
private:
|
||||
EntityFile(boost::filesystem::path path);
|
||||
~EntityFile();
|
||||
|
||||
public:
|
||||
static std::size_t GetTypeStride(std::string typeName);
|
||||
static void WriteAttributeData(char* outData, const ComponentInfo::Field_t& field, const std::map<std::string, std::string>& attributes);
|
||||
static void WriteValueData(char* outData, const ComponentInfo::Field_t& field, const char* valueData);
|
||||
|
||||
xercesc::XMLGrammarPool* GrammarPool() const { return m_GrammarPool; }
|
||||
|
||||
void Parse(const EntityFileHandler* handler) const;
|
||||
|
||||
private:
|
||||
boost::filesystem::path m_FilePath;
|
||||
xercesc::XMLGrammarPool* m_GrammarPool;
|
||||
xercesc::SAX2XMLReader* m_SAX2XMLReader;
|
||||
//std::map<std::string, ::ComponentInfo> m_ComponentInfo;
|
||||
//std::vector<std::string> m_EntityReferences;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef EntityFileParser_h__
|
||||
#define EntityFileParser_h__
|
||||
|
||||
#include "EntityFile.h"
|
||||
#include "World.h"
|
||||
|
||||
class EntityFileParser
|
||||
{
|
||||
public:
|
||||
EntityFileParser(const EntityFile* entityFile);
|
||||
|
||||
void MergeEntities(World* world);
|
||||
|
||||
private:
|
||||
const EntityFile* m_EntityFile;
|
||||
EntityFileHandler m_Handler;
|
||||
World* m_World = nullptr;
|
||||
// Maps EntityIDs local to the file to real IDs in the world after they've been
|
||||
// created in order to resolve parent-child relationships.
|
||||
std::map<EntityID, EntityID> m_EntityIDMapper;
|
||||
|
||||
void onStartEntity(EntityID entity, EntityID parent, const std::string& name);
|
||||
void onStartComponent(EntityID entity, const std::string& component);
|
||||
void onStartComponentField(EntityID entity, const std::string& componentType, const std::string& fieldName, const std::map<std::string, std::string>& attributes);
|
||||
void onFieldData(EntityID entity, const std::string& componentType, const std::string& fieldName, const char* fieldData);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef EntityFilePreprocessor_h__
|
||||
#define EntityFilePreprocessor_h__
|
||||
|
||||
#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/MemBufFormatTarget.hpp>
|
||||
#include <xercesc/framework/MemBufInputSource.hpp>
|
||||
#include <xercesc/parsers/XercesDOMParser.hpp>
|
||||
|
||||
#include "Util/XercesString.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "World.h"
|
||||
#include "EntityFile.h"
|
||||
|
||||
class EntityFilePreprocessor
|
||||
{
|
||||
public:
|
||||
EntityFilePreprocessor(const EntityFile* entityFile);
|
||||
|
||||
void RegisterComponents(World* world);
|
||||
|
||||
private:
|
||||
const EntityFile* m_EntityFile;
|
||||
std::map<std::string, unsigned int> m_ComponentCounts;
|
||||
std::map<std::string, ComponentInfo> m_ComponentInfo;
|
||||
|
||||
void onStartComponent(EntityID entity, std::string type);
|
||||
void parseComponentInfo();
|
||||
void parseDefaults();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef EntityFileWriter_h__
|
||||
#define EntityFileWriter_h__
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/framework/LocalFileFormatTarget.hpp>
|
||||
|
||||
#include "Util/XercesString.h"
|
||||
#include "EntityFile.h"
|
||||
#include "World.h"
|
||||
|
||||
class EntityFileWriter
|
||||
{
|
||||
public:
|
||||
EntityFileWriter(boost::filesystem::path file)
|
||||
: m_FilePath(file)
|
||||
{
|
||||
using namespace xercesc;
|
||||
m_DOMImplementation = DOMImplementationRegistry::getDOMImplementation(XS::ToXMLCh("LS"));
|
||||
m_DOMLSSerializer = static_cast<DOMImplementationLS*>(m_DOMImplementation)->createLSSerializer();
|
||||
m_DOMLSSerializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true);
|
||||
m_DOMLSSerializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
|
||||
}
|
||||
|
||||
void WriteWorld(World* world);
|
||||
void WriteEntity(World* world, EntityID entity);
|
||||
|
||||
private:
|
||||
boost::filesystem::path m_FilePath;
|
||||
xercesc::DOMImplementation* m_DOMImplementation;
|
||||
xercesc::DOMLSSerializer* m_DOMLSSerializer;
|
||||
|
||||
void appendEntityChildren(xercesc::DOMElement* parentElement, const World* world, EntityID entity);
|
||||
void appentEntityComponents(xercesc::DOMElement* parentElemetn, const World* world, EntityID entity);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,141 +0,0 @@
|
||||
#ifndef EntityXMLFile_h__
|
||||
#define EntityXMLFile_h__
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "../Common.h"
|
||||
#include "../GLM.h"
|
||||
#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 "ResourceManager.h"
|
||||
#include "Entity.h"
|
||||
#include "ComponentInfo.h"
|
||||
class World;
|
||||
|
||||
class EntityPreprocessorXMLErrorHandler : public xercesc::DOMErrorHandler
|
||||
{
|
||||
public:
|
||||
bool handleError(const xercesc::DOMError &e) override
|
||||
{
|
||||
char* message = xercesc::XMLString::transcode(e.getMessage());
|
||||
std::cerr << "Preprocessor DOMError: " << message << std::endl;
|
||||
xercesc::XMLString::release(&message);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class EntityParserXMLErrorHandler : public xercesc::ErrorHandler
|
||||
{
|
||||
public:
|
||||
void warning(const xercesc::SAXParseException& e) override
|
||||
{
|
||||
reportParseException("Warning", e);
|
||||
}
|
||||
void error(const xercesc::SAXParseException& e) override
|
||||
{
|
||||
reportParseException("Error", e);
|
||||
}
|
||||
void fatalError(const xercesc::SAXParseException& e) override
|
||||
{
|
||||
reportParseException("FATAL ERROR", e);
|
||||
}
|
||||
void resetErrors() override { }
|
||||
|
||||
private:
|
||||
void reportParseException(std::string type, const xercesc::SAXParseException& e)
|
||||
{
|
||||
char* message = xercesc::XMLString::transcode(e.getMessage());
|
||||
char* systemID = xercesc::XMLString::transcode(e.getSystemId());
|
||||
std::cerr << systemID << ":" << e.getLineNumber() << ":" << e.getColumnNumber() << std::endl;
|
||||
std::cerr << type << ": " << message << std::endl;
|
||||
xercesc::XMLString::release(&systemID);
|
||||
xercesc::XMLString::release(&message);
|
||||
}
|
||||
};
|
||||
|
||||
class XSTR
|
||||
{
|
||||
public:
|
||||
XSTR(const XMLCh* const xmlString)
|
||||
{
|
||||
m_AsChar = xercesc::XMLString::transcode(xmlString);
|
||||
}
|
||||
|
||||
XSTR(const char* normalString)
|
||||
{
|
||||
m_AsXMLCh = xercesc::XMLString::transcode(normalString);
|
||||
}
|
||||
|
||||
~XSTR()
|
||||
{
|
||||
if (m_AsChar != nullptr) {
|
||||
xercesc::XMLString::release(&m_AsChar);
|
||||
}
|
||||
if (m_AsXMLCh != nullptr) {
|
||||
xercesc::XMLString::release(&m_AsXMLCh);
|
||||
}
|
||||
}
|
||||
|
||||
operator const char*() const { return m_AsChar; }
|
||||
operator const XMLCh*() const { return m_AsXMLCh; }
|
||||
|
||||
private:
|
||||
char* m_AsChar = nullptr;
|
||||
XMLCh* m_AsXMLCh = nullptr;
|
||||
};
|
||||
|
||||
class EntityXMLFile : public Resource
|
||||
{
|
||||
friend class ResourceManager;
|
||||
|
||||
private:
|
||||
EntityXMLFile(std::string path);
|
||||
|
||||
public:
|
||||
~EntityXMLFile();
|
||||
|
||||
void PopulateWorld(World* world);
|
||||
|
||||
private:
|
||||
static unsigned int InstanceCount;
|
||||
|
||||
std::string m_EntityFile;
|
||||
xercesc::XMLGrammarPool* m_GrammarPool = nullptr;
|
||||
EntityParserXMLErrorHandler* m_ErrorHandler = nullptr;
|
||||
xercesc::XercesDOMParser* m_DOMParser = nullptr;
|
||||
xercesc::DOMDocument* m_DOMDocument = nullptr;
|
||||
std::map<std::string, ComponentInfo> m_ComponentInfo;
|
||||
|
||||
// Preprocesses the entity file to insert include-by-copy child entities
|
||||
// TODO: Make this work in memory instead of saving to file
|
||||
void preprocess(std::string inPath, std::string outPath);
|
||||
|
||||
void parseComponentInfo();
|
||||
void parseDefaults();
|
||||
void predictComponentAllocation();
|
||||
void parseEntityGraph(World* world, xercesc::DOMElement* parent, EntityID parentEntity);
|
||||
std::size_t getTypeStride(std::string typeName);
|
||||
float getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) const;
|
||||
void writeData(const xercesc::DOMElement* element, std::string typeName, char* outData);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -80,4 +80,27 @@ static void _LOG(_LOG_LEVEL logLevel, const char* file, const char* func, unsign
|
||||
#define LOG_DEBUG(format, ...) \
|
||||
LOG(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__)
|
||||
|
||||
// Set the log level temporarily for the current scope
|
||||
#define LOG_LEVEL_SCOPE(logLevel) \
|
||||
_LOG_LEVEL_SCOPED_HELPER<logLevel> _logLevelScopedHelper;
|
||||
|
||||
template <_LOG_LEVEL LEVEL>
|
||||
class _LOG_LEVEL_SCOPED_HELPER
|
||||
{
|
||||
public:
|
||||
_LOG_LEVEL_SCOPED_HELPER()
|
||||
{
|
||||
m_OriginalLogLevel = LOG_LEVEL;
|
||||
LOG_LEVEL = LEVEL;
|
||||
}
|
||||
|
||||
~_LOG_LEVEL_SCOPED_HELPER()
|
||||
{
|
||||
LOG_LEVEL = m_OriginalLogLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
_LOG_LEVEL m_OriginalLogLevel;
|
||||
};
|
||||
|
||||
#endif // Logging_h__
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef Util_XercesString_h__
|
||||
#define Util_XercesString_h__
|
||||
|
||||
#include <string>
|
||||
#include <xercesc/util/XercesDefs.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
|
||||
namespace XS
|
||||
{
|
||||
|
||||
class ToString
|
||||
{
|
||||
public:
|
||||
ToString(const XMLCh* const str) { m_AsChar = xercesc::XMLString::transcode(str); }
|
||||
~ToString()
|
||||
{
|
||||
if (m_AsChar != nullptr) {
|
||||
xercesc::XMLString::release(&m_AsChar);
|
||||
}
|
||||
}
|
||||
|
||||
operator std::string() const { return std::string(m_AsChar); }
|
||||
private:
|
||||
char* m_AsChar = nullptr;
|
||||
};
|
||||
|
||||
class ToXMLCh
|
||||
{
|
||||
public:
|
||||
ToXMLCh(const std::string str) { m_Transcoded = xercesc::XMLString::transcode(str.c_str()); }
|
||||
ToXMLCh(const char* str) { m_Transcoded = xercesc::XMLString::transcode(str); }
|
||||
~ToXMLCh()
|
||||
{
|
||||
if (m_Transcoded != nullptr) {
|
||||
xercesc::XMLString::release(&m_Transcoded);
|
||||
}
|
||||
}
|
||||
|
||||
operator const XMLCh*() const { return m_Transcoded; }
|
||||
private:
|
||||
XMLCh* m_Transcoded = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,13 +16,14 @@ public:
|
||||
EntityID CreateEntity(EntityID parent = 0);
|
||||
// Delete entity and all components within
|
||||
void DeleteEntity(EntityID entity);
|
||||
|
||||
// Check if an entity exists
|
||||
bool ValidEntity(EntityID entity) const;
|
||||
// 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);
|
||||
// Check if an entity has a component
|
||||
bool HasComponent(EntityID entity, std::string componentType);
|
||||
bool HasComponent(EntityID entity, std::string componentType) const;
|
||||
// Get a component of an entity
|
||||
ComponentWrapper GetComponent(EntityID entity, std::string componentType);
|
||||
// Delete a component off an entity
|
||||
@@ -37,14 +38,19 @@ public:
|
||||
const std::unordered_map<std::string, ComponentPool*>& GetComponentPools() const { return m_ComponentPools; }
|
||||
// Get the entity children map
|
||||
const std::unordered_multimap<EntityID, EntityID>& GetEntityChildren() const { return m_EntityChildren; }
|
||||
// Set the textual name of an entity
|
||||
void SetName(EntityID entity, const std::string& name);
|
||||
// Get the textual name of an entity
|
||||
std::string GetName(EntityID entity) const;
|
||||
|
||||
private:
|
||||
EntityID m_CurrentEntityID = 1;
|
||||
EntityID m_CurrentEntityID = 0;
|
||||
|
||||
std::unordered_map<EntityID, EntityID> m_EntityParents;
|
||||
// TODO: This should be a more effective structure
|
||||
std::unordered_multimap<EntityID, EntityID> m_EntityChildren;
|
||||
std::unordered_map<std::string, ComponentPool*> m_ComponentPools;
|
||||
std::unordered_map<EntityID, std::string> m_EntityNames;
|
||||
|
||||
EntityID generateEntityID();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <imgui/imgui.h>
|
||||
#include <glm/gtx/common.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <nativefiledialog/nfd.h>
|
||||
#include "../Core/System.h"
|
||||
#include "../Core/EMousePress.h"
|
||||
#include "../Core/EMouseRelease.h"
|
||||
@@ -11,6 +12,9 @@
|
||||
#include "../Rendering/EPicking.h"
|
||||
#include "../Core/EFileDropped.h"
|
||||
#include "../Rendering/RenderQueueFactory.h"
|
||||
#include "../Core/EntityFilePreprocessor.h"
|
||||
#include "../Core/EntityFileParser.h"
|
||||
#include "../Core/EntityFileWriter.h"
|
||||
|
||||
class EditorSystem : public ImpureSystem
|
||||
{
|
||||
@@ -25,6 +29,8 @@ private:
|
||||
|
||||
bool m_Enabled;
|
||||
bool m_Visible;
|
||||
boost::filesystem::path m_DefaultEntityDir;
|
||||
boost::filesystem::path m_CurrentFile;
|
||||
std::vector<glm::vec2> m_PickingQueue;
|
||||
|
||||
enum class WidgetMode
|
||||
@@ -41,22 +47,26 @@ private:
|
||||
Global
|
||||
} m_WidgetSpace = WidgetSpace::Global;
|
||||
|
||||
EntityID m_Widget = 0;
|
||||
EntityID m_WidgetX = 0;
|
||||
EntityID m_WidgetPlaneX = 0;
|
||||
EntityID m_WidgetY = 0;
|
||||
EntityID m_WidgetPlaneY = 0;
|
||||
EntityID m_WidgetZ = 0;
|
||||
EntityID m_WidgetPlaneZ = 0;
|
||||
EntityID m_WidgetOrigin = 0;
|
||||
EntityID m_Widget = EntityID_Invalid;
|
||||
EntityID m_WidgetX = EntityID_Invalid;
|
||||
EntityID m_WidgetPlaneX = EntityID_Invalid;
|
||||
EntityID m_WidgetY = EntityID_Invalid;
|
||||
EntityID m_WidgetPlaneY = EntityID_Invalid;
|
||||
EntityID m_WidgetZ = EntityID_Invalid;
|
||||
EntityID m_WidgetPlaneZ = EntityID_Invalid;
|
||||
EntityID m_WidgetOrigin = EntityID_Invalid;
|
||||
glm::vec3 m_WidgetCurrentAxis;
|
||||
float m_WidgetPickingDepth = 0.f;
|
||||
|
||||
EntityID m_Selection = 0;
|
||||
EntityID m_LastSelection = 0;
|
||||
EntityID m_Selection = EntityID_Invalid;
|
||||
EntityID m_LastSelection = EntityID_Invalid;
|
||||
EntityID m_UIDraggingEntity = EntityID_Invalid;
|
||||
glm::vec3 m_Position;
|
||||
std::string m_LastDroppedFile;
|
||||
|
||||
static boost::filesystem::path openDialog(boost::filesystem::path defaultPath);
|
||||
static boost::filesystem::path saveDialog(boost::filesystem::path defaultPath);
|
||||
|
||||
EventRelay<EditorSystem, Events::InputCommand> m_EInputCommand;
|
||||
bool OnInputCommand(const Events::InputCommand& e);
|
||||
EventRelay<EditorSystem, Events::MouseRelease> m_EMouseRelease;
|
||||
@@ -70,10 +80,15 @@ private:
|
||||
EventRelay<EditorSystem, Events::FileDropped> m_EFileDropped;
|
||||
bool OnFileDropped(const Events::FileDropped& e);
|
||||
|
||||
void createWidget();
|
||||
void updateWidget();
|
||||
void setWidgetMode(WidgetMode newMode);
|
||||
void setWidgetSpace(WidgetSpace space);
|
||||
void drawUI(World* world, double dt);
|
||||
bool createDeleteButton(std::string componentType);
|
||||
bool createEntityNode(World* world, EntityID entity);
|
||||
void changeParent(EntityID entity, EntityID newParent);
|
||||
void fileImport(World* world);
|
||||
void fileSave(World* world);
|
||||
void fileSaveAs(World* world);
|
||||
};
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
if (it != PickingColorsToEntity->end()) {
|
||||
pickData.Entity = it->second;
|
||||
} else {
|
||||
pickData.Entity = 0;
|
||||
pickData.Entity = EntityID_Invalid;
|
||||
}
|
||||
pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, Resolution, ProjectionMatrix, ViewMatrix);
|
||||
|
||||
|
||||
+3
-1
@@ -13,11 +13,13 @@
|
||||
#include "Input/KeyboardInputHandler.h"
|
||||
#include "Input/MouseInputHandler.h"
|
||||
#include "Core/EKeyDown.h"
|
||||
#include "Core/EntityXMLFile.h"
|
||||
#include "Core/EntityFilePreprocessor.h"
|
||||
#include "Core/SystemPipeline.h"
|
||||
#include "RaptorCopterSystem.h"
|
||||
#include "PlayerSystem.h"
|
||||
#include "Editor/EditorSystem.h"
|
||||
#include "Core/EntityFile.h"
|
||||
#include "Core/EntityFileParser.h"
|
||||
|
||||
// Network
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Types/Entity.xsd" xmlns:c="components">
|
||||
<Components>
|
||||
<c:Transform/>
|
||||
</Components>
|
||||
</Entity>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude" xsi:noNamespaceSchemaLocation="../Types/Entity.xsd" xmlns:c="components" name="RaptorCopter">
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0"/>
|
||||
<Orientation X="0.0" Y="0" Z="0.0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitRaptor.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0"/>
|
||||
<Orientation X="0" Y="0" Z="0"/>
|
||||
</c:Transform>
|
||||
<c:RaptorCopter>
|
||||
<Speed>20</Speed>
|
||||
<Axis Y="1"/>
|
||||
</c:RaptorCopter>
|
||||
</Components>
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0.4"/>
|
||||
<Scale X="0.1" Y="0.4" Z="0.1"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCylinder.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0.6"/>
|
||||
<Scale X="1.7" Y="0.03" Z="0.1"/>
|
||||
<Orientation X="0" Y="0" Z="0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0.6"/>
|
||||
<Scale X="1.7" Y="0.03" Z="0.1"/>
|
||||
<Orientation X="0" Y="1.57" Z="0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
</Entity>
|
||||
</Children>
|
||||
</Entity>
|
||||
</Children>
|
||||
</Entity>
|
||||
@@ -24,11 +24,19 @@
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Entity" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="EntityRef" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:attribute name="file" type="xs:string" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute ref="xml:base"/>
|
||||
<xs:attribute name="name" type="xs:string" minOccurs="0" default=""/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -90,12 +90,28 @@ set(SOURCE_FILES
|
||||
${SOURCE_FILES_Rendering}
|
||||
${SOURCE_FILES_Rendering_Util}
|
||||
${SOURCE_FILES_Collision}
|
||||
${SOURCE_FILES_Editor}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_draw.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_demo.cpp
|
||||
${SOURCE_FILES_Editor}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/ini_file/ini_file.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_common.c
|
||||
)
|
||||
|
||||
# nativefiledialog
|
||||
if(WIN32)
|
||||
set(SOURCE_FILES ${SOURCE_FILES}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_win.cpp
|
||||
)
|
||||
endif()
|
||||
if(UNIX)
|
||||
set(SOURCE_FILES ${SOURCE_FILES}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_gtk.c
|
||||
# TODO: Link with GTK+ here!
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
set(LIBRARIES
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLEW_LIBRARIES}
|
||||
|
||||
@@ -7,36 +7,110 @@ ConfigFile::ConfigFile(std::string path)
|
||||
boost::filesystem::path defaultFile;
|
||||
defaultFile = m_Path.parent_path() / ("Default" + m_Path.filename().string());
|
||||
|
||||
std::fstream file;
|
||||
// Read defaults
|
||||
if (boost::filesystem::exists(defaultFile)) {
|
||||
file.open(defaultFile.string());
|
||||
if (file) {
|
||||
try {
|
||||
boost::property_tree::ini_parser::read_ini(defaultFile.string(), m_PTreeDefaults);
|
||||
} catch (boost::property_tree::ptree_error& e) {
|
||||
LOG_ERROR("Failed to parse \"%s\":\n%s", defaultFile.string().c_str(), e.what());
|
||||
file >> m_Defaults;
|
||||
} catch (ini_file::ini_exceptions::ini_file_exception& e) {
|
||||
LOG_ERROR("Failed to parse \"%s\":\n%s", defaultFile.string().c_str(), "ini_file_exception");
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING("Failed to find \"%s\"! Relying on hardcoded default values!", defaultFile.string().c_str());
|
||||
}
|
||||
file.close();
|
||||
|
||||
// Read overrides
|
||||
file.open(m_Path.string());
|
||||
if (file) {
|
||||
try {
|
||||
file >> m_Overrides;
|
||||
} catch (ini_file::ini_exceptions::ini_file_exception& e) {
|
||||
LOG_ERROR("Failed to parse \"%s\":\n%s", defaultFile.string().c_str(), "ini_file_exception");
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Failed to find \"%s\"! Relying on hardcoded default values!", defaultFile.string().c_str());
|
||||
}
|
||||
file.close();
|
||||
|
||||
m_PTreeMerged = m_PTreeDefaults;
|
||||
// Merge
|
||||
mergeINI(m_Merged, m_Defaults);
|
||||
mergeINI(m_Merged, m_Overrides);
|
||||
}
|
||||
|
||||
// Read overrides
|
||||
if (boost::filesystem::exists(m_Path)) {
|
||||
try {
|
||||
boost::property_tree::ini_parser::read_ini(m_Path.string(), m_PTreeOverrides);
|
||||
for (auto& topLevelNode : m_PTreeOverrides) {
|
||||
auto& mergedTopLevelNode = m_PTreeMerged.find(topLevelNode.first);
|
||||
for (auto& childOverrideNode : topLevelNode.second) {
|
||||
mergedTopLevelNode->second.put_child(childOverrideNode.first, childOverrideNode.second);
|
||||
}
|
||||
}
|
||||
} catch (boost::property_tree::ptree_error& e) {
|
||||
LOG_ERROR("Failed to parse \"%s\":\n%s", m_Path.filename().string().c_str(), e.what());
|
||||
}
|
||||
const ini_file::section* ConfigFile::GetSection(std::string section)
|
||||
{
|
||||
auto sectionIt = m_Merged.find(section);
|
||||
if (sectionIt == m_Merged.end()) {
|
||||
LOG_WARNING("%s: Unknown section \"%s\"", m_Path.string().c_str(), section.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sectionIt->second.get();
|
||||
}
|
||||
|
||||
const ini_file::param* ConfigFile::GetParam(std::string key)
|
||||
{
|
||||
std::string section;
|
||||
std::string param;
|
||||
if (auto tokens = tokenizeKey(key)) {
|
||||
std::tie(section, param) = *tokens;
|
||||
} else {
|
||||
LOG_WARNING("%s: Malformed config key \"%s\"", m_Path.string().c_str(), key.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sectionIt = m_Merged.find(section);
|
||||
if (sectionIt == m_Merged.end()) {
|
||||
LOG_WARNING("%s: Unknown section \"%s\"", m_Path.string().c_str(), section.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto paramIt = sectionIt->second->find(param);
|
||||
if (paramIt == sectionIt->second->end()) {
|
||||
LOG_WARNING("%s: Unknown section param \"%s\"", m_Path.string().c_str(), key.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return paramIt->second.get();
|
||||
}
|
||||
|
||||
void ConfigFile::SaveToDisk()
|
||||
{
|
||||
boost::property_tree::ini_parser::write_ini(m_Path.string(), m_PTreeOverrides);
|
||||
std::ofstream file(m_Path.string());
|
||||
file << m_Overrides;
|
||||
file.close();
|
||||
}
|
||||
|
||||
void ConfigFile::mergeINI(ini_file::section_map& to, const ini_file::section_map& from)
|
||||
{
|
||||
for (auto& section : from) {
|
||||
for (auto& param : *section.second) {
|
||||
auto& p = to[section.first][param.first];
|
||||
if (!param.second->get_comment().empty()) {
|
||||
p.set_comment(param.second->get_comment());
|
||||
}
|
||||
p.set_value(param.second->get_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<std::pair<std::string, std::string>> ConfigFile::tokenizeKey(std::string key)
|
||||
{
|
||||
std::size_t delimiter = key.find_last_of('.');
|
||||
if (delimiter == std::string::npos) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::string section = key.substr(0, delimiter);
|
||||
if (section.empty()) {
|
||||
return boost::none;
|
||||
}
|
||||
std::string param = key.substr(delimiter + 1);
|
||||
if (param.empty()) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return std::make_pair(section, param);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "Core/DeveloperConsole.h"
|
||||
|
||||
std::map<std::string, std::function<void(std::string)>> DeveloperConsole::m_VariableBindingSetters;
|
||||
std::map<std::string, std::function<std::string()>> DeveloperConsole::m_VariableBindingGetters;
|
||||
@@ -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,229 @@
|
||||
#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.Type = type;
|
||||
field.Offset = fieldOffset;
|
||||
field.Stride = stride;
|
||||
compInfo.FieldsInOrder.push_back(&field);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+224
-105
@@ -9,6 +9,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer)
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_Enabled = config->Get<bool>("Debug.EditorEnabled", false);
|
||||
m_Visible = m_Enabled;
|
||||
m_DefaultEntityDir = boost::filesystem::path("Schema") / boost::filesystem::path("Entities");
|
||||
|
||||
if (!m_Enabled) {
|
||||
return;
|
||||
@@ -44,6 +45,35 @@ void EditorSystem::Update(World* world, double dt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::filesystem::path EditorSystem::openDialog(boost::filesystem::path defaultPath)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
auto absolutePath = bfs::absolute(defaultPath);
|
||||
nfdchar_t* outPath = nullptr;
|
||||
nfdresult_t result = NFD_OpenDialog(NULL, absolutePath.string().c_str(), &outPath);
|
||||
if (result == NFD_ERROR) {
|
||||
LOG_ERROR("NFD Error: %s", NFD_GetError());
|
||||
return bfs::path();
|
||||
}
|
||||
|
||||
return bfs::absolute(outPath);
|
||||
}
|
||||
|
||||
boost::filesystem::path EditorSystem::saveDialog(boost::filesystem::path defaultPath)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
auto absolutePath = bfs::absolute(defaultPath);
|
||||
nfdchar_t* outPath = nullptr;
|
||||
nfdresult_t result = NFD_SaveDialog(NULL, absolutePath.string().c_str(), &outPath);
|
||||
if (result == NFD_ERROR) {
|
||||
LOG_ERROR("NFD Error: %s", NFD_GetError());
|
||||
return bfs::path();
|
||||
}
|
||||
|
||||
return bfs::absolute(outPath);
|
||||
}
|
||||
|
||||
bool EditorSystem::OnInputCommand(const Events::InputCommand& e)
|
||||
{
|
||||
if (e.Command == "ToggleEditor" && e.Value > 0) {
|
||||
@@ -81,17 +111,24 @@ bool EditorSystem::OnMousePress(const Events::MousePress& e)
|
||||
|
||||
bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
|
||||
{
|
||||
if (m_Widget == 0) {
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Selection == EntityID_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (m_Selection == m_Widget) {
|
||||
return false;
|
||||
}
|
||||
// TODO: No widgets for root entity until widgets reside in thier own world,
|
||||
// or the widgets will move relative to the root entity being moved, which is WEEEIRD.
|
||||
if (m_Selection == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
glm::vec3 widgetOrientation = widgetTransform["Orientation"];
|
||||
|
||||
glm::quat totalOrientation = m_Renderer->Camera()->Orientation() * glm::inverse(glm::quat(widgetOrientation));
|
||||
glm::quat totalOrientation = m_Renderer->Camera()->Orientation() * glm::inverse(glm::quat(widgetOrientation));
|
||||
|
||||
int width;
|
||||
int height;
|
||||
@@ -122,9 +159,9 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
|
||||
if (m_WidgetSpace == WidgetSpace::Global) {
|
||||
EntityID parent = m_World->GetParent(m_Selection);
|
||||
glm::quat inverseParentOrientation;
|
||||
if (parent != 0) {
|
||||
//if (parent != 0) {
|
||||
inverseParentOrientation = glm::inverse(RenderQueueFactory::AbsoluteOrientation(m_World, parent));
|
||||
}
|
||||
//}
|
||||
(glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Position"] += inverseParentOrientation * movement;
|
||||
} else if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
@@ -138,12 +175,12 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
|
||||
if (m_WidgetSpace == WidgetSpace::Global) {
|
||||
EntityID parent = m_World->GetParent(m_Selection);
|
||||
glm::quat parentOrientation;
|
||||
if (parent != 0) {
|
||||
parentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, parent);
|
||||
}
|
||||
//if (parent != 0) {
|
||||
// parentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, parent);
|
||||
//}
|
||||
glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"];
|
||||
//glm::quat currentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection);
|
||||
glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation);
|
||||
glm::quat currentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection);
|
||||
//glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation);
|
||||
glm::quat deltaOrientation(finalMovement);
|
||||
selectionOrientation = glm::eulerAngles(glm::inverse(parentOrientation) * (deltaOrientation * currentOrientation));
|
||||
} else if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
@@ -204,9 +241,10 @@ bool EditorSystem::OnPicking(const Events::Picking& e)
|
||||
auto result = e.Pick(pos);
|
||||
EntityID entity = result.Entity;
|
||||
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
|
||||
// ???
|
||||
} else {
|
||||
LOG_INFO("Selected %i", entity);
|
||||
if (entity != 0) {
|
||||
if (entity != EntityID_Invalid) {
|
||||
EntityID parent = m_World->GetParent(entity);
|
||||
if (parent == m_Widget) {
|
||||
m_WidgetCurrentAxis = glm::vec3(
|
||||
@@ -221,11 +259,12 @@ bool EditorSystem::OnPicking(const Events::Picking& e)
|
||||
//widgetTransform["Position"] = (glm::vec3)selectionTransform["Position"];
|
||||
} else {
|
||||
ImGui::SetActiveID(0, nullptr);
|
||||
m_Selection = entity;
|
||||
if (m_WidgetMode == WidgetMode::None) {
|
||||
m_WidgetMode = WidgetMode::Translate;
|
||||
}
|
||||
setWidgetMode(m_WidgetMode);
|
||||
m_Selection = entity;
|
||||
}
|
||||
} else {
|
||||
m_Selection = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,9 +279,9 @@ bool EditorSystem::OnFileDropped(const Events::FileDropped& e)
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorSystem::updateWidget()
|
||||
void EditorSystem::createWidget()
|
||||
{
|
||||
if (m_Widget == 0) {
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
m_Widget = m_World->CreateEntity();
|
||||
m_World->AttachComponent(m_Widget, "Transform");
|
||||
m_WidgetX = m_World->CreateEntity(m_Widget);
|
||||
@@ -269,10 +308,20 @@ void EditorSystem::updateWidget()
|
||||
m_WidgetOrigin = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetOrigin, "Transform");
|
||||
m_World->AttachComponent(m_WidgetOrigin, "Model");
|
||||
setWidgetMode(WidgetMode::Translate);
|
||||
setWidgetMode(WidgetMode::None);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSystem::updateWidget()
|
||||
{
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
return;
|
||||
}
|
||||
if (m_Selection == m_Widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Selection != 0) {
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
glm::vec3 selectionPosition = RenderQueueFactory::AbsolutePosition(m_World, m_Selection);
|
||||
widgetTransform["Position"] = selectionPosition;
|
||||
@@ -284,7 +333,7 @@ void EditorSystem::updateWidget()
|
||||
|
||||
void EditorSystem::setWidgetMode(WidgetMode newMode)
|
||||
{
|
||||
if (m_Widget == 0) {
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,7 +358,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode)
|
||||
m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = true;
|
||||
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = true;
|
||||
}
|
||||
if (m_Selection != 0) {
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection));
|
||||
@@ -321,7 +370,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode)
|
||||
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/ScaleWidgetZ.obj";
|
||||
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = true;
|
||||
m_World->GetComponent(m_WidgetOrigin, "Model")["Resource"] = "Models/ScaleWidgetOrigin.obj";
|
||||
if (m_Selection != 0) {
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection));
|
||||
}
|
||||
@@ -329,7 +378,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode)
|
||||
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/RotationWidgetX.obj";
|
||||
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/RotationWidgetY.obj";
|
||||
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/RotationWidgetZ.obj";
|
||||
if (m_Selection != 0) {
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection));
|
||||
@@ -339,7 +388,6 @@ void EditorSystem::setWidgetMode(WidgetMode newMode)
|
||||
m_WidgetMode = newMode;
|
||||
}
|
||||
|
||||
|
||||
void EditorSystem::setWidgetSpace(WidgetSpace space)
|
||||
{
|
||||
m_WidgetSpace = space;
|
||||
@@ -348,16 +396,23 @@ void EditorSystem::setWidgetSpace(WidgetSpace space)
|
||||
|
||||
void EditorSystem::drawUI(World* world, double dt)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
ImGui::ShowTestWindow();
|
||||
//ImGui::ShowStyleEditor();
|
||||
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
|
||||
if (ImGui::MenuItem("New")) { }
|
||||
if (ImGui::MenuItem("Open", "Ctrl+O")) { }
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) { }
|
||||
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) { }
|
||||
//if (ImGui::MenuItem("New")) { }
|
||||
if (ImGui::MenuItem("Import", "Ctrl+O")) {
|
||||
fileImport(world);
|
||||
}
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) {
|
||||
fileSave(world);
|
||||
}
|
||||
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) {
|
||||
fileSaveAs(world);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Close Editor", "F1")) { }
|
||||
|
||||
@@ -392,7 +447,7 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
|
||||
std::string title = std::string("Components #") + std::to_string(m_Selection) + std::string("###Components");
|
||||
if (ImGui::Begin(title.c_str())) {
|
||||
if (m_Selection != 0) {
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto& pools = world->GetComponentPools();
|
||||
|
||||
std::vector<const char*> componentTypes;
|
||||
@@ -432,16 +487,16 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
}
|
||||
|
||||
auto& component = world->GetComponent(m_Selection, componentType);
|
||||
for (auto& pair : ci.FieldTypes) {
|
||||
const std::string& field = pair.first;
|
||||
const std::string& type = pair.second;
|
||||
for (auto& kv : ci.Fields) {
|
||||
const std::string& fieldName = kv.first;
|
||||
auto& field = kv.second;
|
||||
|
||||
ImGui::PushID(field.c_str());
|
||||
if (type == "Vector") {
|
||||
auto& val = component.Property<glm::vec3>(field);
|
||||
if (field == "Scale") {
|
||||
ImGui::PushID(fieldName.c_str());
|
||||
if (field.Type == "Vector") {
|
||||
auto& val = component.Property<glm::vec3>(fieldName);
|
||||
if (fieldName == "Scale") {
|
||||
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits<float>::max());
|
||||
} else if (field == "Orientation") {
|
||||
} else if (fieldName == "Orientation") {
|
||||
glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi<float>()));
|
||||
if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi<float>())) {
|
||||
val = tempVal;
|
||||
@@ -449,16 +504,16 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
} else {
|
||||
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
|
||||
}
|
||||
} else if (type == "Color") {
|
||||
auto& val = component.Property<glm::vec4>(field);
|
||||
} else if (field.Type == "Color") {
|
||||
auto& val = component.Property<glm::vec4>(fieldName);
|
||||
ImGui::ColorEdit4("", glm::value_ptr(val), true);
|
||||
} else if (type == "string") {
|
||||
std::string& val = component.Property<std::string>(field);
|
||||
} else if (field.Type == "string") {
|
||||
std::string& val = component.Property<std::string>(fieldName);
|
||||
char tempString[1024];
|
||||
memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString)));
|
||||
if (ImGui::InputText("", tempString, sizeof(tempString))) {
|
||||
val = std::string(tempString);
|
||||
LOG_DEBUG("%s::%s changed!", componentType.c_str(), field.c_str());
|
||||
LOG_DEBUG("%s::%s changed!", componentType.c_str(), fieldName.c_str());
|
||||
}
|
||||
// DROP STUFF
|
||||
if (ImGui::IsItemHovered() && !m_LastDroppedFile.empty()) {
|
||||
@@ -466,21 +521,21 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
m_LastDroppedFile = "";
|
||||
}
|
||||
|
||||
} else if (type == "double") {
|
||||
float tempVal = static_cast<float>(component.Property<double>(field));
|
||||
} else if (field.Type == "double") {
|
||||
float tempVal = static_cast<float>(component.Property<double>(fieldName));
|
||||
if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) {
|
||||
component.SetProperty(field, static_cast<double>(tempVal));
|
||||
component.SetProperty(fieldName, static_cast<double>(tempVal));
|
||||
}
|
||||
} else if (type == "bool") {
|
||||
auto& val = component.Property<bool>(field);
|
||||
} else if (field.Type == "bool") {
|
||||
auto& val = component.Property<bool>(fieldName);
|
||||
ImGui::Checkbox("", &val);
|
||||
} else {
|
||||
ImGui::TextDisabled(type.c_str());
|
||||
ImGui::TextDisabled(field.Type.c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(field.c_str());
|
||||
ImGui::Text(fieldName.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("field annotation goes here");
|
||||
}
|
||||
@@ -492,74 +547,93 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (ImGui::Begin("Entitites")) {
|
||||
static EntityID draggingEntity = 0;
|
||||
if (ImGui::Begin("Entities")) {
|
||||
auto entityChildren = world->GetEntityChildren();
|
||||
std::function<void(EntityID)> recurse = [&](EntityID parent) {
|
||||
auto range = entityChildren.equal_range(parent);
|
||||
for (auto it = range.first; it != range.second; it++) {
|
||||
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float width = ImGui::GetContentRegionAvailWidth();
|
||||
ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13));
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
if (m_Selection == it->second) {
|
||||
const ImU32 col = window->Color(ImGuiCol_HeaderActive);
|
||||
window->DrawList->AddRectFilled(bb.Min, bb.Max, col);
|
||||
}
|
||||
ImGuiID id = window->GetID((std::string("#SelectButton") + std::to_string(it->second)).c_str());
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) {
|
||||
m_Selection = it->second;
|
||||
}
|
||||
if (held) {
|
||||
ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0);
|
||||
if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) {
|
||||
if (draggingEntity == 0) {
|
||||
draggingEntity = it->second;
|
||||
LOG_DEBUG("Started drag of entity %i", draggingEntity);
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0));
|
||||
ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::Text("#%i", draggingEntity);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once);
|
||||
if (ImGui::TreeNode((std::string("#") + std::to_string(it->second)).c_str())) {
|
||||
if (draggingEntity != 0 && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) {
|
||||
LOG_DEBUG("Changed parent of %i to %i", draggingEntity, it->second);
|
||||
changeParent(draggingEntity, it->second);
|
||||
draggingEntity = 0;
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem("item context menu")) {
|
||||
if (ImGui::Button("Add")) {
|
||||
EntityID entity = world->CreateEntity(it->second);
|
||||
world->AttachComponent(entity, "Transform");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete")) {
|
||||
world->DeleteEntity(it->second);
|
||||
ImGui::CloseCurrentPopup();
|
||||
if (m_Selection == it->second) {
|
||||
m_Selection = 0;
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
if (createEntityNode(world, it->second)) {
|
||||
recurse(it->second);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
};
|
||||
recurse(0);
|
||||
recurse(EntityID_Invalid);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool EditorSystem::createEntityNode(World* world, EntityID entity)
|
||||
{
|
||||
// HACK: Don't show the widget entities in the entity tree
|
||||
if (entity == m_Widget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float width = ImGui::GetContentRegionAvailWidth();
|
||||
ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13));
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
if (m_Selection == entity) {
|
||||
const ImU32 col = window->Color(ImGuiCol_HeaderActive);
|
||||
window->DrawList->AddRectFilled(bb.Min, bb.Max, col);
|
||||
}
|
||||
ImGuiID id = window->GetID((std::string("#SelectButton") + std::to_string(entity)).c_str());
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) {
|
||||
m_Selection = entity;
|
||||
}
|
||||
if (held) {
|
||||
ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0);
|
||||
if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) {
|
||||
if (m_UIDraggingEntity == EntityID_Invalid) {
|
||||
m_UIDraggingEntity = entity;
|
||||
LOG_DEBUG("Started drag of entity %i", m_UIDraggingEntity);
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0));
|
||||
ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::Text("#%i", m_UIDraggingEntity);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once);
|
||||
std::string nodeTitle;
|
||||
const std::string& entityName = world->GetName(entity);
|
||||
if (!entityName.empty()) {
|
||||
nodeTitle = entityName;
|
||||
} else {
|
||||
nodeTitle = std::string("#") + std::to_string(entity);
|
||||
}
|
||||
if (ImGui::TreeNode(nodeTitle.c_str())) {
|
||||
if (m_UIDraggingEntity != EntityID_Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) {
|
||||
LOG_DEBUG("Changed parent of %i to %i", m_UIDraggingEntity, entity);
|
||||
changeParent(m_UIDraggingEntity, entity);
|
||||
m_UIDraggingEntity = EntityID_Invalid;
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem("item context menu")) {
|
||||
if (ImGui::Button("Add")) {
|
||||
EntityID newEntity = world->CreateEntity(entity);
|
||||
world->AttachComponent(newEntity, "Transform");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete")) {
|
||||
world->DeleteEntity(entity);
|
||||
ImGui::CloseCurrentPopup();
|
||||
if (!world->ValidEntity(m_Selection)) {
|
||||
m_Selection = EntityID_Invalid;
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorSystem::createDeleteButton(std::string componentType)
|
||||
{
|
||||
float width = ImGui::GetContentRegionAvailWidth();
|
||||
@@ -594,3 +668,48 @@ void EditorSystem::changeParent(EntityID entity, EntityID newParent)
|
||||
|
||||
m_World->SetParent(entity, newParent);
|
||||
}
|
||||
|
||||
void EditorSystem::fileImport(World* world)
|
||||
{
|
||||
m_CurrentFile = openDialog(m_DefaultEntityDir);
|
||||
auto file = ResourceManager::Load<EntityFile>(m_CurrentFile.string());
|
||||
EntityFilePreprocessor fpp(file);
|
||||
fpp.RegisterComponents(world);
|
||||
EntityFileParser fp(file);
|
||||
fp.MergeEntities(world);
|
||||
createWidget();
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
void EditorSystem::fileSave(World* world)
|
||||
{
|
||||
if (boost::filesystem::exists(m_CurrentFile)) {
|
||||
// HACK: Delete the widgets so they don't appear in the saved file
|
||||
world->DeleteEntity(m_Widget);
|
||||
m_Widget = EntityID_Invalid;
|
||||
|
||||
EntityFileWriter writer(m_CurrentFile.string());
|
||||
writer.WriteWorld(world);
|
||||
|
||||
createWidget();
|
||||
} else {
|
||||
fileSaveAs(world);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSystem::fileSaveAs(World* world)
|
||||
{
|
||||
auto filePath = saveDialog(m_DefaultEntityDir);
|
||||
if (filePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: Delete the widgets so they don't appear in the saved file
|
||||
world->DeleteEntity(m_Widget);
|
||||
m_Widget = EntityID_Invalid;
|
||||
|
||||
EntityFileWriter writer(filePath.string());
|
||||
writer.WriteWorld(world);
|
||||
|
||||
createWidget();
|
||||
}
|
||||
|
||||
@@ -17,10 +17,15 @@ InputProxy::~InputProxy()
|
||||
void InputProxy::LoadBindings(std::string file)
|
||||
{
|
||||
auto config = ResourceManager::Load<ConfigFile>(file);
|
||||
for (auto& origin : config->GetAll<std::string>("Bindings")) {
|
||||
auto section = config->GetSection("Bindings");
|
||||
if (section == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& param : *section) {
|
||||
Events::BindOrigin e;
|
||||
e.Origin = origin.first;
|
||||
e.Command = origin.second;
|
||||
e.Origin = param.first;
|
||||
e.Command = param.second->get_value();
|
||||
e.Value = 1.f;
|
||||
if (!e.Command.empty()) {
|
||||
char prefix = e.Command.at(0);
|
||||
|
||||
@@ -27,16 +27,16 @@ glm::vec3 RenderQueueFactory::AbsolutePosition(World* world, EntityID entity)
|
||||
{
|
||||
glm::vec3 position;
|
||||
|
||||
do {
|
||||
while (entity != EntityID_Invalid) {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
EntityID parent = world->GetParent(entity);
|
||||
if (parent != 0) {
|
||||
//if (parent != EntityID_Invalid) {
|
||||
position += AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"];
|
||||
} else {
|
||||
position += (glm::vec3)transform["Position"];
|
||||
}
|
||||
//} else {
|
||||
// position += (glm::vec3)transform["Position"];
|
||||
//}
|
||||
entity = parent;
|
||||
} while (entity != 0);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
@@ -45,11 +45,11 @@ glm::quat RenderQueueFactory::AbsoluteOrientation(World* world, EntityID entity)
|
||||
{
|
||||
glm::quat orientation;
|
||||
|
||||
do {
|
||||
while (entity != EntityID_Invalid) {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation;
|
||||
entity = world->GetParent(entity);
|
||||
} while (entity != 0);
|
||||
}
|
||||
|
||||
return orientation;
|
||||
}
|
||||
@@ -58,11 +58,11 @@ glm::vec3 RenderQueueFactory::AbsoluteScale(World* world, EntityID entity)
|
||||
{
|
||||
glm::vec3 scale(1.f);
|
||||
|
||||
do {
|
||||
while (entity != EntityID_Invalid) {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
scale *= (glm::vec3)transform["Scale"];
|
||||
entity = world->GetParent(entity);
|
||||
} while (entity != 0);
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
Regular → Executable
+16
-2
@@ -2,18 +2,28 @@
|
||||
#include "Collision/TriggerSystem.h"
|
||||
#include "Collision/CollisionSystem.h"
|
||||
#include "Game/HealthSystem.h"
|
||||
#include "Core/DeveloperConsole.h"
|
||||
#include "Core/EntityFileWriter.h"
|
||||
|
||||
Game::Game(int argc, char* argv[])
|
||||
{
|
||||
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
|
||||
ResourceManager::RegisterType<Model>("Model");
|
||||
ResourceManager::RegisterType<Texture>("Texture");
|
||||
ResourceManager::RegisterType<EntityXMLFile>("EntityXMLFile");
|
||||
ResourceManager::RegisterType<ShaderProgram>("ShaderProgram");
|
||||
ResourceManager::RegisterType<EntityFile>("EntityFile");
|
||||
|
||||
m_Config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get<int>("Debug.LogLevel", 1));
|
||||
|
||||
DeveloperConsole::MergeConfig("Config.ini");
|
||||
DeveloperConsole::BindVariable("Debug.LogLevel", LOG_LEVEL);
|
||||
std::string testBind = "Carlito";
|
||||
DeveloperConsole::BindVariable("Test", testBind);
|
||||
while (true) {
|
||||
DeveloperConsole::Consume(std::cin);
|
||||
}
|
||||
|
||||
// Create the core event broker
|
||||
m_EventBroker = new EventBroker();
|
||||
|
||||
@@ -48,7 +58,11 @@ Game::Game(int argc, char* argv[])
|
||||
m_World = new World();
|
||||
std::string mapToLoad = m_Config->Get<std::string>("Debug.LoadMap", "");
|
||||
if (!mapToLoad.empty()) {
|
||||
ResourceManager::Load<EntityXMLFile>(mapToLoad)->PopulateWorld(m_World);
|
||||
auto file = ResourceManager::Load<EntityFile>(mapToLoad);
|
||||
EntityFilePreprocessor fpp(file);
|
||||
fpp.RegisterComponents(m_World);
|
||||
EntityFileParser fp(file);
|
||||
fp.MergeEntities(m_World);
|
||||
}
|
||||
|
||||
// Create system pipeline
|
||||
|
||||
+17
-10
@@ -19,22 +19,29 @@ void HealthSystem::UpdateComponent(World *world, ComponentWrapper &health, doubl
|
||||
for (size_t i = m_DeltaHealthVector.size(); i > 0; i--)
|
||||
{
|
||||
auto deltaHP = m_DeltaHealthVector[i - 1];
|
||||
//if we have a healthchange for the current player, then apply it
|
||||
if (std::get<0>(deltaHP) == player.EntityID) {
|
||||
//if we have a healthchange for the current player and health is greater than 0, then apply it
|
||||
if (std::get<0>(deltaHP) == player.EntityID && (double)health["Health"] > 0.0f) {
|
||||
//get the deltaHP value from the tuple and make sure you dont get more than maxHealth
|
||||
double newHealth = std::min((double)health["Health"] + (double)std::get<1>(deltaHP), maxHealth);
|
||||
health["Health"] = newHealth;
|
||||
m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + i - 1);
|
||||
//check if health is <= 0
|
||||
if ((double)health["Health"] <= 0.0f) {
|
||||
//publish death event
|
||||
Events::PlayerDeath e;
|
||||
e.PlayerID = player.EntityID;
|
||||
m_EventBroker->Publish(e);
|
||||
//clear the remaining hpDeltas for the dead player
|
||||
for (size_t j = m_DeltaHealthVector.size(); j > 0; j--)
|
||||
{
|
||||
if (std::get<0>(m_DeltaHealthVector[j - 1]) == player.EntityID)
|
||||
m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + j - 1);
|
||||
}
|
||||
//break the loop if the player is dead
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if health is <= 0
|
||||
if ((double)health["Health"] <= 0.0f) {
|
||||
//publish death event
|
||||
Events::PlayerDeath e;
|
||||
e.PlayerID = player.EntityID;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e)
|
||||
|
||||
Reference in New Issue
Block a user