diff --git a/README.md b/README.md index 7c5f2268..003cb2be 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/assets b/assets index 673d4a4e..6cbf2365 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 673d4a4e4c5a3f5bc9fedf82234e8f8751f63a44 +Subproject commit 6cbf2365d49e6280750ea3bcd0f9c271779e6f15 diff --git a/deps b/deps index 1ae6ba5b..bf83f099 160000 --- a/deps +++ b/deps @@ -1 +1 @@ -Subproject commit 1ae6ba5b1297ed71b560aee211b9f0007ba52547 +Subproject commit bf83f099ba16f0a87f9bebe8cfc5fd4e59fee805 diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h index ee5f1a11..985a057e 100644 --- a/include/Engine/Core/ComponentInfo.h +++ b/include/Engine/Core/ComponentInfo.h @@ -12,9 +12,17 @@ struct ComponentInfo unsigned int Stride = 0; }; + struct Field_t + { + std::string Name; + std::string Type; + unsigned int Offset; + unsigned int Stride; + }; + std::string Name; - std::unordered_map FieldTypes; - std::unordered_map FieldOffsets; + std::unordered_map Fields; + std::vector FieldsInOrder; Meta_t Meta; std::shared_ptr Defaults = nullptr; }; diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 56ae943f..922b3d79 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -21,7 +21,7 @@ struct ComponentWrapper template T& Property(std::string name) { - unsigned int offset = Info.FieldOffsets.at(name); + unsigned int offset = Info.Fields.at(name).Offset; return *reinterpret_cast(&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); } diff --git a/include/Engine/Core/EPlayerDamage.h b/include/Engine/Core/EPlayerDamage.h new file mode 100644 index 00000000..87ad67aa --- /dev/null +++ b/include/Engine/Core/EPlayerDamage.h @@ -0,0 +1,20 @@ +#ifndef EPlayerDamage_h__ +#define EPlayerDamage_h__ + +#include "EventBroker.h" +#include "../Core/Entity.h" + +namespace Events +{ + +struct PlayerDamage : Event +{ + double DamageAmount; + EntityID PlayerDamagedID; + //optional TypeOfDamage + std::string TypeOfDamage; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EPlayerDeath.h b/include/Engine/Core/EPlayerDeath.h new file mode 100644 index 00000000..00ede5ed --- /dev/null +++ b/include/Engine/Core/EPlayerDeath.h @@ -0,0 +1,20 @@ +#ifndef EPlayerDeath_h__ +#define EPlayerDeath_h__ + +#include "EventBroker.h" +#include "../Core/Entity.h" + +namespace Events +{ + +struct PlayerDeath : Event +{ + //KilledBy,KilledByWhat is optional for now. It might be used later in the playerlog-system + EntityID KilledBy; + EntityID PlayerID; + std::string KilledByWhat; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EPlayerHealthPickup.h b/include/Engine/Core/EPlayerHealthPickup.h new file mode 100644 index 00000000..f3158f92 --- /dev/null +++ b/include/Engine/Core/EPlayerHealthPickup.h @@ -0,0 +1,18 @@ +#ifndef EPlayerHealthPickup_h__ +#define EPlayerHealthPickup_h__ + +#include "EventBroker.h" +#include "../Core/Entity.h" + +namespace Events +{ + +struct PlayerHealthPickup : Event +{ + double HealthAmount; + EntityID PlayerHealedID; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/Entity.h b/include/Engine/Core/Entity.h index c6a475e3..1f40b9b7 100644 --- a/include/Engine/Core/Entity.h +++ b/include/Engine/Core/Entity.h @@ -2,5 +2,6 @@ #define Entity_h__ typedef unsigned int EntityID; +const static unsigned int EntityID_Invalid = -1; #endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h new file mode 100644 index 00000000..27115264 --- /dev/null +++ b/include/Engine/Core/EntityFile.h @@ -0,0 +1,293 @@ +#ifndef EntityFile_h__ +#define EntityFile_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 OnStartComponentCallback; + void SetStartComponentCallback(OnStartComponentCallback c) { m_OnStartComponentCallback = c; } + // @param EntityID Entity + // @param std::string Component name + // @param std::string Field name + // @param std::map Field attribute names and values + typedef std::function&)> 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 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 m_StateStack; + unsigned int m_NextEntityID = 0; + std::stack m_EntityStack; + std::string m_CurrentComponent; + std::string m_CurrentField; + std::map 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& 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 m_ComponentInfo; + //std::vector m_EntityReferences; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFileParser.h b/include/Engine/Core/EntityFileParser.h new file mode 100644 index 00000000..d46b508b --- /dev/null +++ b/include/Engine/Core/EntityFileParser.h @@ -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 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& attributes); + void onFieldData(EntityID entity, const std::string& componentType, const std::string& fieldName, const char* fieldData); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFilePreprocessor.h b/include/Engine/Core/EntityFilePreprocessor.h new file mode 100644 index 00000000..3c3998b1 --- /dev/null +++ b/include/Engine/Core/EntityFilePreprocessor.h @@ -0,0 +1,36 @@ +#ifndef EntityFilePreprocessor_h__ +#define EntityFilePreprocessor_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 m_ComponentCounts; + std::map m_ComponentInfo; + + void onStartComponent(EntityID entity, std::string type); + void parseComponentInfo(); + void parseDefaults(); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFileWriter.h b/include/Engine/Core/EntityFileWriter.h new file mode 100644 index 00000000..b75bc145 --- /dev/null +++ b/include/Engine/Core/EntityFileWriter.h @@ -0,0 +1,39 @@ +#ifndef EntityFileWriter_h__ +#define EntityFileWriter_h__ + +#include +#include +#include +#include +#include + +#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(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 \ No newline at end of file diff --git a/include/Engine/Core/EntityXMLFile.h b/include/Engine/Core/EntityXMLFile.h deleted file mode 100644 index 6fa71dad..00000000 --- a/include/Engine/Core/EntityXMLFile.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef EntityXMLFile_h__ -#define EntityXMLFile_h__ - -#include - -#include "../Common.h" -#include "../GLM.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ResourceManager.h" -#include "Entity.h" -#include "ComponentInfo.h" -class World; - -class EntityPreprocessorXMLErrorHandler : public xercesc::DOMErrorHandler -{ -public: - bool handleError(const xercesc::DOMError &e) override - { - char* message = xercesc::XMLString::transcode(e.getMessage()); - std::cerr << "Preprocessor DOMError: " << message << std::endl; - xercesc::XMLString::release(&message); - return false; - } -}; - -class EntityParserXMLErrorHandler : public xercesc::ErrorHandler -{ -public: - void warning(const xercesc::SAXParseException& e) override - { - reportParseException("Warning", e); - } - void error(const xercesc::SAXParseException& e) override - { - reportParseException("Error", e); - } - void fatalError(const xercesc::SAXParseException& e) override - { - reportParseException("FATAL ERROR", e); - } - void resetErrors() override { } - -private: - void reportParseException(std::string type, const xercesc::SAXParseException& e) - { - char* message = xercesc::XMLString::transcode(e.getMessage()); - char* systemID = xercesc::XMLString::transcode(e.getSystemId()); - std::cerr << systemID << ":" << e.getLineNumber() << ":" << e.getColumnNumber() << std::endl; - std::cerr << type << ": " << message << std::endl; - xercesc::XMLString::release(&systemID); - xercesc::XMLString::release(&message); - } -}; - -class XSTR -{ -public: - XSTR(const XMLCh* const xmlString) - { - m_AsChar = xercesc::XMLString::transcode(xmlString); - } - - XSTR(const char* normalString) - { - m_AsXMLCh = xercesc::XMLString::transcode(normalString); - } - - ~XSTR() - { - if (m_AsChar != nullptr) { - xercesc::XMLString::release(&m_AsChar); - } - if (m_AsXMLCh != nullptr) { - xercesc::XMLString::release(&m_AsXMLCh); - } - } - - operator const char*() const { return m_AsChar; } - operator const XMLCh*() const { return m_AsXMLCh; } - -private: - char* m_AsChar = nullptr; - XMLCh* m_AsXMLCh = nullptr; -}; - -class EntityXMLFile : public Resource -{ - friend class ResourceManager; - -private: - EntityXMLFile(std::string path); - -public: - ~EntityXMLFile(); - - void PopulateWorld(World* world); - -private: - static unsigned int InstanceCount; - - std::string m_EntityFile; - xercesc::XMLGrammarPool* m_GrammarPool = nullptr; - EntityParserXMLErrorHandler* m_ErrorHandler = nullptr; - xercesc::XercesDOMParser* m_DOMParser = nullptr; - xercesc::DOMDocument* m_DOMDocument = nullptr; - std::map m_ComponentInfo; - - // Preprocesses the entity file to insert include-by-copy child entities - // TODO: Make this work in memory instead of saving to file - void preprocess(std::string inPath, std::string outPath); - - void parseComponentInfo(); - void parseDefaults(); - void predictComponentAllocation(); - void parseEntityGraph(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 \ No newline at end of file diff --git a/include/Engine/Core/SystemPipeline.h b/include/Engine/Core/SystemPipeline.h index 4aaa5163..d6a6b371 100644 --- a/include/Engine/Core/SystemPipeline.h +++ b/include/Engine/Core/SystemPipeline.h @@ -14,21 +14,28 @@ public: { } ~SystemPipeline() { - for (auto& pair : m_Systems) { - delete pair.second; + for (UnorderedSystems& group : m_OrderedSystemGroups) { + for (auto& pair : group.Systems) { + delete pair.second; + } } } template - void AddSystem(Arguments... args) + //All systems with orderlevel 0 will be updated first, then 1, 2, etc. + void AddSystem(int updateOrderLevel, Arguments... args) { + if (updateOrderLevel + 1 > m_OrderedSystemGroups.size()) { + m_OrderedSystemGroups.resize(updateOrderLevel + 1); + } + UnorderedSystems& group = m_OrderedSystemGroups[updateOrderLevel]; System* system = new T(m_EventBroker, args...); - m_Systems[typeid(T).name()] = system; + group.Systems[typeid(T).name()] = system; if (std::is_base_of::value) { PureSystem* pureSystem = static_cast(system); if (!pureSystem->m_ComponentType.empty()) { - m_PureSystems[pureSystem->m_ComponentType].push_back(pureSystem); + group.PureSystems[pureSystem->m_ComponentType].push_back(pureSystem); } else { LOG_ERROR("Failed to add pure system \"%s\": Missing component type!", typeid(T).name()); } @@ -36,41 +43,47 @@ public: if (std::is_base_of::value) { ImpureSystem* impureSystem = static_cast(system); - m_ImpureSystems.push_back(impureSystem); + group.ImpureSystems.push_back(impureSystem); } } void Update(World* world, double dt) { - // Process events - for (auto& pair : m_Systems) { - m_EventBroker->Process(pair.first); - } - - // Update - for (auto& pair : m_PureSystems) { - const std::string& componentName = pair.first; - auto& systems = pair.second; - const ComponentPool* pool = world->GetComponents(componentName); - if (pool == nullptr) { - continue; + for (UnorderedSystems& group : m_OrderedSystemGroups) { + // Process events + for (auto& pair : group.Systems) { + m_EventBroker->Process(pair.first); } - for (auto& component : *pool) { - for (auto& system : systems) { - system->UpdateComponent(world, component, dt); + + // Update + for (auto& pair : group.PureSystems) { + const std::string& componentName = pair.first; + auto& systems = pair.second; + const ComponentPool* pool = world->GetComponents(componentName); + if (pool == nullptr) { + continue; + } + for (auto& component : *pool) { + for (auto& system : systems) { + system->UpdateComponent(world, component, dt); + } } } - } - for (auto& system : m_ImpureSystems) { - system->Update(world, dt); + for (auto& system : group.ImpureSystems) { + system->Update(world, dt); + } } } private: EventBroker* m_EventBroker; - std::map m_Systems; - std::map> m_PureSystems; - std::vector m_ImpureSystems; + struct UnorderedSystems + { + std::map Systems; + std::map> PureSystems; + std::vector ImpureSystems; + }; + std::vector m_OrderedSystemGroups; }; #endif \ No newline at end of file diff --git a/include/Engine/Core/Transform.h b/include/Engine/Core/Transform.h new file mode 100644 index 00000000..474a7bdb --- /dev/null +++ b/include/Engine/Core/Transform.h @@ -0,0 +1,17 @@ +#ifndef Transform_h__ +#define Transform_h__ + +#include "../GLM.h" +#include "World.h" + +namespace Transform +{ + +glm::vec3 AbsolutePosition(World* world, EntityID entity); +glm::quat AbsoluteOrientation(World* world, EntityID entity); +glm::vec3 AbsoluteScale(World* world, EntityID entity); +glm::mat4 ModelMatrix(EntityID entity, World* world); + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/Util/XercesString.h b/include/Engine/Core/Util/XercesString.h new file mode 100644 index 00000000..b0b6fa76 --- /dev/null +++ b/include/Engine/Core/Util/XercesString.h @@ -0,0 +1,46 @@ +#ifndef Util_XercesString_h__ +#define Util_XercesString_h__ + +#include +#include +#include + +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 diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 92378816..0a121728 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -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& GetComponentPools() const { return m_ComponentPools; } // Get the entity children map const std::unordered_multimap& 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 m_EntityParents; // TODO: This should be a more effective structure std::unordered_multimap m_EntityChildren; std::unordered_map m_ComponentPools; + std::unordered_map m_EntityNames; EntityID generateEntityID(); }; diff --git a/include/Engine/Editor/EditorSystem.h b/include/Engine/Editor/EditorSystem.h index 779b7f97..05e106d9 100644 --- a/include/Engine/Editor/EditorSystem.h +++ b/include/Engine/Editor/EditorSystem.h @@ -1,6 +1,7 @@ #include #include #include +#include #include "../Core/System.h" #include "../Core/EMousePress.h" #include "../Core/EMouseRelease.h" @@ -8,9 +9,11 @@ #include "../Core/ConfigFile.h" #include "../Input/EInputCommand.h" #include "../Rendering/IRenderer.h" -#include "../Rendering/EPicking.h" +#include "../Core/Transform.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 { @@ -22,9 +25,12 @@ public: private: IRenderer* m_Renderer; World* m_World = nullptr; + Camera* m_Camera = nullptr; bool m_Enabled; bool m_Visible; + boost::filesystem::path m_DefaultEntityDir; + boost::filesystem::path m_CurrentFile; std::vector m_PickingQueue; enum class WidgetMode @@ -41,22 +47,27 @@ 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; + glm::vec3 m_WidgetPickingPosition = glm::vec3(0); - 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 m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); EventRelay m_EMouseRelease; @@ -65,15 +76,19 @@ private: bool OnMousePress(const Events::MousePress& e); EventRelay m_EMouseMove; bool OnMouseMove(const Events::MouseMove& e); - EventRelay m_EPicking; - bool OnPicking(const Events::Picking& e); EventRelay m_EFileDropped; bool OnFileDropped(const Events::FileDropped& e); - + + void Picking(); + 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); }; \ No newline at end of file diff --git a/include/Engine/GUI/Button.h b/include/Engine/GUI/Button.h index 91bd9782..80845cb7 100644 --- a/include/Engine/GUI/Button.h +++ b/include/Engine/GUI/Button.h @@ -40,7 +40,7 @@ public: m_TexturePressed = resourceName; } - void Draw(RenderQueueCollection& rq) override + void Draw(RenderScene& rq) override { if (m_Texture == nullptr && !m_TextureReleased.empty()) { SetTexture(m_TextureReleased); diff --git a/include/Engine/GUI/Frame.h b/include/Engine/GUI/Frame.h index 416b8117..4c5f2eb9 100644 --- a/include/Engine/GUI/Frame.h +++ b/include/Engine/GUI/Frame.h @@ -212,7 +212,7 @@ public: virtual void Update(double dt) { } - void DrawLayered(RenderQueueCollection& rq) + void DrawLayered(RenderScene& rq) { if (this->Hidden()) return; @@ -232,7 +232,7 @@ public: } } - virtual void Draw(RenderQueueCollection& rq) { } + virtual void Draw(RenderScene& rq) { } protected: ::EventBroker* m_EventBroker; diff --git a/include/Engine/GUI/TextureFrame.h b/include/Engine/GUI/TextureFrame.h index f02285ca..2c6d34dd 100644 --- a/include/Engine/GUI/TextureFrame.h +++ b/include/Engine/GUI/TextureFrame.h @@ -16,7 +16,7 @@ public: void EnableScissor() { m_ScissorEnabled = true; } void DisableScissor() { m_ScissorEnabled = false; } - void Draw(RenderQueueCollection& rq) override + void Draw(RenderScene& rq) override { if (m_Texture == nullptr) return; diff --git a/include/Engine/Network/Client.h b/include/Engine/Network/Client.h index 95888b1f..a5488210 100644 --- a/include/Engine/Network/Client.h +++ b/include/Engine/Network/Client.h @@ -23,7 +23,6 @@ public: ~Client(); void Start(World* world, EventBroker* eventBroker) override; void Update() override; - void Close(); private: // Assio UDP logic boost::asio::ip::udp::endpoint m_ReceiverEndpoint; @@ -32,7 +31,7 @@ private: // Sending message to server logic int bytesRead = -1; - char readBuf[1024] = { 0 }; + char readBuf[INPUTSIZE] = { 0 }; int snapshotInterval = 33; std::clock_t previousSnapshotMessage = std::clock(); @@ -49,7 +48,6 @@ private: // Network logic PlayerDefinition m_PlayerDefinitions[MAXCONNECTIONS]; SnapshotDefinitions m_NextSnapshot; - bool m_ThreadIsRunning = true; double m_DurationOfPingTime; std::clock_t m_StartPingTime; // Use to check if we should send disconnect message @@ -59,7 +57,7 @@ private: // Private member functions void readFromServer(); void sendSnapshotToServer(); - int receive(char* data, size_t length); + int receive(char* data, size_t length); void send(Packet& packet); void connect(); void disconnect(); @@ -67,6 +65,7 @@ private: void moveMessageHead(char*& data, size_t& length, size_t stepSize); void parseMessageType(Packet& packet); void parseEventMessage(Packet& packet); + void updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType); void parseConnect(Packet& packet); void parsePing(); void parseServerPing(); diff --git a/include/Engine/Network/Network.h b/include/Engine/Network/Network.h index 0f7baefe..cb86b941 100644 --- a/include/Engine/Network/Network.h +++ b/include/Engine/Network/Network.h @@ -6,7 +6,7 @@ #include "Network/Packet.h" #define MAXCONNECTIONS 8 -#define INPUTSIZE 128 +#define INPUTSIZE 4097 class Network { diff --git a/include/Engine/Network/Packet.h b/include/Engine/Network/Packet.h index daf39962..112ebe34 100644 --- a/include/Engine/Network/Packet.h +++ b/include/Engine/Network/Packet.h @@ -14,15 +14,17 @@ public: Packet(MessageType type, unsigned int& packetID); // Used to create packet from already existing data buffer. Packet(char* data, const int sizeOfPacket); - ~Packet(); + void Init(MessageType type, unsigned int& packetID); + // Add primitive types like int, float, char... template void WritePrimitive(T val) { // Check if we are trying to add more than the package can fit. if (m_MaxPacketSize < m_Offset + sizeof(T)) { - LOG_WARNING("Packet AddPrimitive(): You are trying to add more than we have allocated for!"); + LOG_WARNING("Packet AddPrimitive(): You are trying to add more than we have allocated for! New size is %i bytes\n", m_MaxPacketSize*2); + resizeData(); } memcpy(m_Data + m_Offset, &val, sizeof(T)); m_Offset += sizeof(T); @@ -41,7 +43,7 @@ public: return returnValue; } // Add a string to the message - void WriteString(std::string str); + void WriteString(const std::string& str); // Add data to the message void WriteData(char* data, int sizeOfData); // Pops the first element as if it was a string. @@ -50,12 +52,15 @@ public: int Size() { return m_Offset; }; char* Data() { return m_Data; }; + unsigned int DataReadSize() { return m_ReturnDataOffset; } + unsigned int MaxSize() { return m_MaxPacketSize; } private: char* m_Data; unsigned int m_ReturnDataOffset = 0; int m_Offset = 0; - unsigned int m_MaxPacketSize = 128; + unsigned int m_MaxPacketSize = 512; + void resizeData(); }; #endif \ No newline at end of file diff --git a/include/Engine/Network/Server.h b/include/Engine/Network/Server.h index 5c1ac1fb..893eed0b 100644 --- a/include/Engine/Network/Server.h +++ b/include/Engine/Network/Server.h @@ -20,8 +20,6 @@ public: ~Server(); void Start(World* m_world, EventBroker *eventBroker) override; void Update() override; - void Close(); - private: // UDP logic boost::asio::ip::udp::endpoint m_ReceiverEndpoint; @@ -30,7 +28,7 @@ private: PlayerDefinition m_PlayerDefinitions[MAXCONNECTIONS]; // Sending messages to client logic - char readBuffer[1024] = { 0 }; + char readBuffer[INPUTSIZE] = { 0 }; int bytesRead = 0; // time for previouse message std::clock_t previousePingMessage = std::clock(); @@ -55,9 +53,6 @@ private: unsigned int m_PacketID; unsigned int m_PreviousPacketID; unsigned int m_SendPacketID; - - // Close logic - bool m_ThreadIsRunning = true; // Private member functions int receive(char* data, size_t length); diff --git a/include/Engine/Rendering/Camera.h b/include/Engine/Rendering/Camera.h index 660dfd49..2b863448 100644 --- a/include/Engine/Rendering/Camera.h +++ b/include/Engine/Rendering/Camera.h @@ -26,13 +26,12 @@ public: glm::quat Orientation() const { return m_Orientation; } void SetOrientation(glm::quat val); - /*float Pitch() const { return m_Pitch; } - void Pitch(float val); - float Yaw() const { return m_Yaw; } - void Yaw(float val);*/ - glm::mat4 ProjectionMatrix() const { return m_ProjectionMatrix; } + void SetProjectionMatrix(glm::mat4 val); + glm::mat4 ViewMatrix() const { return m_ViewMatrix; } + void SetViewMatrix(glm::mat4 val); + float AspectRatio() const { return m_AspectRatio; } void SetAspectRatio(float val); @@ -46,11 +45,10 @@ public: float FarClip() const { return m_FarClip; } void SetFarClip(float val); - + void UpdateViewMatrix(); + void UpdateProjectionMatrix(); private: - void UpdateViewMatrix(); - void UpdateProjectionMatrix(); glm::vec3 m_Position; glm::quat m_Orientation; diff --git a/include/Engine/Rendering/DebugCameraInputController.h b/include/Engine/Rendering/DebugCameraInputController.h index 614b071c..4d74e288 100644 --- a/include/Engine/Rendering/DebugCameraInputController.h +++ b/include/Engine/Rendering/DebugCameraInputController.h @@ -9,6 +9,9 @@ public: : FirstPersonInputController(eventBroker, playerID) { } + void SetPosition(const glm::vec3 position) { m_Position = position; } + void SetOrientation(const glm::quat orientation) { m_Orientation = orientation; } + const glm::vec3 Position() const { return m_Position; } void SetBaseSpeed(float speed) { m_BaseSpeed = speed; } diff --git a/include/Engine/Rendering/DrawScenePass.h b/include/Engine/Rendering/DrawScenePass.h index 782c5a49..ca2463cf 100644 --- a/include/Engine/Rendering/DrawScenePass.h +++ b/include/Engine/Rendering/DrawScenePass.h @@ -17,7 +17,7 @@ public: void InitializeFrameBuffers(); void InitializeShaderPrograms(); - void Draw(RenderQueueCollection& rq); + void Draw(RenderScene& scene); //Getters @@ -25,12 +25,18 @@ public: private: void GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const; + static bool DepthSort(const std::shared_ptr &i, const std::shared_ptr &j) + { + return (i->Depth < j->Depth); + }; + Texture* m_WhiteTexture; const IRenderer* m_Renderer; ShaderProgram* m_BasicForwardProgram; + }; #endif \ No newline at end of file diff --git a/include/Engine/Rendering/DummyRenderer.h b/include/Engine/Rendering/DummyRenderer.h index 56790fa1..176c5834 100644 --- a/include/Engine/Rendering/DummyRenderer.h +++ b/include/Engine/Rendering/DummyRenderer.h @@ -9,7 +9,7 @@ class DummyRenderer : public IRenderer { public: virtual void Initialize() override; - virtual void Draw(RenderQueueCollection& rq) override; + virtual void Draw(RenderFrame& rq) override; }; #endif \ No newline at end of file diff --git a/include/Engine/Rendering/EPicking.h b/include/Engine/Rendering/EPicking.h deleted file mode 100644 index 85c4b629..00000000 --- a/include/Engine/Rendering/EPicking.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef Events_Picking_h__ -#define Events_Picking_h__ - -#include "../OpenGL.h" -#include "../GLM.h" - -#include "../Core/EventBroker.h" -#include "Util/ScreenCoords.h" -#include "FrameBuffer.h" -#include "../Core/Entity.h" -#include "Util/UnorderedMapVec2.h" - -namespace Events -{ - -/** Thrown Every frame, use functions to pick*/ -struct Picking : Event -{ -public: - Picking(FrameBuffer* pickingBuffer, GLuint* depthBuffer, glm::mat4 projectionMatrix, glm::mat4 viewMatrix, Rectangle resolution, const std::unordered_map* pickingColorsToEntity) - : PickingBuffer(pickingBuffer) - , DepthBuffer(depthBuffer) - , ProjectionMatrix(projectionMatrix) - , ViewMatrix(viewMatrix) - , Resolution(resolution) - , PickingColorsToEntity(pickingColorsToEntity) - { } - - - - struct PickData - { - //Picked Entity - EntityID Entity; - //World position of the "pick" - glm::vec3 Position; - // Depth - float Depth; - }; - - PickData Pick(glm::vec2 screenCoord) const - { - PickData pickData; - - // Invert screen y coordinate - screenCoord.y = Resolution.Height - screenCoord.y; - ScreenCoords::PixelData data = ScreenCoords::ToPixelData(screenCoord, PickingBuffer, *DepthBuffer); - pickData.Depth = data.Depth; - - auto it = PickingColorsToEntity->find(glm::vec2(data.Color[0], data.Color[1])); - if (it != PickingColorsToEntity->end()) { - pickData.Entity = it->second; - } else { - pickData.Entity = 0; - } - pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, Resolution, ProjectionMatrix, ViewMatrix); - - return pickData; - } - - -private: - FrameBuffer* PickingBuffer; - GLuint* DepthBuffer; - const glm::mat4 ProjectionMatrix; - const glm::mat4 ViewMatrix; - const Rectangle Resolution; - const std::unordered_map* PickingColorsToEntity; - -}; - -} - -#endif diff --git a/include/Engine/Rendering/ESetCamera.h b/include/Engine/Rendering/ESetCamera.h new file mode 100644 index 00000000..650f3b12 --- /dev/null +++ b/include/Engine/Rendering/ESetCamera.h @@ -0,0 +1,23 @@ +#ifndef Events_SetCamera_h__ +#define Events_SetCamera_h__ + +#include "../Core/EventBroker.h" +#include "../Core/Entity.h" +#include + +namespace Events +{ + +struct SetCamera : Event +{ +public: + SetCamera() { }; + std::string Name; + +private: + +}; + +} + +#endif diff --git a/include/Engine/Rendering/IRenderer.h b/include/Engine/Rendering/IRenderer.h index 5dce14c7..773da732 100644 --- a/include/Engine/Rendering/IRenderer.h +++ b/include/Engine/Rendering/IRenderer.h @@ -10,6 +10,15 @@ #include "RenderQueue.h" #include "Model.h" +struct PickData +{ + EntityID Entity; + glm::vec3 Position; //World position + float Depth; + ::Camera* Camera; + const ::World* World; +}; + class IRenderer { public: @@ -20,19 +29,19 @@ public: void SetFullscreen(bool fullscreen) { m_Fullscreen = fullscreen; } bool VSYNC() const { return m_VSYNC; } void SetVSYNC(bool vsync) { m_VSYNC = vsync; } - ::Camera* Camera() const { return m_Camera; } - void SetCamera(::Camera* camera) - { - if (camera == nullptr) { - m_Camera = m_DefaultCamera; - } else { - m_Camera = camera; - } - } - + ::Camera* Camera() const { return m_Camera; } + void SetCamera(::Camera* camera) + { + if (camera == nullptr) { + m_Camera = m_DefaultCamera; + } else { + m_Camera = camera; + } + } virtual void Initialize() = 0; virtual void Update(double dt) = 0; - virtual void Draw(RenderQueueCollection& rq) = 0; + virtual void Draw(RenderFrame& rq) = 0; + virtual PickData Pick(glm::vec2 screenCord) = 0; protected: Rectangle m_Resolution = Rectangle::Rectangle(1280, 720); @@ -40,9 +49,9 @@ protected: bool m_VSYNC = false; int m_GLVersion[2]; std::string m_GLVendor; - ::Camera* m_DefaultCamera; - ::Camera* m_Camera = nullptr; GLFWwindow* m_Window = nullptr; + ::Camera* m_DefaultCamera; + ::Camera* m_Camera = nullptr; }; #endif // Renderer_h__ diff --git a/include/Engine/Rendering/ModelJob.h b/include/Engine/Rendering/ModelJob.h new file mode 100644 index 00000000..d598766f --- /dev/null +++ b/include/Engine/Rendering/ModelJob.h @@ -0,0 +1,55 @@ +#ifndef ModelJob_h__ +#define ModelJob_h__ + +#include + +#include "../Common.h" +#include "../GLM.h" +#include "../Core/ComponentWrapper.h" +#include "Texture.h" +#include "Model.h" +#include "RenderJob.h" +#include "../Core/ResourceManager.h" +#include "Camera.h" +#include "../Core/World.h" + +struct ModelJob : RenderJob +{ + ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::Model::MaterialGroup texGroup, ComponentWrapper modelComponent, World* world) + : RenderJob() + { + Model = model; + TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0; + DiffuseTexture = texGroup.Texture.get(); + NormalTexture = texGroup.NormalMap.get(); + SpecularTexture = texGroup.SpecularMap.get(); + StartIndex = texGroup.StartIndex; + EndIndex = texGroup.EndIndex; + Matrix = matrix; + Color = modelComponent["Color"]; + Entity = modelComponent.EntityID; + World = world; + }; + + unsigned int TextureID; + unsigned int ShaderID; + + EntityID Entity; + glm::mat4 Matrix; + const Texture* DiffuseTexture; + const Texture* NormalTexture; + const Texture* SpecularTexture; + float Shininess = 0.f; + glm::vec4 Color; + const ::Model* Model = nullptr; + unsigned int StartIndex = 0; + unsigned int EndIndex = 0; + const World* World; + + void CalculateHash() override + { + Hash = TextureID; + } +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/PickingPass.h b/include/Engine/Rendering/PickingPass.h index e1bc42db..523c7d65 100644 --- a/include/Engine/Rendering/PickingPass.h +++ b/include/Engine/Rendering/PickingPass.h @@ -5,9 +5,9 @@ #include "PickingPassState.h" #include "FrameBuffer.h" #include "ShaderProgram.h" -#include "Util/UnorderedMapVec2.h" +#include "Util/UnorderedMapiVec2.h" #include "../Core/EventBroker.h" -#include "EPicking.h" +#include "../Core/World.h" class PickingPass { @@ -18,16 +18,19 @@ public: void InitializeFrameBuffers(); void InitializeShaderPrograms(); - void Draw(RenderQueueCollection& rq); + void Draw(RenderScene& scene); + void ClearPicking(); //Getters const ShaderProgram& PickingProgram() const { return *m_PickingProgram; } - const std::unordered_map& PickingColorsToEntity() const { return m_PickingColorsToEntity; } + //const std::unordered_map& PickingColorsToEntity() const { return m_PickingColorsToEntity; } GLuint PickingTexture() const { return m_PickingTexture; } GLuint DepthBuffer() const { return m_DepthBuffer; } const FrameBuffer& PickingBuffer() const { return m_PickingBuffer; } + + PickData Pick(glm::vec2 screenCoord); private: void GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const; @@ -37,13 +40,24 @@ private: const IRenderer* m_Renderer; ShaderProgram* m_PickingProgram; + Camera* m_Camera; - std::unordered_map m_PickingColorsToEntity; + struct PickingInfo + { + EntityID Entity; + const ::World* World; + ::Camera* Camera; + }; + + std::unordered_map m_PickingColorsToEntity; GLuint m_PickingTexture; GLuint m_DepthBuffer; FrameBuffer m_PickingBuffer; + + int m_ColorCounter[2]; + std::map, glm::ivec2> m_EntityColors; }; #endif \ No newline at end of file diff --git a/include/Engine/Rendering/RawModel.h b/include/Engine/Rendering/RawModel.h index 82619c86..5117f953 100644 --- a/include/Engine/Rendering/RawModel.h +++ b/include/Engine/Rendering/RawModel.h @@ -46,6 +46,7 @@ public: struct MaterialGroup { float Shininess; + float Transparency; std::string TexturePath; std::shared_ptr<::Texture> Texture; std::string NormalMapPath; diff --git a/include/Engine/Rendering/RenderJob.h b/include/Engine/Rendering/RenderJob.h new file mode 100644 index 00000000..4afe0386 --- /dev/null +++ b/include/Engine/Rendering/RenderJob.h @@ -0,0 +1,32 @@ +#ifndef RenderJob_h__ +#define RenderJob_h__ + +#include + +#include "../Common.h" +#include "../GLM.h" +#include "../Core/ComponentWrapper.h" +#include "RenderQueue.h" + + +struct RenderJob +{ + friend class RenderQueue; + +public: + + float Depth; + +protected: + uint64_t Hash; + + virtual void CalculateHash() = 0; + + bool operator<(const RenderJob& rhs) + { + return this->Hash < rhs.Hash; + } + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/RenderQueue.h b/include/Engine/Rendering/RenderQueue.h index 2942c743..dba34c3e 100644 --- a/include/Engine/Rendering/RenderQueue.h +++ b/include/Engine/Rendering/RenderQueue.h @@ -8,61 +8,13 @@ #include "../GLM.h" #include "../Core/Util/Rectangle.h" #include "../Core/Entity.h" +#include "Camera.h" +#include "RenderJob.h" +#include "ModelJob.h" -class Model; -class Skeleton; -class Texture; -class RenderQueue; -//TODO: Render: Remove obsolete RenderJobs and fix standard values on variables. - -struct RenderJob -{ - friend class RenderQueue; - - float Depth; - -protected: - uint64_t Hash; - - virtual void CalculateHash() = 0; - - bool operator<(const RenderJob& rhs) - { - return this->Hash < rhs.Hash; - } -}; - -struct ModelJob : RenderJob -{ - unsigned int ShaderID = 0; - unsigned int TextureID = 0; - - //TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this - EntityID Entity; - - glm::mat4 ModelMatrix; - const Texture* DiffuseTexture; - const Texture* NormalTexture; - const Texture* SpecularTexture; - float Shininess = 0.f; - glm::vec4 Color; - const Model* Model = nullptr; - unsigned int StartIndex = 0; - unsigned int EndIndex = 0; - - // Animation - Skeleton* Skeleton = nullptr; - bool NoRootMotion = true; - std::string AnimationName; - double AnimationTime = 0; - - void CalculateHash() override - { - Hash = TextureID; - } -}; +/* struct SpriteJob : RenderJob { unsigned int ShaderID = 0; @@ -93,62 +45,53 @@ struct PointLightJob : RenderJob Hash = 0; } }; +*/ -class RenderQueue +struct RenderScene { -public: - template - void Add(T &job) - { - job.CalculateHash(); - Jobs.push_back(std::shared_ptr(new T(job))); - m_Size++; - } - - void Sort() - { - Jobs.sort(); - } + ::Camera* Camera; + std::list> ForwardJobs; + std::list> LightJobs; + Rectangle Viewport; void Clear() { - Jobs.clear(); - m_Size = 0; + ForwardJobs.clear(); + LightJobs.clear(); } - - int Size() const { return m_Size; } - std::list>::const_iterator begin() - { - return Jobs.begin(); - } - - std::list>::const_iterator end() - { - return Jobs.end(); - } - - std::list> Jobs; - -private: - int m_Size = 0; }; -struct RenderQueueCollection +struct RenderFrame { - RenderQueue Forward; - RenderQueue Lights; +public: - void Clear() - { - Forward.Clear(); - Lights.Clear(); - } + void Add(RenderScene &scene) + { + RenderScenes.push_back(std::shared_ptr(new RenderScene(scene))); + m_Size++; + } - void Sort() - { - Forward.Sort(); - Lights.Sort(); - } + void Clear() + { + RenderScenes.clear(); + m_Size = 0; + } + + int Size() const { return m_Size; } + std::list>::const_iterator begin() + { + return RenderScenes.begin(); + } + + std::list>::const_iterator end() + { + return RenderScenes.end(); + } + + std::list> RenderScenes; + +private: + int m_Size = 0; }; #endif \ No newline at end of file diff --git a/include/Engine/Rendering/RenderQueueFactory.h b/include/Engine/Rendering/RenderQueueFactory.h deleted file mode 100644 index be2c55ca..00000000 --- a/include/Engine/Rendering/RenderQueueFactory.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef RenderQueueFactory_h__ -#define RenderQueueFactory_h__ - -#include "../Core/World.h" -#include "RenderQueue.h" -#include "../Core/ResourceManager.h" -#include "Model.h" -#include "../GLM.h" - -class RenderQueueFactory -{ -public: - RenderQueueFactory(); - void Update(World* world); - - RenderQueueCollection RenderQueues() const { return m_RenderQueues; } - - static glm::vec3 AbsolutePosition(World* world, EntityID entity); - static glm::quat AbsoluteOrientation(World* world, EntityID entity); - static glm::vec3 AbsoluteScale(World* world, EntityID entity); - -private: - RenderQueueCollection m_RenderQueues; - - void FillModels(World* world, RenderQueue* renderQueue); - void FillLights(World* world, RenderQueue* renderQueue); - - glm::mat4 ModelMatrix(World* world, EntityID entity); -}; - -#endif \ No newline at end of file diff --git a/include/Engine/Rendering/RenderSystem.h b/include/Engine/Rendering/RenderSystem.h new file mode 100644 index 00000000..efe3b88c --- /dev/null +++ b/include/Engine/Rendering/RenderSystem.h @@ -0,0 +1,53 @@ +#ifndef RenderSystem_h__ +#define RenderSystem_h__ + +#include "../Core/System.h" +#include "RenderQueue.h" +#include "../GLM.h" +#include "../OpenGL.h" +#include "../Core/ResourceManager.h" +#include "ESetCamera.h" +#include "Model.h" +#include "../Core/EKeyDown.h" +#include "../Input/EInputCommand.h" +#include "Camera.h" +#include "ModelJob.h" +#include "Renderer.h" +#include "../Core/Transform.h" +#include "DebugCameraInputController.h" + +class RenderSystem : public ImpureSystem +{ +public: + RenderSystem(EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame); + ~RenderSystem(); + + virtual void Update(World* world, double dt) override; + +private: + World* m_World = nullptr; + const IRenderer* m_Renderer; + + RenderFrame* m_RenderFrame; + bool m_SwitchCamera = false; + Camera* m_Camera; + DebugCameraInputController* m_DebugCameraInputController; + + std::list m_CameraComponents; + + EventRelay m_ESetCamera; + bool OnSetCamera(const Events::SetCamera &event); + EntityID m_CurrentCamera = EntityID_Invalid; + + void switchCamera(EntityID entity); + + void updateCamera(World* world, double dt); + void updateProjectionMatrix(ComponentWrapper& cameraComponent); + + void fillModels(std::list>& jobs, World* world); + + EventRelay m_EInputCommand; + bool OnInputCommand(const Events::InputCommand& e); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/Renderer.h b/include/Engine/Rendering/Renderer.h index 19b48e1a..6dd7e250 100644 --- a/include/Engine/Rendering/Renderer.h +++ b/include/Engine/Rendering/Renderer.h @@ -12,45 +12,31 @@ #include "../Core/World.h" #include "PickingPass.h" #include "DrawScenePass.h" -#include "DebugCameraInputController.h" - - -#define TILE_SIZE 16 -#define NUM_LIGHTS 3 - - -enum lightType -{ - Point, - Spot, - Directional, - Area -}; - #include "../Core/EventBroker.h" -#include "EPicking.h" #include "ImGuiRenderPass.h" +#include "Camera.h" class Renderer : public IRenderer { public: - Renderer(EventBroker* eventBroker) + Renderer(EventBroker* eventBroker, World* world) : m_EventBroker(eventBroker) + , m_World(world) { } virtual void Initialize() override; virtual void Update(double dt) override; - virtual void Draw(RenderQueueCollection& rq) override; + virtual void Draw(RenderFrame& frame) override; + + virtual PickData Pick(glm::vec2 screenCoord) override; private: //----------------------Variables----------------------// EventBroker* m_EventBroker; - - std::shared_ptr> m_DebugCameraInputController; + World* m_World; Texture* m_ErrorTexture; Texture* m_WhiteTexture; - float m_CameraMoveSpeed; Model* m_ScreenQuad; Model* m_UnitQuad; @@ -71,56 +57,10 @@ private: //void PickingPass(RenderQueueCollection& rq); void DrawScreenQuad(GLuint textureToDraw); - //----------------------Forward+-----------------------// - void CalculateFrustum(); - void CullLights(); - //Frustum - struct Plane { - glm::vec3 Normal; - float d; - }; - struct Frustum { - Plane Planes[4]; - }; - Frustum m_Frustums[80*45]; //TODO: Renderer: Make this change with resolution - - //Lights - void TEMPCreateLights(); - //TODO: Renderer: Add Directionllights, spotlights and area lights to this as type. - struct PointLight { - glm::vec4 Position = glm::vec4(0.f); - glm::vec4 Color = glm::vec4(1.f); - float Radius = 5.f; - float Intensity = 0.8f; - float Falloff = 0.3f; - float Padding = 1337; - }; - PointLight m_PointLights[NUM_LIGHTS]; - - struct LightGrid { - int Amount; - int Start; - glm::vec2 Padding; - }; - LightGrid m_LightGrid[80*45]; - - int m_LightOffset = 0; - - int m_LightIndex[80*45*200]; - - //-------------------------SSBO------------------------// - GLuint m_FrustumSSBO = 0; - GLuint m_LightSSBO = 1; - GLuint m_LightGridSSBO = 2; - GLuint m_LightOffsetSSBO = 3; - GLuint m_LightIndexSSBO = 4; - void GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type); //--------------------ShaderPrograms-------------------// ShaderProgram* m_BasicForwardProgram; ShaderProgram* m_DrawScreenQuadProgram; - ShaderProgram* m_CalculateFrustumProgram; - ShaderProgram* m_LightCullProgram; }; diff --git a/include/Engine/Rendering/Util/UnorderedMapiVec2.h b/include/Engine/Rendering/Util/UnorderedMapiVec2.h new file mode 100644 index 00000000..9797046e --- /dev/null +++ b/include/Engine/Rendering/Util/UnorderedMapiVec2.h @@ -0,0 +1,24 @@ +#pragma once +#ifndef UnorderedMapiVec2_h__ +#define UnorderedMapiVec2_h__ + +#include +#include +#include + +template<> +struct std::hash +{ + inline std::size_t operator()(const glm::ivec2 &v) const + { + return boost::hash()(v.x) ^ boost::hash()(v.y); + } + + inline bool operator()(const glm::ivec2& a, const glm::ivec2& b)const + { + return a.x == b.x && a.y == b.y; + } + +}; + +#endif \ No newline at end of file diff --git a/include/Game/Game.h b/include/Game/Game.h index 33dc88a6..dfe1cff6 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -8,16 +8,18 @@ #include "Core/InputManager.h" #include "GUI/Frame.h" #include "Core/World.h" -#include "Rendering/RenderQueueFactory.h" #include "Input/InputProxy.h" #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 "Rendering/RenderSystem.h" +#include "Core/EntityFileParser.h" // Network #include @@ -45,7 +47,7 @@ private: GUI::Frame* m_FrameStack; World* m_World; SystemPipeline* m_SystemPipeline; - RenderQueueFactory* m_RenderQueueFactory; + RenderFrame* m_RenderFrame; // Network variables boost::thread m_NetworkThread; diff --git a/include/Game/HealthSystem.h b/include/Game/HealthSystem.h new file mode 100644 index 00000000..a836e797 --- /dev/null +++ b/include/Game/HealthSystem.h @@ -0,0 +1,36 @@ +#ifndef HealthSystem_h__ +#define HealthSystem_h__ + +#include +#include + +#include "Common.h" +#include "Core/System.h" +#include "Core\EPlayerDamage.h"; +#include "Core\EPlayerHealthPickup.h"; +#include "Core\EPlayerDeath.h"; + +#include +#include + +class HealthSystem : public PureSystem +{ +public: + HealthSystem(EventBroker* eventBroker); + + //updatecomponent + virtual void UpdateComponent(World* world, ComponentWrapper& health, double dt) override; + +private: + //methods which will take care of specific events + EventRelay m_EPlayerDamage; + bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e); + EventRelay m_EPlayerHealthPickup; + bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e); + + //vector which will keep track of health changes + std::vector> m_DeltaHealthVector; + +}; + +#endif \ No newline at end of file diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index fd04fd39..15861716 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -6,6 +6,8 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/Camera.xml b/resources/Schema/Components/Camera.xml new file mode 100644 index 00000000..92225dde --- /dev/null +++ b/resources/Schema/Components/Camera.xml @@ -0,0 +1,6 @@ + + cam + 60.0 + 0.01 + 5000 + \ No newline at end of file diff --git a/resources/Schema/Components/Camera.xsd b/resources/Schema/Components/Camera.xsd new file mode 100644 index 00000000..4f02deb0 --- /dev/null +++ b/resources/Schema/Components/Camera.xsd @@ -0,0 +1,19 @@ + + + + + + + + It's a camera thingy! + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Health.xml b/resources/Schema/Components/Health.xml new file mode 100644 index 00000000..143a91d1 --- /dev/null +++ b/resources/Schema/Components/Health.xml @@ -0,0 +1,4 @@ + + 100 + 100 + \ No newline at end of file diff --git a/resources/Schema/Components/Health.xsd b/resources/Schema/Components/Health.xsd new file mode 100644 index 00000000..0da80c1f --- /dev/null +++ b/resources/Schema/Components/Health.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Entities/Empty.xml b/resources/Schema/Entities/Empty.xml new file mode 100644 index 00000000..70581847 --- /dev/null +++ b/resources/Schema/Entities/Empty.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resources/Schema/Entities/RaptorCopter.xml b/resources/Schema/Entities/RaptorCopter.xml new file mode 100644 index 00000000..0ee62368 --- /dev/null +++ b/resources/Schema/Entities/RaptorCopter.xml @@ -0,0 +1,68 @@ + + + + + + + + + + Models/Core/UnitRaptor.obj + + + + + + + + + + + + 20 + + + + + + + + + + + + Models/Core/UnitCylinder.obj + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Entities/RenderingWorld.xml b/resources/Schema/Entities/RenderingWorld.xml new file mode 100644 index 00000000..86a3090a --- /dev/null +++ b/resources/Schema/Entities/RenderingWorld.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + Models/Camera.obj + + + MainCamera + + + + + + + + + + Models/Camera.obj + + + ActionCamera + + + + + + + + + + + Models/Core/UnitPlane.obj + + + + + + + + + + An error + + + + + \ No newline at end of file diff --git a/resources/Schema/Entities/Test.xml b/resources/Schema/Entities/Test.xml index 528c7e95..d2838ae9 100644 --- a/resources/Schema/Entities/Test.xml +++ b/resources/Schema/Entities/Test.xml @@ -9,114 +9,19 @@ Models/DummyScene.obj + - - - - - - - - Models/ScaleWidget.obj - - - - - - - - - - - Models/RotationWidget.obj - - - - - - - + + + + Models/Camera.obj + - - - - - - - - - Models/Core/UnitRaptor.obj - - - - - - - - - - - - 20 - - - - - - - - - - - - - Models/Core/UnitCube.obj - - - - - - - - - - - - - Models/Core/UnitCube.obj - - - - - - - - - - \ No newline at end of file diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 92f7dc31..6562f3ed 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -15,6 +15,7 @@ + @@ -23,11 +24,19 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/Shaders/BasicForward.frag.glsl b/resources/Shaders/BasicForward.frag.glsl index 274ec8d7..dc04f59f 100644 --- a/resources/Shaders/BasicForward.frag.glsl +++ b/resources/Shaders/BasicForward.frag.glsl @@ -1,8 +1,5 @@ #version 430 -uniform mat4 M; -uniform mat4 V; -uniform mat4 P; uniform vec4 Color; uniform sampler2D texture0; diff --git a/resources/Shaders/BasicForward.vert.glsl b/resources/Shaders/BasicForward.vert.glsl index 20ab9051..96f081f4 100644 --- a/resources/Shaders/BasicForward.vert.glsl +++ b/resources/Shaders/BasicForward.vert.glsl @@ -25,7 +25,7 @@ out VertexData{ void main() { - gl_Position = P*V*M * vec4(Position, 1.0); + gl_Position = P * V * M * vec4(Position, 1.0); Output.Position = Position; Output.TextureCoordinate = TextureCoords; diff --git a/resources/Shaders/Picking.frag.glsl b/resources/Shaders/Picking.frag.glsl index 8c95ead3..59f761f9 100644 --- a/resources/Shaders/Picking.frag.glsl +++ b/resources/Shaders/Picking.frag.glsl @@ -1,8 +1,5 @@ #version 430 -uniform mat4 M; -uniform mat4 V; -uniform mat4 P; uniform vec2 PickingColor; in VertexData{ diff --git a/resources/Shaders/Picking.vert.glsl b/resources/Shaders/Picking.vert.glsl index 47c0ecd7..b2857cea 100644 --- a/resources/Shaders/Picking.vert.glsl +++ b/resources/Shaders/Picking.vert.glsl @@ -22,7 +22,7 @@ out VertexData{ void main() { - gl_Position = P*V*M * vec4(Position, 1.0); + gl_Position = P * V* M * vec4(Position, 1.0); Output.Position = Position; } \ No newline at end of file diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt index 5d43db8b..5d74d7d7 100644 --- a/src/Engine/CMakeLists.txt +++ b/src/Engine/CMakeLists.txt @@ -90,12 +90,27 @@ 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/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} diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 7abc63ea..c0b2c658 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -8,196 +8,196 @@ namespace Collision { - //note: this one hasnt been delta adjusted like RayVsAABB has - bool RayAABBIntr(const Ray& ray, const AABB& box) - { - glm::vec3 w = 75.0f * ray.Direction(); - glm::vec3 v = glm::abs(w); - glm::vec3 c = ray.Origin() - box.Center() + w; - glm::vec3 half = box.HalfSize(); +//note: this one hasnt been delta adjusted like RayVsAABB has +bool RayAABBIntr(const Ray& ray, const AABB& box) +{ + glm::vec3 w = 75.0f * ray.Direction(); + glm::vec3 v = glm::abs(w); + glm::vec3 c = ray.Origin() - box.Center() + w; + glm::vec3 half = box.HalfSize(); - if (abs(c.x) > v.x + half.x) { - return false; - } - if (abs(c.y) > v.y + half.y) { - return false; - } - if (abs(c.z) > v.z + half.z) { - return false; - } - - if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) { - return false; - } - if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) { - return false; - } - return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); + if (abs(c.x) > v.x + half.x) { + return false; } - - bool RayVsAABB(const Ray& ray, const AABB& box) - { - float dummy; - return RayVsAABB(ray, box, dummy); + if (abs(c.y) > v.y + half.y) { + return false; } - - bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) - { - glm::vec3 invdir = 1.0f / ray.Direction(); - glm::vec3 origin = ray.Origin(); - - float t1 = (box.MinCorner().x - origin.x)*invdir.x; - float t2 = (box.MaxCorner().x - origin.x)*invdir.x; - float t3 = (box.MinCorner().y - origin.y)*invdir.y; - float t4 = (box.MaxCorner().y - origin.y)*invdir.y; - float t5 = (box.MinCorner().z - origin.z)*invdir.z; - float t6 = (box.MaxCorner().z - origin.z)*invdir.z; - - float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); - float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); - - //if (tmax < 0 || tmin > tmax) - //if tmin,tmax are almost the same (i.e. hitting exactly in the corner) then tmin might be slightly - //greater than tmax becuase of floating-precision problems. fixed by adding a small delta to tmax - if (tmax < 0 || tmin>(tmax + 0.0001f)) - return false; - - outDistance = (tmin > 0) ? tmin : tmax; - return true; - } - - bool AABBVsAABB(const AABB& a, const AABB& b) - { - const glm::vec3& aCenter = a.Center(); - const glm::vec3& bCenter = b.Center(); - const glm::vec3& aHSize = a.HalfSize(); - const glm::vec3& bHSize = b.HalfSize(); - //Test will probably exit because of the X and Z axes more often, so test them first. - if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) { - return false; - } - if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) { - return false; - } - return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); - } - - bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation) - { - minimumTranslation = glm::vec3(0, 0, 0); - const glm::vec3& aMax = a.MaxCorner(); - const glm::vec3& bMax = b.MaxCorner(); - const glm::vec3& aMin = a.MinCorner(); - const glm::vec3& bMin = b.MinCorner(); - const glm::vec3& bSize = b.Size(); - const glm::vec3& aSize = a.Size(); - float minOffset = INFINITY; - float off; - auto axisesIntersecting = glm::tvec3(false, false, false); - for (int i = 0; i < 3; ++i) { - off = bMax[i] - aMin[i]; - if (off > 0 && off < bSize[i] + aSize[i]) { - if (off < minOffset) { - minimumTranslation = glm::vec3(); - minimumTranslation[i] = minOffset = off; - } - axisesIntersecting[i] = true; - } - off = aMax[i] - bMin[i]; - if (off > 0 && off < bSize[i] + aSize[i]) { - if (off < minOffset) { - minOffset = off; - minimumTranslation = glm::vec3(); - minimumTranslation[i] = -off; - } - axisesIntersecting[i] = true; - } - } - return glm::all(axisesIntersecting); - } - - bool RayVsModel(const Ray& ray, - const std::vector& modelVertices, - const std::vector& modelIndices) - { - for (int i = 0; i < modelIndices.size(); ++i) { - glm::vec3 v0 = modelVertices[modelIndices[i]].Position; - glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 - glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin() - v0; - glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction(), e2); - float DetInv = glm::dot(e1, DxE2); - if (std::abs(DetInv) < FLT_EPSILON) { - continue; - } - DetInv = 1.0f / DetInv; - float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction(), MxE1) * DetInv; - //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem - if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) { - continue; - } - //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. - if (0 <= glm::dot(e2, MxE1) * DetInv) { - return true; - } - } + if (abs(c.z) > v.z + half.z) { return false; } - bool RayVsModel(const Ray& ray, - const std::vector& modelVertices, - const std::vector& modelIndices, - float& outDistance, - float& outUCoord, - float& outVCoord) - { - outDistance = INFINITY; - bool hit = false; - for (int i = 0; i < modelIndices.size(); ++i) { - glm::vec3 v0 = modelVertices[modelIndices[i]].Position; - glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 - glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin() - v0; - glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);//pVec - float DetInv = glm::dot(e1, DxE2); - if (std::abs(DetInv) < FLT_EPSILON) { - continue; - } - DetInv = 1.0f / DetInv; - float dist = glm::dot(e2, MxE1) * DetInv; - if (dist >= outDistance) { - continue; - } - float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction(), MxE1) * DetInv; + if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) { + return false; + } + if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) { + return false; + } + return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); +} - //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem - //If u and v are positive, u+v <= 1, dist is positive, and less than closest. - if (0 <= (u + 0.001f) && 0 <= (v + 0.001f) && u + v <= 1 && 0 <= dist) { - outDistance = dist; - outUCoord = u; - outVCoord = v; - hit = true; +bool RayVsAABB(const Ray& ray, const AABB& box) +{ + float dummy; + return RayVsAABB(ray, box, dummy); +} + +bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) +{ + glm::vec3 invdir = 1.0f / ray.Direction(); + glm::vec3 origin = ray.Origin(); + + float t1 = (box.MinCorner().x - origin.x)*invdir.x; + float t2 = (box.MaxCorner().x - origin.x)*invdir.x; + float t3 = (box.MinCorner().y - origin.y)*invdir.y; + float t4 = (box.MaxCorner().y - origin.y)*invdir.y; + float t5 = (box.MinCorner().z - origin.z)*invdir.z; + float t6 = (box.MaxCorner().z - origin.z)*invdir.z; + + float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); + float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); + + //if (tmax < 0 || tmin > tmax) + //if tmin,tmax are almost the same (i.e. hitting exactly in the corner) then tmin might be slightly + //greater than tmax becuase of floating-precision problems. fixed by adding a small delta to tmax + if (tmax < 0 || tmin>(tmax + 0.0001f)) + return false; + + outDistance = (tmin > 0) ? tmin : tmax; + return true; +} + +bool AABBVsAABB(const AABB& a, const AABB& b) +{ + const glm::vec3& aCenter = a.Center(); + const glm::vec3& bCenter = b.Center(); + const glm::vec3& aHSize = a.HalfSize(); + const glm::vec3& bHSize = b.HalfSize(); + //Test will probably exit because of the X and Z axes more often, so test them first. + if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) { + return false; + } + if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) { + return false; + } + return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); +} + +bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation) +{ + minimumTranslation = glm::vec3(0, 0, 0); + const glm::vec3& aMax = a.MaxCorner(); + const glm::vec3& bMax = b.MaxCorner(); + const glm::vec3& aMin = a.MinCorner(); + const glm::vec3& bMin = b.MinCorner(); + const glm::vec3& bSize = b.Size(); + const glm::vec3& aSize = a.Size(); + float minOffset = INFINITY; + float off; + auto axisesIntersecting = glm::tvec3(false, false, false); + for (int i = 0; i < 3; ++i) { + off = bMax[i] - aMin[i]; + if (off > 0 && off < bSize[i] + aSize[i]) { + if (off < minOffset) { + minimumTranslation = glm::vec3(); + minimumTranslation[i] = minOffset = off; } + axisesIntersecting[i] = true; + } + off = aMax[i] - bMin[i]; + if (off > 0 && off < bSize[i] + aSize[i]) { + if (off < minOffset) { + minOffset = off; + minimumTranslation = glm::vec3(); + minimumTranslation[i] = -off; + } + axisesIntersecting[i] = true; } - return hit; } + return glm::all(axisesIntersecting); +} - bool RayVsModel(const Ray& ray, - const std::vector& modelVertices, - const std::vector& modelIndices, - glm::vec3& outHitPosition) - { - float u; - float v; - float dist; - bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); - outHitPosition = ray.Origin() + dist * ray.Direction(); - return hit; +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices) +{ + for (int i = 0; i < modelIndices.size(); ++i) { + glm::vec3 v0 = modelVertices[modelIndices[i]].Position; + glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 + glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 + glm::vec3 m = ray.Origin() - v0; + glm::vec3 MxE1 = glm::cross(m, e1); + glm::vec3 DxE2 = glm::cross(ray.Direction(), e2); + float DetInv = glm::dot(e1, DxE2); + if (std::abs(DetInv) < FLT_EPSILON) { + continue; + } + DetInv = 1.0f / DetInv; + float u = glm::dot(m, DxE2) * DetInv; + float v = glm::dot(ray.Direction(), MxE1) * DetInv; + //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem + if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) { + continue; + } + //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. + if (0 <= glm::dot(e2, MxE1) * DetInv) { + return true; + } } + return false; +} + +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + float& outDistance, + float& outUCoord, + float& outVCoord) +{ + outDistance = INFINITY; + bool hit = false; + for (int i = 0; i < modelIndices.size(); ++i) { + glm::vec3 v0 = modelVertices[modelIndices[i]].Position; + glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 + glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 + glm::vec3 m = ray.Origin() - v0; + glm::vec3 MxE1 = glm::cross(m, e1); + glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);//pVec + float DetInv = glm::dot(e1, DxE2); + if (std::abs(DetInv) < FLT_EPSILON) { + continue; + } + DetInv = 1.0f / DetInv; + float dist = glm::dot(e2, MxE1) * DetInv; + if (dist >= outDistance) { + continue; + } + float u = glm::dot(m, DxE2) * DetInv; + float v = glm::dot(ray.Direction(), MxE1) * DetInv; + + //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem + //If u and v are positive, u+v <= 1, dist is positive, and less than closest. + if (0 <= (u + 0.001f) && 0 <= (v + 0.001f) && u + v <= 1 && 0 <= dist) { + outDistance = dist; + outUCoord = u; + outVCoord = v; + hit = true; + } + } + return hit; +} + +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + glm::vec3& outHitPosition) +{ + float u; + float v; + float dist; + bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); + outHitPosition = ray.Origin() + dist * ray.Direction(); + return hit; +} bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon) { diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index dec33a20..69929c6d 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -4,8 +4,6 @@ void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, double dt) { - //TODO: Update CollisionSystem system after PlayerSystem. - //Right now, cAABB is a component attached to any entity that should be collideable. AABB thisBox; if (!Collision::GetEntityBox(world, cAABB, thisBox)) { diff --git a/src/Engine/Core/EntityFile.cpp b/src/Engine/Core/EntityFile.cpp new file mode 100644 index 00000000..1e381df5 --- /dev/null +++ b/src/Engine/Core/EntityFile.cpp @@ -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 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& attributes) +{ + if (field.Type == "Vector") { + glm::vec3 vec; + vec.x = boost::lexical_cast(attributes.at("X")); + vec.y = boost::lexical_cast(attributes.at("Y")); + vec.z = boost::lexical_cast(attributes.at("Z")); + memcpy(outData, reinterpret_cast(&vec), field.Stride); + } else if (field.Type == "Color") { + glm::vec4 vec; + vec.r = boost::lexical_cast(attributes.at("R")); + vec.g = boost::lexical_cast(attributes.at("G")); + vec.b = boost::lexical_cast(attributes.at("B")); + vec.a = boost::lexical_cast(attributes.at("A")); + memcpy(outData, reinterpret_cast(&vec), field.Stride); + } else if (field.Type == "Quaternion") { + glm::quat q; + q.x = boost::lexical_cast(attributes.at("X")); + q.y = boost::lexical_cast(attributes.at("Y")); + q.z = boost::lexical_cast(attributes.at("Z")); + q.w = boost::lexical_cast(attributes.at("W")); + memcpy(outData, reinterpret_cast(&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(valueData); + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "float") { + float value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "double") { + double value = boost::lexical_cast(valueData); + memcpy(outData, reinterpret_cast(&value), field.Stride); + } else if (field.Type == "bool") { + bool value = (valueData[0] == 't'); // Lazy bool evaluation + memcpy(outData, reinterpret_cast(&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()); + } +} diff --git a/src/Engine/Core/EntityFileParser.cpp b/src/Engine/Core/EntityFileParser.cpp new file mode 100644 index 00000000..541da01d --- /dev/null +++ b/src/Engine/Core/EntityFileParser.cpp @@ -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& 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); +} diff --git a/src/Engine/Core/EntityFilePreprocessor.cpp b/src/Engine/Core/EntityFilePreprocessor.cpp new file mode 100644 index 00000000..ca445042 --- /dev/null +++ b/src/Engine/Core/EntityFilePreprocessor.cpp @@ -0,0 +1,230 @@ +#include "Core/EntityFilePreprocessor.h" + +EntityFilePreprocessor::EntityFilePreprocessor(const EntityFile* entityFile) + : m_EntityFile(entityFile) +{ + EntityFileHandler handler; + handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2)); + m_EntityFile->Parse(&handler); + + LOG_DEBUG("___ COMPONENT DEFINITIONS ___"); + for (auto& kv : m_ComponentCounts) { + LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second); + } + + parseComponentInfo(); + + for (auto& kv : m_ComponentInfo) { + auto& info = kv.second; + LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str()); + LOG_DEBUG("Stride: %i", info.Meta.Stride); + LOG_DEBUG("Allocation: %i", info.Meta.Allocation); + for (auto& kv : info.Fields) { + auto& field = kv.second; + LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type, kv.first.c_str()); + } + } + + parseDefaults(); +} + +void EntityFilePreprocessor::RegisterComponents(World* world) +{ + for (auto& kv : m_ComponentInfo) { + world->RegisterComponent(kv.second); + } +} + +void EntityFilePreprocessor::onStartComponent(EntityID entity, std::string type) +{ + //LOG_DEBUG("Component: %s", type.c_str()); + m_ComponentCounts[type]++; +} + +void EntityFilePreprocessor::parseComponentInfo() +{ + using namespace xercesc; + EntityFileXMLErrorHandler errorHandler; + auto grammarPool = m_EntityFile->GrammarPool(); + bool whateverTheFuckThisIs; + auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs); + + // Find component xsd element declarations + std::cout << "Enumerating components..." << std::endl; + // + auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION); + for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { + auto element = static_cast(topLevelElements->item(i)); + + std::string nameSpace = 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(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(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!"); + } + + // + auto typeDefinition = element->getTypeDefinition(); + if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) { + LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping."); + continue; + } + auto complexTypeDefinition = dynamic_cast(typeDefinition); + + // + 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(); + + // getParticles(); + for (unsigned int i = 0; i < particles->size(); ++i) { + auto particle = particles->elementAt(i); + if (particle->getTermType() != XSParticle::TERM_ELEMENT) { + LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping."); + continue; + } + auto elementDeclaration = particle->getElementTerm(); + + std::string name = XS::ToString(elementDeclaration->getName()); + std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName()); + + size_t stride = EntityFile::GetTypeStride(type); + if (stride == 0) { + std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl; + continue; + } + + auto& field = compInfo.Fields[name]; + field.Name = name; + field.Type = type; + field.Offset = fieldOffset; + field.Stride = stride; + compInfo.FieldsInOrder.push_back(name); + + fieldOffset += stride; + } + + compInfo.Meta.Stride = fieldOffset; + m_ComponentInfo[compInfo.Name] = compInfo; + } +} + +void EntityFilePreprocessor::parseDefaults() +{ + using namespace xercesc; + + EntityFileXMLErrorHandler errorHandler; + + for (auto& ci : m_ComponentInfo) { + // Allocate memory for default values + ci.second.Defaults = std::shared_ptr(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(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(fieldNode); + + char* data = ci.second.Defaults.get() + field.Offset; + + // Handle potential field attributes + if (fieldElement->hasAttributes()) { + std::map 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); + } + } + } +} + diff --git a/src/Engine/Core/EntityFileWriter.cpp b/src/Engine/Core/EntityFileWriter.cpp new file mode 100644 index 00000000..5b6c466d --- /dev/null +++ b/src/Engine/Core/EntityFileWriter.cpp @@ -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(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(vec.x))); + fieldElement->setAttribute(X("Y"), X(boost::lexical_cast(vec.y))); + fieldElement->setAttribute(X("Z"), X(boost::lexical_cast(vec.z))); + } else if (field.Type == "Color") { + const glm::vec4& vec = c[fieldName]; + fieldElement->setAttribute(X("R"), X(boost::lexical_cast(vec.r))); + fieldElement->setAttribute(X("G"), X(boost::lexical_cast(vec.g))); + fieldElement->setAttribute(X("B"), X(boost::lexical_cast(vec.b))); + fieldElement->setAttribute(X("A"), X(boost::lexical_cast(vec.a))); + } else if (field.Type == "Quaternion") { + const glm::quat& q = c[fieldName]; + fieldElement->setAttribute(X("X"), X(boost::lexical_cast(q.x))); + fieldElement->setAttribute(X("Y"), X(boost::lexical_cast(q.y))); + fieldElement->setAttribute(X("Z"), X(boost::lexical_cast(q.z))); + fieldElement->setAttribute(X("W"), X(boost::lexical_cast(q.w))); + } else if (field.Type == "int") { + const int& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); + } else if (field.Type == "float") { + const float& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(value)))); + } else if (field.Type == "double") { + const double& value = c[fieldName]; + fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast(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))); + } + } + } +} + diff --git a/src/Engine/Core/EntityXMLFile.cpp b/src/Engine/Core/EntityXMLFile.cpp deleted file mode 100644 index c6e00a1b..00000000 --- a/src/Engine/Core/EntityXMLFile.cpp +++ /dev/null @@ -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(DOMImplementationRegistry::getDOMImplementation(gLS)); - - // Parse the file - DOMLSParser* parser = di->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, nullptr); - DOMConfiguration* config = parser->getDomConfig(); - config->setParameter(XMLUni::fgDOMNamespaces, true); - config->setParameter(XMLUni::fgXercesSchema, true); - config->setParameter(XMLUni::fgXercesHandleMultipleImports, true); - config->setParameter(XMLUni::fgXercesSchemaFullChecking, true); - config->setParameter(XMLUni::fgXercesDoXInclude, true); - auto errHandler = new EntityPreprocessorXMLErrorHandler(); - config->setParameter(XMLUni::fgDOMErrorHandler, errHandler); - - auto source = new LocalFileInputSource(XSTR(inPath.c_str())); - Wrapper4InputSource* domSourceWrapper = new Wrapper4InputSource(source); - DOMDocument* doc = parser->parse(dynamic_cast(domSourceWrapper)); - - // Serialize and output the new XML - DOMLSSerializer* writer = di->createLSSerializer(); - DOMLSOutput* output = di->createLSOutput(); - XMLFormatTarget* formatTarget = new LocalFileFormatTarget(outPath.c_str()); - // TODO: MemBufFormatTarget* formatTarget = new MemBufFormatTarget() - output->setByteStream(formatTarget); - writer->write(doc, output); - - delete formatTarget; - output->release(); - writer->release(); - parser->release(); -} - -void EntityXMLFile::parseComponentInfo() -{ - using namespace xercesc; - bool wasChanged; - XSModel* xsModel = m_GrammarPool->getXSModel(wasChanged); - - // Find component xsd element declarations - std::cout << "Enumerating components..." << std::endl; - // - auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION); - for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) { - auto element = static_cast(topLevelElements->item(i)); - - std::string nameSpace(XSTR(element->getNamespace())); - if (nameSpace != "components") { - continue; - } - - ComponentInfo compInfo; - - // Name - compInfo.Name = XSTR(element->getName()); - // Annotation - auto componentAnnotation = element->getAnnotation(); - if (componentAnnotation != nullptr) { - // Parse annotation XML - char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString()); - MemBufInputSource annotationInput(reinterpret_cast(annotationString), strlen(annotationString), "MemBuf: Annotation String"); - XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool); - parser.setErrorHandler(m_ErrorHandler); - parser.parse(annotationInput); - XMLString::release(&annotationString); - auto doc = parser.getDocument(); - - // Add allocation estimation(s) - auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation")); - for (int i = 0; i < allocationTags->getLength(); ++i) { - auto allocation = dynamic_cast(allocationTags->item(i)); - auto child = allocation->getFirstChild(); - if (child == nullptr) { - continue; - } - - XSValue::Status status; - XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status); - compInfo.Meta.Allocation += val->fData.fValue.f_int; - } - - // Save documentation string - auto documentationTags = doc->getElementsByTagName(XSTR("xs:documentation")); - if (documentationTags->getLength() != 0) { - auto child = documentationTags->item(0)->getFirstChild(); - if (child != nullptr) { - compInfo.Meta.Annotation = XSTR(child->getNodeValue()); - } - } - // TODO: Parse annotation string XML - // compInfo.Meta.Allocation = ... - } else { - std::cout << "Warning: Component is missing an annotation!" << std::endl; - } - - // - auto typeDefinition = element->getTypeDefinition(); - if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) { - std::cerr << "Error: Type definition wasn't COMPLEX_TYPE! Skipping." << std::endl; - continue; - } - auto complexTypeDefinition = dynamic_cast(typeDefinition); - - // - auto modelGroupParticle = complexTypeDefinition->getParticle(); - if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) { - std::cerr << "Error: Model group particle wasn't TERM_MODELGROUP! Skipping." << std::endl; - continue; - } - auto modelGroup = modelGroupParticle->getModelGroupTerm(); - - // getParticles(); - for (unsigned int i = 0; i < particles->size(); ++i) { - auto particle = particles->elementAt(i); - if (particle->getTermType() != XSParticle::TERM_ELEMENT) { - std::cerr << "Error: Particle wasn't TERM_ELEMENT! Skipping." << std::endl; - continue; - } - auto elementDeclaration = particle->getElementTerm(); - - std::string name = XSTR(elementDeclaration->getName()); - std::string type = XSTR(elementDeclaration->getTypeDefinition()->getName()); - - size_t stride = getTypeStride(type); - if (stride == 0) { - std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl; - continue; - } - - compInfo.FieldTypes[name] = type; - compInfo.FieldOffsets[name] = fieldOffset; - fieldOffset += getTypeStride(type); - } - - compInfo.Meta.Stride = fieldOffset; - m_ComponentInfo[compInfo.Name] = compInfo; - } -} - -void EntityXMLFile::parseDefaults() -{ - using namespace xercesc; - - for (auto& ci : m_ComponentInfo) { - // Allocate memory for default values - ci.second.Defaults = std::shared_ptr(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(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(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(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(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(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(children->getNodeValue()), entity); - } - - //auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*")); - //for (int i = 0; i < components->getLength(); ++i) { - // auto component = dynamic_cast(components->item(i)); - - // std::string componentName = XSTR(component->getLocalName()); - // auto& compStore = m_ComponentStore.at(componentName); - // auto& compInfo = compStore.Info; - - // char* data = &compStore.Data[compStore.Size*compStore.Stride]; - // compStore.Size += 1; - - // auto fields = component->getChildNodes(); - // for (int j = 0; j < fields->getLength(); ++j) { - // auto field = fields->item(j); - // auto nodeType = field->getNodeType(); - // if (nodeType != DOMNode::ELEMENT_NODE) { - // continue; - // } - // //auto field = dynamic_cast(fields->item(j)); - // //const XMLCh* value = fields->item(j)->getTextContent(); - // std::string fieldName = XSTR(field->getLocalName()); - // if (compInfo.FieldTypes.find(fieldName) == compInfo.FieldTypes.end()) { - // std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl; - // continue; - // } - - // std::string fieldType = compInfo.FieldTypes.at(fieldName); - // unsigned int fieldOffset = compInfo.FieldOffsets.at(fieldName); - - // XSValue::DataType dataType = XSValue::getDataType(XSTR(fieldType.c_str())); - // if (dataType == XSValue::DataType::dt_MAXCOUNT) { - // // TODO: - // continue; - // } - // if (dataType == XSValue::DataType::dt_string) { - // char* str = XMLString::transcode(field->getTextContent()); - // std::string standardString(str); - // XMLString::release(&str); - // memcpy(&data[fieldOffset], reinterpret_cast(&standardString), getTypeStride(fieldType)); - // } else { - // XSValue::Status status; - // XSValue* val = XSValue::getActualValue(field->getTextContent(), dataType, status); - // memcpy(&data[fieldOffset], reinterpret_cast(&val->fData.fValue), getTypeStride(fieldType)); - // } - // } - //} - - //auto entities = m_DOMDocument->getElementsByTagName(XSTR("Entity")); - //for (int i = 0; i < entities->getLength(); ++i) { - // auto entity = dynamic_cast(entities->item(i)); - - - // //entity->setIdAttribute() - - // std::cout << "ENTITY " << i + 1 << std::endl; - //} -} - -std::size_t EntityXMLFile::getTypeStride(std::string typeName) -{ - std::map typeStrides{ - { "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(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(&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(&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(&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(&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(&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(&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(&standardString), getTypeStride(typeName)); - } else { - //XSValue::Status status; - //XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status); - //memcpy(outData, reinterpret_cast(&val->fData.fValue), getTypeStride(typeName)); - LOG_WARNING("Unknown native data type: %s", typeName.c_str()); - } - } -} - diff --git a/src/Engine/Core/EventBroker.cpp b/src/Engine/Core/EventBroker.cpp index 6767878f..d847e1a2 100644 --- a/src/Engine/Core/EventBroker.cpp +++ b/src/Engine/Core/EventBroker.cpp @@ -3,7 +3,7 @@ BaseEventRelay::~BaseEventRelay() { if (m_Broker != nullptr) { - m_Broker->Unsubscribe(*this); + m_Broker->Unsubscribe(*this); } } diff --git a/src/Engine/Core/Transform.cpp b/src/Engine/Core/Transform.cpp new file mode 100644 index 00000000..cbc405a3 --- /dev/null +++ b/src/Engine/Core/Transform.cpp @@ -0,0 +1,52 @@ +#include "Core/Transform.h" + +glm::vec3 Transform::AbsolutePosition(World* world, EntityID entity) +{ + glm::vec3 position; + + while (entity != EntityID_Invalid) { + ComponentWrapper transform = world->GetComponent(entity, "Transform"); + EntityID parent = world->GetParent(entity); + position += Transform::AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"]; + entity = parent; + } + + return position; +} + +glm::quat Transform::AbsoluteOrientation(World* world, EntityID entity) +{ + glm::quat orientation; + + while (entity != EntityID_Invalid) { + ComponentWrapper transform = world->GetComponent(entity, "Transform"); + orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation; + entity = world->GetParent(entity); + } + + return orientation; +} + +glm::vec3 Transform::AbsoluteScale(World* world, EntityID entity) +{ + glm::vec3 scale(1.f); + + while (entity != EntityID_Invalid) { + ComponentWrapper transform = world->GetComponent(entity, "Transform"); + scale *= (glm::vec3)transform["Scale"]; + entity = world->GetParent(entity); + } + + return scale; +} + +glm::mat4 Transform::ModelMatrix(EntityID entity, World* world) +{ + glm::vec3 position = Transform::AbsolutePosition(world, entity); + glm::quat orientation = Transform::AbsoluteOrientation(world, entity); + glm::vec3 scale = Transform::AbsoluteScale(world, entity); + + glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(orientation) * glm::scale(scale); + return modelMatrix; +} + diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 5443d388..c94cb8b3 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -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 diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index a43ca900..94e848c0 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -9,6 +9,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer) auto config = ResourceManager::Load("Config.ini"); m_Enabled = config->Get("Debug.EditorEnabled", false); m_Visible = m_Enabled; + m_DefaultEntityDir = boost::filesystem::path("Schema") / boost::filesystem::path("Entities"); if (!m_Enabled) { return; @@ -18,7 +19,6 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer) EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &EditorSystem::OnMousePress); EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &EditorSystem::OnMouseRelease); EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &EditorSystem::OnMouseMove); - EVENT_SUBSCRIBE_MEMBER(m_EPicking, &EditorSystem::OnPicking); EVENT_SUBSCRIBE_MEMBER(m_EFileDropped, &EditorSystem::OnFileDropped); } @@ -33,7 +33,7 @@ void EditorSystem::Update(World* world, double dt) if (!m_Visible) { return; } - + Picking(); updateWidget(); drawUI(world, dt); @@ -44,6 +44,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 +110,27 @@ 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; } + if (m_Camera == nullptr) { + 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_Camera->Orientation() * glm::inverse(glm::quat(widgetOrientation)); int width; int height; @@ -103,14 +142,14 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e) delta2, m_WidgetPickingDepth, res, - m_Renderer->Camera()->ProjectionMatrix(), + m_Camera->ProjectionMatrix(), glm::toMat4(glm::inverse(totalOrientation)) ); glm::vec3 origin = ScreenCoords::ToWorldPos( glm::vec2(res.Width / 2.f, res.Height / 2.f), m_WidgetPickingDepth, res, - m_Renderer->Camera()->ProjectionMatrix(), + m_Camera->ProjectionMatrix(), glm::toMat4(glm::inverse(totalOrientation)) ); deltaWorld = deltaWorld - origin; @@ -122,9 +161,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) { - inverseParentOrientation = glm::inverse(RenderQueueFactory::AbsoluteOrientation(m_World, parent)); - } + //if (parent != 0) { + inverseParentOrientation = glm::inverse(Transform::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 +177,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 = RenderSystem::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 = Transform::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) { @@ -198,16 +237,18 @@ bool EditorSystem::OnMouseRelease(const Events::MouseRelease& e) return true; } -bool EditorSystem::OnPicking(const Events::Picking& e) +void EditorSystem::Picking() { for (auto& pos : m_PickingQueue) { - auto result = e.Pick(pos); + auto result = m_Renderer->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); + m_Camera = result.Camera; if (parent == m_Widget) { m_WidgetCurrentAxis = glm::vec3( (entity == m_WidgetX) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneY || entity == m_WidgetPlaneZ), @@ -215,22 +256,21 @@ bool EditorSystem::OnPicking(const Events::Picking& e) (entity == m_WidgetZ) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneY) ); m_WidgetPickingDepth = result.Depth; - //auto widgetTransform = m_World->GetComponent(m_Widget, "Transform"); //auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); //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; } } } m_PickingQueue.clear(); - return true; }; bool EditorSystem::OnFileDropped(const Events::FileDropped& e) @@ -240,9 +280,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,22 +309,32 @@ 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); + glm::vec3 selectionPosition = Transform::AbsolutePosition(m_World, m_Selection); widgetTransform["Position"] = selectionPosition; if (m_WidgetSpace == WidgetSpace::Local) { - widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection)); + widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection)); } } } void EditorSystem::setWidgetMode(WidgetMode newMode) { - if (m_Widget == 0) { + if (m_Widget == EntityID_Invalid) { return; } @@ -309,10 +359,10 @@ 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)); + widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection)); } } } else if (newMode == WidgetMode::Scale) { @@ -321,25 +371,24 @@ 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)); + widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection)); } } else if (newMode == WidgetMode::Rotate) { 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)); + widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection)); } } } m_WidgetMode = newMode; } - void EditorSystem::setWidgetSpace(WidgetSpace space) { m_WidgetSpace = space; @@ -348,16 +397,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 +448,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 componentTypes; @@ -432,16 +488,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(field); - if (field == "Scale") { + ImGui::PushID(fieldName.c_str()); + if (field.Type == "Vector") { + auto& val = component.Property(fieldName); + if (fieldName == "Scale") { ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits::max()); - } else if (field == "Orientation") { + } else if (fieldName == "Orientation") { glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi())); if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi())) { val = tempVal; @@ -449,16 +505,16 @@ void EditorSystem::drawUI(World* world, double dt) } else { ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits::lowest(), std::numeric_limits::max()); } - } else if (type == "Color") { - auto& val = component.Property(field); + } else if (field.Type == "Color") { + auto& val = component.Property(fieldName); ImGui::ColorEdit4("", glm::value_ptr(val), true); - } else if (type == "string") { - std::string& val = component.Property(field); + } else if (field.Type == "string") { + std::string& val = component.Property(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 +522,21 @@ void EditorSystem::drawUI(World* world, double dt) m_LastDroppedFile = ""; } - } else if (type == "double") { - float tempVal = static_cast(component.Property(field)); + } else if (field.Type == "double") { + float tempVal = static_cast(component.Property(fieldName)); if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) { - component.SetProperty(field, static_cast(tempVal)); + component.SetProperty(fieldName, static_cast(tempVal)); } - } else if (type == "bool") { - auto& val = component.Property(field); + } else if (field.Type == "bool") { + auto& val = component.Property(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 +548,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 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 +669,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(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(); +} diff --git a/src/Engine/Input/InputProxy.cpp b/src/Engine/Input/InputProxy.cpp index c3e4d669..ad589df3 100644 --- a/src/Engine/Input/InputProxy.cpp +++ b/src/Engine/Input/InputProxy.cpp @@ -62,7 +62,7 @@ void InputProxy::Process() e.Command = command; e.Value = currentValue; m_EventBroker->Publish(e); - LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID); + //LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID); m_LastCommandValues[command] = currentValue; } } @@ -78,7 +78,7 @@ void InputProxy::Process() } //e.Value = std::max(-1.f, std::min(e.Value, 1.f)); m_EventBroker->Publish(e); - LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID); + //LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID); } m_CommandQueue.clear(); } diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index 5c7e9c8f..b8849ed0 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -17,7 +17,6 @@ Client::Client(ConfigFile* config) : m_Socket(m_IOService) Client::~Client() { - } void Client::Start(World* world, EventBroker* eventBroker) @@ -27,14 +26,8 @@ void Client::Start(World* world, EventBroker* eventBroker) m_World = world; // Subscribe to events - m_EInputCommand = decltype(m_EInputCommand)(std::bind(&Client::OnInputCommand, this, std::placeholders::_1)); - m_EventBroker->Subscribe(m_EInputCommand); + EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand); - - //while (m_PlayerName.size() > 7) { - // LOG_INFO("Please enter your name (No longer than 7 characters):"); - // std::cin >> m_PlayerName; - //} m_Socket.connect(m_ReceiverEndpoint); LOG_INFO("I am client. BIP BOP"); } @@ -44,18 +37,9 @@ void Client::Update() readFromServer(); } -void Client::Close() -{ - if (m_WasStarted) { - disconnect(); - m_ThreadIsRunning = false; - m_EventBroker->Unsubscribe(m_EInputCommand); - } -} - void Client::readFromServer() { - if (m_Socket.available()) { + while (m_Socket.available()) { bytesRead = receive(readBuf, INPUTSIZE); if (bytesRead > 0) { Packet packet(readBuf, bytesRead); @@ -65,7 +49,7 @@ void Client::readFromServer() std::clock_t currentTime = std::clock(); if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) { if (isConnected()) { - sendSnapshotToServer(); + //sendSnapshotToServer(); } previousSnapshotMessage = currentTime; } @@ -73,13 +57,12 @@ void Client::readFromServer() void Client::sendSnapshotToServer() { - // Reset previouse key state in snapshot. + // Reset previous key state in snapshot. m_NextSnapshot.InputForward = ""; m_NextSnapshot.InputRight = ""; auto player = m_World->GetComponent(m_PlayerDefinitions[m_PlayerID].EntityID, "Player"); - // See if any movement keys are down // We dont care if it's overwritten by later // if statement. Watcha gonna do, right! @@ -125,6 +108,8 @@ void Client::parseMessageType(Packet& packet) // Read packet ID m_PreviousPacketID = m_PacketID; // Set previous packet id m_PacketID = packet.ReadPrimitive(); //Read new packet id + if (m_PacketID <= m_PreviousPacketID) + return; //IdentifyPacketLoss(); switch (static_cast(messageType)) { @@ -184,33 +169,51 @@ void Client::parseEventMessage(Packet& packet) } } +void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType) +{ + for (auto field : componentInfo.FieldsInOrder) { + ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field); + if (fieldInfo.Type == "string") { + std::string& value = packet.ReadString(); + m_World->GetComponent(entityID, componentType)[fieldInfo.Name] = value; + } else { + memcpy(m_World->GetComponent(entityID, componentType).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride); + } + } +} + +// Field parse void Client::parseSnapshot(Packet& packet) { - std::string tempName; - for (size_t i = 0; i < MAXCONNECTIONS; i++) { - // We're checking for empty name for now. This might not be the best way, - // but it is to avoid sending redundant data. - tempName = packet.ReadString(); - - - // Apply the position data read to the player entity - // New player connected on the server side - if (m_PlayerDefinitions[i].Name == "" && tempName != "") { - m_PlayerDefinitions[i].Name = tempName; - m_PlayerDefinitions[i].EntityID = createPlayer(); - } else if (m_PlayerDefinitions[i].Name != "" && tempName == "") { - // Someone disconnected - // TODO: Insert code here - break; - } else if (m_PlayerDefinitions[i].Name == "" && tempName == "") { - // Not a connected player - break; - } - if (m_PlayerDefinitions[i].EntityID != -1) { - - // Move player to server position - int dataSize = m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform").Info.Meta.Stride; - memcpy(m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform").Data, packet.ReadData(dataSize), dataSize); + std::string componentType = packet.ReadString(); + while (packet.DataReadSize() < packet.Size()) { + EntityID entityID = packet.ReadPrimitive(); + ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo(); + if (m_World->ValidEntity(entityID)) { + if (m_World->HasComponent(entityID, componentType)) { + // If the entity and the component exists update it + updateFields(packet, componentInfo, entityID, componentType); + // if entity exists but not the component + } else { + // Create component + m_World->AttachComponent(entityID, componentType); + // Copy data to newly created component + updateFields(packet, componentInfo, entityID, componentType); + } + // If the entity dosent exist nor the component + } else { + //Create Entity + // If entity dosen't exist + EntityID newEntityID = m_World->CreateEntity(); + // Check if EntityIDs are out of sync + if (newEntityID != entityID) { + LOG_INFO("Client::parseSnapshot(Packet& packet): Newly created EntityID is not the \ + same as the one sent by server (EntityIDs are out of sync)"); + } + // Create component + m_World->AttachComponent(newEntityID, componentType); + // Copy data to newly created component + updateFields(packet, componentInfo, newEntityID, componentType); } } } @@ -225,7 +228,7 @@ int Client::receive(char* data, size_t length) 0, error); if (error) { - //LOG_ERROR("receive: %s", error.message().c_str()); + LOG_ERROR("receive: %s", error.message().c_str()); } return bytesReceived; diff --git a/src/Engine/Network/Packet.cpp b/src/Engine/Network/Packet.cpp index 52b15065..2b2c7938 100644 --- a/src/Engine/Network/Packet.cpp +++ b/src/Engine/Network/Packet.cpp @@ -3,13 +3,7 @@ Packet::Packet(MessageType type, unsigned int& packetID) { m_Data = new char[m_MaxPacketSize]; - // Create message header - // Add message type - int messageType = static_cast(type); - Packet::WritePrimitive(messageType); - packetID = packetID % 1000; // Packet id modulos - Packet::WritePrimitive(packetID); - packetID++; + Init(type, packetID); } // Create message @@ -28,12 +22,26 @@ Packet::~Packet() delete[] m_Data; } -void Packet::WriteString(std::string str) +void Packet::Init(MessageType type, unsigned int & packetID) +{ + m_ReturnDataOffset = 0; + m_Offset = 0; + // Create message header + // Add message type + int messageType = static_cast(type); + Packet::WritePrimitive(messageType); + packetID = packetID % 1000; // Packet id modulos + Packet::WritePrimitive(packetID); + packetID++; +} + +void Packet::WriteString(const std::string& str) { // Message, add one extra byte for null terminator int sizeOfString = str.size() + 1; if (m_Offset + sizeOfString > m_MaxPacketSize) { - LOG_WARNING("Package::WriteString(): Data size in packet exceeded maximum package size.\n"); + LOG_WARNING("Package::WriteString(): Data size in packet exceeded maximum package size. New size is %i bytes\n", m_MaxPacketSize*2); + resizeData(); } memcpy(m_Data + m_Offset, str.data(), sizeOfString * sizeof(char)); m_Offset += sizeOfString * sizeof(char); @@ -42,7 +50,8 @@ void Packet::WriteString(std::string str) void Packet::WriteData(char * data, int sizeOfData) { if (m_Offset + sizeOfData > m_MaxPacketSize) { - LOG_WARNING("Packet::WriteData(): Data size in packet exceeded maximum packet size.\n"); + LOG_WARNING("Packet::WriteData(): Data size in packet exceeded maximum packet size. New size is %i bytes\n", m_MaxPacketSize*2); + resizeData(); } memcpy(m_Data + m_Offset, data, sizeOfData); m_Offset += sizeOfData; @@ -69,4 +78,24 @@ char * Packet::ReadData(int SizeOfData) unsigned int oldReturnDataOffset = m_ReturnDataOffset; m_ReturnDataOffset += SizeOfData; return (m_Data + oldReturnDataOffset); -} \ No newline at end of file +} + +void Packet::resizeData() +{ + + // Allocate memory to store our data in + char* holdData = new char[m_MaxPacketSize]; + // Copy our data to the newly allocated memory + memcpy(holdData, m_Data, m_Offset); + // Increase max packet size + m_MaxPacketSize = m_MaxPacketSize * 2; + // Delete our data + delete m_Data; + // Allocate twice the memory we had before + m_Data = new char[m_MaxPacketSize]; + // Copy our data to new location + memcpy(m_Data, holdData, m_Offset); + // Delete the memory allocated to hold our data + // while we resized the old data container. + delete holdData; +} diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index 261583cc..07fed65b 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -4,7 +4,9 @@ Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::a { } Server::~Server() -{ } +{ + +} void Server::Start(World* world, EventBroker* eventBroker) @@ -22,18 +24,9 @@ void Server::Update() readFromClients(); } -void Server::Close() -{ - m_ThreadIsRunning = false; -} - void Server::readFromClients() { - // m_ThreadIsRunning might be unnecessary but the - // program crashed if it executed m_Socket.available() - // when closing the program. - - if (m_Socket.available()) { + while (m_Socket.available()) { try { bytesRead = receive(readBuffer, INPUTSIZE); Packet packet(readBuffer, bytesRead); @@ -41,7 +34,6 @@ void Server::readFromClients() } catch (const std::exception& err) { //LOG_ERROR("%i: Read from client crashed %s", m_PacketID, err.what()); } - } std::clock_t currentTime = std::clock(); // Send snapshot @@ -58,7 +50,7 @@ void Server::readFromClients() // Time out logic if (checkTimeOutInterval < (1000 * (currentTime - timOutTimer) / (double)CLOCKS_PER_SEC)) { - checkForTimeOuts(); + //checkForTimeOuts(); timOutTimer = currentTime; } } @@ -108,7 +100,7 @@ int Server::receive(char * data, size_t length) void Server::send(Packet& packet, int playerID) { - m_Socket.send_to( + int bytesSent = m_Socket.send_to( boost::asio::buffer(packet.Data(), packet.Size()), m_PlayerDefinitions[playerID].Endpoint, 0); @@ -150,22 +142,32 @@ void Server::broadcast(Packet& packet) } } +// Send snapshot fields void Server::sendSnapshot() { - Packet packet(MessageType::Snapshot, m_SendPacketID); - for (size_t i = 0; i < MAXCONNECTIONS; i++) { + // Should time this + std::unordered_map worldComponentPools = m_World->GetComponentPools(); + for (auto& it : worldComponentPools) { + Packet packet(MessageType::Snapshot, m_SendPacketID); + std::string componentType = it.first; + ComponentPool* componentPool = it.second; + ComponentInfo componentInfo = componentPool->ComponentInfo(); + packet.WriteString(componentInfo.Name); - // Send an empty name if there is no player connected on this position. - packet.WriteString(m_PlayerDefinitions[i].Name); - - if (m_PlayerDefinitions[i].EntityID == -1) { - continue; + for (auto& componentWrapper : *componentPool) { + packet.WritePrimitive(componentWrapper.EntityID); + for (auto& componentField : componentWrapper.Info.FieldsInOrder) { + ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(componentField); + if (fieldInfo.Type == "string") { + std::string& value = componentWrapper[componentField]; + packet.WriteString(value); + } else { + packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride); + } + } } - // Pack transfrom component into data packet - auto transform = m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform"); - packet.WriteData(transform.Data, transform.Info.Meta.Stride); + broadcast(packet); } - broadcast(packet); } void Server::sendPing() @@ -174,10 +176,9 @@ void Server::sendPing() for (size_t i = 0; i < MAXCONNECTIONS; i++) { if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) { int ping = 1000 * (m_StopTimes[i] - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); - LOG_INFO("%i: Player %i's ping: %i", m_PacketID, i, ping); + LOG_INFO("Last packetID received %i: Player %i's ping: %i", m_PacketID, i, ping); } } - // Create ping message Packet packet(MessageType::ServerPing, m_SendPacketID); packet.WriteString("Ping from server"); @@ -271,7 +272,7 @@ void Server::parseConnect(Packet& packet) m_StopTimes[i] = std::clock(); - LOG_INFO("Player \"%s\" connected on IP: %s", m_PlayerDefinitions[i].Name, m_PlayerDefinitions[i].Endpoint.address().to_string()); + LOG_INFO("Player \"%s\" connected on IP: %s", m_PlayerDefinitions[i].Name.c_str(), m_PlayerDefinitions[i].Endpoint.address().to_string().c_str()); Packet packet(MessageType::Connect, m_SendPacketID); packet.WritePrimitive(i); // Player ID diff --git a/src/Engine/Rendering/Camera.cpp b/src/Engine/Rendering/Camera.cpp index 6ddf1c6c..5a774c34 100644 --- a/src/Engine/Rendering/Camera.cpp +++ b/src/Engine/Rendering/Camera.cpp @@ -50,6 +50,18 @@ void Camera::SetOrientation(glm::quat val) UpdateViewMatrix(); } + +void Camera::SetProjectionMatrix(glm::mat4 val) +{ + m_ProjectionMatrix = val; +} + + +void Camera::SetViewMatrix(glm::mat4 val) +{ + m_ViewMatrix = val; +} + //void Camera::Pitch(float val) //{ // m_Pitch = val; @@ -64,15 +76,6 @@ void Camera::SetOrientation(glm::quat val) void Camera::UpdateProjectionMatrix() { -// m_ProjectionMatrix = glm::ortho( -// -16.f, -// 16.f, -// -9.f, -// 9.f, -// m_NearClip, -// m_FarClip -// ); - m_ProjectionMatrix = glm::perspective(m_FOV, m_AspectRatio, m_NearClip, m_FarClip); } diff --git a/src/Engine/Rendering/DrawScenePass.cpp b/src/Engine/Rendering/DrawScenePass.cpp index 559a0f58..8fe6d827 100644 --- a/src/Engine/Rendering/DrawScenePass.cpp +++ b/src/Engine/Rendering/DrawScenePass.cpp @@ -21,29 +21,25 @@ void DrawScenePass::InitializeShaderPrograms() m_BasicForwardProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/BasicForward.frag.glsl"))); m_BasicForwardProgram->Compile(); m_BasicForwardProgram->Link(); - - } -void DrawScenePass::Draw(RenderQueueCollection& rq) +void DrawScenePass::Draw(RenderScene& scene) { //glBindFramebuffer(GL_FRAMEBUFFER, 0); GLERROR("Renderer::Draw PickingPass"); - DrawScenePassState state; + DrawScenePassState state = DrawScenePassState(); - - //TODO: Render: Add code for more jobs than modeljobs. - for (auto &job : rq.Forward) { + for (auto &job : scene.ForwardJobs) { auto modelJob = std::dynamic_pointer_cast(job); if (modelJob) { GLuint ShaderHandle = m_BasicForwardProgram->GetHandle(); m_BasicForwardProgram->Bind(); //TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->ModelMatrix)); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(m_Renderer->Camera()->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(m_Renderer->Camera()->ProjectionMatrix())); + glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); glUniform4fv(glGetUniformLocation(ShaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color)); //TODO: Renderer: bättre textur felhantering samt fler texturer stöd @@ -59,8 +55,9 @@ void DrawScenePass::Draw(RenderQueueCollection& rq) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex); - continue; + //continue; } } + GLERROR("DrawScene Error"); } diff --git a/src/Engine/Rendering/DrawScenePassState.cpp b/src/Engine/Rendering/DrawScenePassState.cpp index 9e7497a3..2d643697 100644 --- a/src/Engine/Rendering/DrawScenePassState.cpp +++ b/src/Engine/Rendering/DrawScenePassState.cpp @@ -8,8 +8,10 @@ DrawScenePassState::DrawScenePassState() GLERROR("---"); Enable(GL_DEPTH_TEST); Enable(GL_CULL_FACE); - ClearColor(glm::vec4(255.f / 255, 163.f / 255, 176.f / 255, 0.f)); - Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + Enable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // ClearColor(glm::vec4(255.f / 255, 163.f / 255, 176.f / 255, 0.f)); + // Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } DrawScenePassState::~DrawScenePassState() diff --git a/src/Engine/Rendering/DummyRenderer.cpp b/src/Engine/Rendering/DummyRenderer.cpp index 4956a87a..ab529315 100644 --- a/src/Engine/Rendering/DummyRenderer.cpp +++ b/src/Engine/Rendering/DummyRenderer.cpp @@ -39,17 +39,10 @@ void DummyRenderer::Initialize() exit(EXIT_FAILURE); } - // Create default camera - m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f); - m_DefaultCamera->SetPosition(glm::vec3(0, 0, 0)); - if (m_Camera == nullptr) { - m_Camera = m_DefaultCamera; - } - glfwSwapInterval(m_VSYNC); } -void DummyRenderer::Draw(RenderQueueCollection& rq) +void DummyRenderer::Draw(RenderFrame& rq) { glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 0.f); glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/Engine/Rendering/PickingPass.cpp b/src/Engine/Rendering/PickingPass.cpp index 148b272c..6800df1d 100644 --- a/src/Engine/Rendering/PickingPass.cpp +++ b/src/Engine/Rendering/PickingPass.cpp @@ -43,70 +43,109 @@ void PickingPass::InitializeShaderPrograms() m_PickingProgram->Link(); } -void PickingPass::Draw(RenderQueueCollection& rq) +void PickingPass::Draw(RenderScene& scene) { - m_PickingColorsToEntity.clear(); PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle()); - int r = 0; - int g = 0; + //TODO: Render: Add code for more jobs than modeljobs. GLuint ShaderHandle = m_PickingProgram->GetHandle(); m_PickingProgram->Bind(); - std::map entityColors; + - for (auto &job : rq.Forward) { - auto modelJob = std::dynamic_pointer_cast(job); + m_Camera = scene.Camera; - if (modelJob) { - int pickColor[2] = { r, g }; - auto color = entityColors.find(modelJob->Entity); - if (color != entityColors.end()) { - pickColor[0] = color->second[0]; - pickColor[1] = color->second[1]; - } else { - entityColors[modelJob->Entity] = glm::vec2(pickColor[0], pickColor[1]); - if (r + 10 > 255) { - r = 0; - g += 1; + for (auto &job : scene.ForwardJobs) { + auto modelJob = std::dynamic_pointer_cast(job); + + if (modelJob) { + int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; + + PickingInfo pickInfo; + pickInfo.Entity = modelJob->Entity; + pickInfo.World = modelJob->World; + pickInfo.Camera = scene.Camera; + + auto color = m_EntityColors.find(std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)); + if (color != m_EntityColors.end()) { + pickColor[0] = color->second[0]; + pickColor[1] = color->second[1]; } else { - r += 1; + m_EntityColors[std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)] = glm::ivec2(pickColor[0], pickColor[1]); + if (m_ColorCounter[0] > 255) { + m_ColorCounter[0] = 0; + m_ColorCounter[1]++;; + } else { + m_ColorCounter[0]++;; + } } - } - m_PickingColorsToEntity[glm::vec2(pickColor[0], pickColor[1])] = modelJob->Entity; - - //Render picking stuff - //TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->ModelMatrix)); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(m_Renderer->Camera()->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(m_Renderer->Camera()->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); - glBindVertexArray(modelJob->Model->VAO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); - glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, nullptr, modelJob->StartIndex); + m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; + + glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + + glBindVertexArray(modelJob->Model->VAO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); + glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, nullptr, modelJob->StartIndex); + } } - } + + m_PickingBuffer.Unbind(); GLERROR("PickingPass Error"); - //Publish pick event every frame with the pick data that can be picked by the event + + delete state; +} + + + +void PickingPass::ClearPicking() +{ + m_PickingColorsToEntity.clear(); + m_EntityColors.clear(); + m_ColorCounter[0] = 1; + m_ColorCounter[1] = 0; + + m_PickingBuffer.Bind(); + glClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_PickingBuffer.Unbind(); +} + +PickData PickingPass::Pick(glm::vec2 screenCoord) +{ int fbWidth; int fbHeight; glfwGetFramebufferSize(m_Renderer->Window(), &fbWidth, &fbHeight); - Events::Picking pickEvent = Events::Picking( - &m_PickingBuffer, - &m_DepthBuffer, - m_Renderer->Camera()->ProjectionMatrix(), - m_Renderer->Camera()->ViewMatrix(), - Rectangle(fbWidth, fbHeight), - &m_PickingColorsToEntity); - m_EventBroker->Publish(pickEvent); + Rectangle resolution = Rectangle(fbWidth, fbHeight); + PickData pickData; + // Invert screen y coordinate + screenCoord.y = resolution.Height - screenCoord.y; + ScreenCoords::PixelData data = ScreenCoords::ToPixelData(screenCoord, &m_PickingBuffer, m_DepthBuffer); + pickData.Depth = data.Depth; - delete state; + PickingInfo pickInfo; + + auto it = m_PickingColorsToEntity.find(glm::ivec2(data.Color[0], data.Color[1])); + if (it != m_PickingColorsToEntity.end()) { + pickInfo = it->second; + } else { + pickData.Entity = EntityID_Invalid; + return pickData; + } + + pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, resolution, pickInfo.Camera->ProjectionMatrix(), pickInfo.Camera->ViewMatrix()); + pickData.Entity = pickInfo.Entity; + pickData.Camera = pickInfo.Camera; + pickData.World = pickInfo.World; + return pickData; } void PickingPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const diff --git a/src/Engine/Rendering/PickingPassState.cpp b/src/Engine/Rendering/PickingPassState.cpp index 1e28ea66..2b4f30c4 100644 --- a/src/Engine/Rendering/PickingPassState.cpp +++ b/src/Engine/Rendering/PickingPassState.cpp @@ -10,8 +10,8 @@ PickingPassState::PickingPassState(GLuint frameBuffer) Enable(GL_CULL_FACE); glm::vec4 clearColor = glm::vec4(0.f); - ClearColor(clearColor); - Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + //ClearColor(clearColor); + //Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } PickingPassState::~PickingPassState() diff --git a/src/Engine/Rendering/RawModel.cpp b/src/Engine/Rendering/RawModel.cpp index fbb406f6..5b0b7260 100644 --- a/src/Engine/Rendering/RawModel.cpp +++ b/src/Engine/Rendering/RawModel.cpp @@ -81,6 +81,9 @@ RawModel::RawModel(std::string fileName) float opacity; material->Get(AI_MATKEY_OPACITY, opacity); desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity); + + desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity); + // Material specular color aiColor3D specular; material->Get(AI_MATKEY_COLOR_SPECULAR, specular); @@ -134,6 +137,7 @@ RawModel::RawModel(std::string fileName) matGroup.EndIndex = m_Indices.size() - 1; // Material shininess material->Get(AI_MATKEY_SHININESS, matGroup.Shininess); + material->Get(AI_MATKEY_OPACITY, matGroup.Transparency); //LOG_DEBUG("Shininess: %f", matGroup.Shininess); // Diffuse texture //LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE)); diff --git a/src/Engine/Rendering/RenderQueueFactory.cpp b/src/Engine/Rendering/RenderQueueFactory.cpp deleted file mode 100644 index 66eacd67..00000000 --- a/src/Engine/Rendering/RenderQueueFactory.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "Rendering/RenderQueueFactory.h" - - -RenderQueueFactory::RenderQueueFactory() -{ - m_RenderQueues = RenderQueueCollection(); -} - -void RenderQueueFactory::Update(World* world) -{ - m_RenderQueues.Clear(); - FillModels(world, &m_RenderQueues.Forward); - FillLights(world, &m_RenderQueues.Lights); -} - -glm::mat4 RenderQueueFactory::ModelMatrix(World* world, EntityID entity) -{ - glm::vec3 position = AbsolutePosition(world, entity); - glm::quat orientation = AbsoluteOrientation(world, entity); - glm::vec3 scale = AbsoluteScale(world, entity); - - glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(orientation) * glm::scale(scale); - return modelMatrix; -} - -glm::vec3 RenderQueueFactory::AbsolutePosition(World* world, EntityID entity) -{ - glm::vec3 position; - - do { - ComponentWrapper transform = world->GetComponent(entity, "Transform"); - EntityID parent = world->GetParent(entity); - if (parent != 0) { - position += AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"]; - } else { - position += (glm::vec3)transform["Position"]; - } - entity = parent; - } while (entity != 0); - - return position; -} - -glm::quat RenderQueueFactory::AbsoluteOrientation(World* world, EntityID entity) -{ - glm::quat orientation; - - do { - ComponentWrapper transform = world->GetComponent(entity, "Transform"); - orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation; - entity = world->GetParent(entity); - } while (entity != 0); - - return orientation; -} - -glm::vec3 RenderQueueFactory::AbsoluteScale(World* world, EntityID entity) -{ - glm::vec3 scale(1.f); - - do { - ComponentWrapper transform = world->GetComponent(entity, "Transform"); - scale *= (glm::vec3)transform["Scale"]; - entity = world->GetParent(entity); - } while (entity != 0); - - return scale; -} - -void RenderQueueFactory::FillModels(World* world, RenderQueue* renderQueue) -{ - auto models = world->GetComponents("Model"); - if (models == nullptr) { - return; - } - - for (auto& modelC : *models) { - bool visible = modelC["Visible"]; - if (!visible) { - continue; - } - std::string resource = modelC["Resource"]; - if (resource.empty()) { - continue; - } - glm::vec4 color = modelC["Color"]; - Model* model = ResourceManager::Load(resource); - if (model == nullptr) { - model = ResourceManager::Load("Models/Core/Error.obj"); - } - - for (auto texGroup : model->TextureGroups()) { - ModelJob job; - job.TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0; - job.DiffuseTexture = texGroup.Texture.get(); - job.NormalTexture = texGroup.NormalMap.get(); - job.SpecularTexture = texGroup.SpecularMap.get(); - job.Model = model; - job.StartIndex = texGroup.StartIndex; - job.EndIndex = texGroup.EndIndex; - job.ModelMatrix = model->Matrix() * ModelMatrix(world, modelC.EntityID); - job.Color = color; - - //TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this - job.Entity = modelC.EntityID; - - renderQueue->Add(job); - } - } -} - -void RenderQueueFactory::FillLights(World* world, RenderQueue* renderQueue) -{ - -} - diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp new file mode 100644 index 00000000..1f2bac9d --- /dev/null +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -0,0 +1,188 @@ +#include "Rendering/RenderSystem.h" + +RenderSystem::RenderSystem(EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame) :ImpureSystem(eventBrokerer) +{ + m_Renderer = renderer; + m_RenderFrame = renderFrame; + EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera); + EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand); + + m_Camera = new Camera((float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height, glm::radians(45.f), 0.01f, 5000.f); + m_DebugCameraInputController = new DebugCameraInputController(eventBrokerer, -1); +} + +RenderSystem::~RenderSystem() +{ + delete m_Camera; + delete m_DebugCameraInputController; +} + +bool RenderSystem::OnSetCamera(const Events::SetCamera &event) +{ + auto cameras = m_World->GetComponents("Camera"); + + if (cameras != nullptr) { + for (auto it = cameras->begin(); it != cameras->end(); it++) { + if ((std::string)(*it)["Name"] == event.Name) { + switchCamera((*it).EntityID); + } + } + } + return true; +} + +void RenderSystem::switchCamera(EntityID entity) +{ + if(m_World->HasComponent(entity, "Camera")) { + + if (m_CurrentCamera != EntityID_Invalid) { + if (m_World->HasComponent(m_CurrentCamera, "Model")) { + m_World->GetComponent(m_CurrentCamera, "Model")["Visible"] = true; + } + } + + if (m_World->HasComponent(entity, "Model")) { + m_World->GetComponent(entity, "Model")["Visible"] = false; + } + m_CurrentCamera = entity; + m_SwitchCamera = false; + + } else { + LOG_ERROR("Entity %i does not have a CameraComponent", entity); + m_SwitchCamera = false; + } +} + +void RenderSystem::updateProjectionMatrix(ComponentWrapper& cameraComponent) +{ + double fov = cameraComponent["FOV"]; + double aspectRatio = (float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height; + double nearClip = cameraComponent["NearClip"]; + double farClip = cameraComponent["FarClip"]; + + m_Camera->SetFOV(glm::radians(fov)); + m_Camera->SetAspectRatio(aspectRatio); + m_Camera->SetNearClip(nearClip); + m_Camera->SetFarClip(farClip); + m_Camera->UpdateProjectionMatrix(); +} + +void RenderSystem::fillModels(std::list>& jobs, World* world) +{ + auto models = world->GetComponents("Model"); + if (models == nullptr) { + return; + } + + for (auto& modelComponent : *models) { + bool visible = modelComponent["Visible"]; + if (!visible) { + continue; + } + std::string resource = modelComponent["Resource"]; + if (resource.empty()) { + continue; + } + + Model* model = ResourceManager::Load<::Model>(resource); + if (model == nullptr) { + model = ResourceManager::Load<::Model>("Models/Core/Error.obj"); + } + + glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world); + + for (auto texGroup : model->TextureGroups) { + std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, texGroup, modelComponent, world)); + jobs.push_back(modelJob); + } + } +} + +bool RenderSystem::OnInputCommand(const Events::InputCommand& e) +{ + if (e.Command == "SwitchCamera" && e.Value > 0) { + m_SwitchCamera = true; + return true; + } else { + return false; + } +} + +void RenderSystem::Update(World* world, double dt) +{ + m_World = world; + m_EventBroker->Process(); + + updateCamera(world, dt); + + //Only supports opaque geometry atm + m_RenderFrame->Clear(); + + RenderScene rs; + rs.Camera = m_Camera; + rs.Viewport = Rectangle(1280, 720); + fillModels(rs.ForwardJobs, world); + m_RenderFrame->Add(rs); + +} + +void RenderSystem::updateCamera(World* world, double dt) +{ + if (m_SwitchCamera) { + auto cameras = world->GetComponents("Camera"); + for (auto it = cameras->begin(); it != cameras->end(); it++) { + if ((*it).EntityID == m_CurrentCamera) { + it++; + if (it != cameras->end()) { + switchCamera((*it).EntityID); + } else { + switchCamera((*cameras->begin()).EntityID); + } + break; + } + } + ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera"); + ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform"); + + m_DebugCameraInputController->SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"])); + m_DebugCameraInputController->SetPosition(cameraTransform["Position"]); + } + + if (m_World->ValidEntity(m_CurrentCamera)) { + if (world->HasComponent(m_CurrentCamera, "Camera") && world->HasComponent(m_CurrentCamera, "Transform")) { + ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera"); + ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform"); + + m_DebugCameraInputController->Update(dt); + (glm::vec3&)cameraTransform["Orientation"] = glm::eulerAngles(m_DebugCameraInputController->Orientation()); + (glm::vec3&)cameraTransform["Position"] = m_DebugCameraInputController->Position(); + + glm::vec3 position = Transform::AbsolutePosition(world, m_CurrentCamera); + glm::quat orientation = Transform::AbsoluteOrientation(world, m_CurrentCamera); + + m_Camera->SetPosition(position); + m_Camera->SetOrientation(orientation); + + updateProjectionMatrix(cameraComponent); + + } + } else { + m_Camera = m_Camera; + + auto cameras = world->GetComponents("Camera"); + if (cameras != nullptr) { + if (cameras->begin() != cameras->end()) { + ComponentWrapper& cameraC = *cameras->begin(); + switchCamera(cameraC.EntityID); + + ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera"); + ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform"); + + m_DebugCameraInputController->SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"])); + m_DebugCameraInputController->SetPosition(cameraTransform["Position"]); + } + } + } + + m_Camera->UpdateViewMatrix(); +} \ No newline at end of file diff --git a/src/Engine/Rendering/Renderer.cpp b/src/Engine/Rendering/Renderer.cpp index d0788b94..a59a6bca 100644 --- a/src/Engine/Rendering/Renderer.cpp +++ b/src/Engine/Rendering/Renderer.cpp @@ -3,27 +3,27 @@ void Renderer::Initialize() { InitializeWindow(); - // Create default camera - m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f); - m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10)); - if (m_Camera == nullptr) { - m_Camera = m_DefaultCamera; - } - m_DebugCameraInputController = std::make_shared>(m_EventBroker, -1); - TEMPCreateLights(); + InitializeRenderPasses(); glfwSwapInterval(m_VSYNC); InitializeShaders(); InitializeTextures(); - InitializeSSBOs(); - //CalculateFrustum(); m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); m_UnitQuad = ResourceManager::Load("Models/Core/UnitQuad.obj"); m_UnitSphere = ResourceManager::Load("Models/Core/UnitSphere.obj"); m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker); + + + // Create default camera + m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f); + m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10)); + if (m_Camera == nullptr) { + m_Camera = m_DefaultCamera; + } + } void Renderer::InitializeWindow() @@ -75,52 +75,11 @@ void Renderer::InitializeShaders() m_DrawScreenQuadProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/DrawScreenQuad.frag.glsl"))); m_DrawScreenQuadProgram->Compile(); m_DrawScreenQuadProgram->Link(); - - //m_CalculateFrustumProgram = ResourceManager::Load("#CalculateFrustumProgram"); - //m_CalculateFrustumProgram.AddShader(std::shared_ptr(new ComputeShader("Shaders/GridFrustum.comp.glsl"))); - //m_CalculateFrustumProgram.Compile(); - //m_CalculateFrustumProgram.Link(); - - //m_LightCullProgram = ResourceManager::Load("#LightCullProgram"); - //m_LightCullProgram.AddShader(std::shared_ptr(new ComputeShader("Shaders/cullLights.comp.glsl"))); - //m_LightCullProgram.Compile(); - //m_LightCullProgram.Link(); } void Renderer::InputUpdate(double dt) { - glm::vec3 m_Position = m_Camera->Position(); - if (glfwGetKey(m_Window, GLFW_KEY_O) == GLFW_PRESS) - { - m_Position = glm::vec3(0.f, 0.f, 5.f); - } - if (glfwGetKey(m_Window, GLFW_KEY_W) == GLFW_PRESS) - { - m_Position += m_Camera->Forward() * m_CameraMoveSpeed * (float)dt; - } - if (glfwGetKey(m_Window, GLFW_KEY_S) == GLFW_PRESS) - { - m_Position -= m_Camera->Forward() * m_CameraMoveSpeed * (float)dt; - } - if (glfwGetKey(m_Window, GLFW_KEY_D) == GLFW_PRESS) - { - m_Position += m_Camera->Right() * m_CameraMoveSpeed * (float)dt; - } - if (glfwGetKey(m_Window, GLFW_KEY_A) == GLFW_PRESS) - { - m_Position -= m_Camera->Right() * m_CameraMoveSpeed * (float)dt; - } - if (glfwGetKey(m_Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) - { - m_CameraMoveSpeed = 5.f; - } - else { - m_CameraMoveSpeed = 0.5f; - } - - m_DebugCameraInputController->Update(dt); - m_Camera->SetOrientation(m_DebugCameraInputController->Orientation()); - m_Camera->SetPosition(m_DebugCameraInputController->Position()); + } void Renderer::Update(double dt) @@ -130,20 +89,31 @@ void Renderer::Update(double dt) m_ImGuiRenderPass->Update(dt); } -void Renderer::Draw(RenderQueueCollection& rq) +void Renderer::Draw(RenderFrame& frame) { - m_PickingPass->Draw(rq); - //DrawScreenQuad(m_PickingPass->PickingTexture()); - //CullLights(); - - glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 1.f); + glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 0.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + m_PickingPass->ClearPicking(); + for (auto scene : frame.RenderScenes){ + m_Camera = scene->Camera; // remove renderer camera when Editor uses the render scene cameras. + m_PickingPass->Draw(*scene); + + + + m_DrawScenePass->Draw(*scene); + GLERROR("Renderer::Draw m_DrawScenePass->Draw"); + } - m_DrawScenePass->Draw(rq); - GLERROR("Renderer::Draw m_DrawScenePass->Draw"); m_ImGuiRenderPass->Draw(); glfwSwapBuffers(m_Window); } +PickData Renderer::Pick(glm::vec2 screenCoord) +{ + return m_PickingPass->Pick(screenCoord); +} + void Renderer::DrawScreenQuad(GLuint textureToDraw) { glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -183,95 +153,8 @@ void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filterin GLERROR("Texture initialization failed"); } -void Renderer::InitializeSSBOs() -{ - printf("Size: %i\n", sizeof(m_Frustums)); - glGenBuffers(1, &m_FrustumSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_FrustumSSBO); - glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_Frustums), &m_Frustums, GL_DYNAMIC_COPY); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_FrustumSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - - GLERROR("m_FrustumSSBO"); - - glGenBuffers(1, &m_LightSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightSSBO); - glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_PointLights), &m_PointLights, GL_DYNAMIC_COPY); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_LightSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - - GLERROR("m_LightSSBO"); - - - glGenBuffers(1, &m_LightGridSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightGridSSBO); - glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_LightGrid), &m_LightGrid, GL_DYNAMIC_COPY); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_LightGridSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - - GLERROR("m_LightGridSSBO"); - - - glGenBuffers(1, &m_LightOffsetSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightOffsetSSBO); - glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_LightOffset), &m_LightOffset, GL_DYNAMIC_COPY); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_LightOffsetSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - - GLERROR("m_LightOffsetSSBO"); - - - glGenBuffers(1, &m_LightIndexSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightIndexSSBO); - glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_LightIndex), &m_LightIndex, GL_DYNAMIC_COPY); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, m_LightIndexSSBO); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - - GLERROR("m_LightIndexSSBO"); - -} - void Renderer::InitializeRenderPasses() { m_DrawScenePass = new DrawScenePass(this); m_PickingPass = new PickingPass(this, m_EventBroker); } - -void Renderer::CalculateFrustum() -{ - GLERROR("CalculateFrustum Error-1"); - m_CalculateFrustumProgram->Bind(); - - GLERROR("CalculateFrustum Error1"); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_FrustumSSBO); - GLERROR("CalculateFrustum Error2"); - glUniformMatrix4fv(glGetUniformLocation(m_CalculateFrustumProgram->GetHandle(), "P"), 1, false, glm::value_ptr(m_Camera->ProjectionMatrix())); - GLERROR("CalculateFrustum Error3"); - glUniform2f(glGetUniformLocation(m_CalculateFrustumProgram->GetHandle(), "ScreenDimensions"), m_Resolution.Width, m_Resolution.Height); - GLERROR("CalculateFrustum Error4"); - glDispatchCompute(5, 3, 1); - GLERROR("CalculateFrustum Error5"); - -} - -void Renderer::TEMPCreateLights() -{ - for (int i = 0; i < NUM_LIGHTS; i++) { - m_PointLights[i].Position = glm::vec4(i, 0.f, 0.f, 0.f); - m_PointLights[i].Color = glm::vec4(1.f, 0.5f, 0.f + i*0.1f, 1.f); - } -} - -void Renderer::CullLights() -{ - m_LightCullProgram->Bind(); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_FrustumSSBO); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_LightSSBO); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_LightGridSSBO); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_LightOffsetSSBO); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, m_LightIndexSSBO); - glDispatchCompute(m_Resolution.Width / TILE_SIZE, m_Resolution.Height / TILE_SIZE, 1); - GLERROR("CullLights Error"); - -} - diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index 18b80f93..04146670 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -19,6 +19,7 @@ file(GLOB SOURCE_FILES set(SOURCE_FILES ${SOURCE_FILES} "Game.cpp" + "HealthSystem.cpp" "PlayerSystem.cpp" ) diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index b6c8b21e..efa773f5 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,6 +1,8 @@ #include "Game.h" #include "Collision/TriggerSystem.h" #include "Collision/CollisionSystem.h" +#include "Game/HealthSystem.h" +#include "Core/EntityFileWriter.h" Game::Game(int argc, char* argv[]) { @@ -8,8 +10,8 @@ Game::Game(int argc, char* argv[]) ResourceManager::RegisterType("Model"); ResourceManager::RegisterType("RawModel"); ResourceManager::RegisterType("Texture"); - ResourceManager::RegisterType("EntityXMLFile"); ResourceManager::RegisterType("ShaderProgram"); + ResourceManager::RegisterType("EntityFile"); m_Config = ResourceManager::Load("Config.ini"); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); @@ -17,10 +19,9 @@ Game::Game(int argc, char* argv[]) // Create the core event broker m_EventBroker = new EventBroker(); - m_RenderQueueFactory = new RenderQueueFactory(); // Create the renderer - m_Renderer = new Renderer(m_EventBroker); + m_Renderer = new Renderer(m_EventBroker, m_World); m_Renderer->SetFullscreen(m_Config->Get("Video.Fullscreen", false)); m_Renderer->SetVSYNC(m_Config->Get("Video.VSYNC", false)); m_Renderer->SetResolution(Rectangle::Rectangle( @@ -28,9 +29,10 @@ Game::Game(int argc, char* argv[]) 0, m_Config->Get("Video.Width", 1280), m_Config->Get("Video.Height", 720) - )); + )); m_Renderer->Initialize(); - m_Renderer->Camera()->SetFOV(glm::radians(m_Config->Get("Video.FOV", 90.f))); + //m_Renderer->Camera()->SetFOV(glm::radians(m_Config->Get("Video.FOV", 90.f))); + m_RenderFrame = new RenderFrame(); // Create input manager m_InputManager = new InputManager(m_Renderer->Window(), m_EventBroker); @@ -48,16 +50,30 @@ Game::Game(int argc, char* argv[]) m_World = new World(); std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); if (!mapToLoad.empty()) { - ResourceManager::Load(mapToLoad)->PopulateWorld(m_World); + auto file = ResourceManager::Load(mapToLoad); + EntityFilePreprocessor fpp(file); + fpp.RegisterComponents(m_World); + EntityFileParser fp(file); + fp.MergeEntities(m_World); } // Create system pipeline m_SystemPipeline = new SystemPipeline(m_EventBroker); - m_SystemPipeline->AddSystem(); - m_SystemPipeline->AddSystem(); - m_SystemPipeline->AddSystem(m_Renderer); - m_SystemPipeline->AddSystem(); - m_SystemPipeline->AddSystem(); + + + //All systems with orderlevel 0 will be updated first. + unsigned int updateOrderLevel = 0; + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); + m_SystemPipeline->AddSystem(updateOrderLevel); + + //Collision and TriggerSystem should update after player. + ++updateOrderLevel; + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + ++updateOrderLevel; + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame); // Invoke network if (m_Config->Get("Networking.StartNetwork", false)) { @@ -74,8 +90,8 @@ Game::~Game() delete m_FrameStack; delete m_InputProxy; delete m_InputManager; + delete m_RenderFrame; delete m_Renderer; - delete m_RenderQueueFactory; delete m_EventBroker; } @@ -101,15 +117,13 @@ void Game::Tick() if (m_IsClientOrServer) { m_ClientOrServer->Update(); } - // Iterate through systems and update world! m_SystemPipeline->Update(m_World, dt); m_Renderer->Update(dt); m_EventBroker->Process(); - m_RenderQueueFactory->Update(m_World); GLERROR("Game::Tick m_RenderQueueFactory->Update"); - m_Renderer->Draw(m_RenderQueueFactory->RenderQueues()); + m_Renderer->Draw(*m_RenderFrame); GLERROR("Game::Tick m_Renderer->Draw"); m_EventBroker->Swap(); m_EventBroker->Clear(); @@ -132,10 +146,5 @@ void Game::networkFunction() m_ClientOrServer = new Server(); } m_ClientOrServer->Start(m_World, m_EventBroker); - // I don't think we are reaching this part of the code right now. - // ~Game() is not called if the game is exited by closing console windows - // When server or client is done set it to false. - //m_IsClientOrServer = false; - // Destroy it - //delete m_ClientOrServer; + } \ No newline at end of file diff --git a/src/Game/HealthSystem.cpp b/src/Game/HealthSystem.cpp new file mode 100644 index 00000000..7a1d5005 --- /dev/null +++ b/src/Game/HealthSystem.cpp @@ -0,0 +1,59 @@ +#include "HealthSystem.h" +#include + +HealthSystem::HealthSystem(EventBroker* eventBroker) + : PureSystem(eventBroker, "Health") +{ + //subscribe/listenTo playerdamage,healthpickup events (using the eventBroker) + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &HealthSystem::OnPlayerDamaged); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerHealthPickup, &HealthSystem::OnPlayerHealthPickup); +} + +void HealthSystem::UpdateComponent(World *world, ComponentWrapper &health, double dt) +{ + //if entityID of health is 9 then the players ID is also 9 (player,health are connected to the same entity) + ComponentWrapper player = world->GetComponent(health.EntityID, "Player"); + double maxHealth = (double)health["MaxHealth"]; + + //process the DeltaHealthVector and change the entitys health accordingly + 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 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; + } + } + } +} + +bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e) +{ + //save the changed HP to a vector. it will be taken care of in UpdateComponent + m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerDamagedID, -e.DamageAmount)); + return true; +} + +bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e) +{ + //save the changed HP to a vector. it will be taken care of in UpdateComponent + m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerHealedID, e.HealthAmount)); + return true; +} diff --git a/src/Tests/ConfigFileTest.cpp b/src/Tests/ConfigFileTest.cpp new file mode 100644 index 00000000..28be5589 --- /dev/null +++ b/src/Tests/ConfigFileTest.cpp @@ -0,0 +1,66 @@ +#include +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; +#include //srand + +//#define private public +#include "Engine\Core\ConfigFile.h" + +#define _CRTDBG_MAP_ALLOC +#include +#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) +#define new DEBUG_CLIENTBLOCK + +BOOST_AUTO_TEST_SUITE(confTest) + +BOOST_AUTO_TEST_CASE(configFileTest) +{ + //note: this ConfigFileclass currently has memleaks! + + ResourceManager::RegisterType("ConfigFile"); + auto m_Config = ResourceManager::Load("ConfigTest.ini"); + + //bägge måste vara av samma typ, T typen är string + //http://www.boost.org/doc/libs/1_42_0/doc/html/boost_propertytree/tutorial.html + //"Note that we construct the path to the value by separating the individual keys with dots" + + //get from tree tests + auto getSomething = m_Config->Get("Test.Test1", 0); + BOOST_CHECK(getSomething == 423); + + auto getSomething2 = m_Config->Get("fsdfdsfd.T", std::string("")); + BOOST_CHECK(getSomething2 == "\"gfdjakflsdl!\""); + + //set/get tests + m_Config->Set("Test.4321", 123); + auto getSomething3 = m_Config->Get("Test.4321", 0); + BOOST_CHECK(getSomething3 == 123); + + m_Config->Set("3_2_1_0_5", "t454j54hj5k32"); + auto getSomething4 = m_Config->Get("3_2_1_0_5", std::string("")); + BOOST_CHECK(getSomething4 == "t454j54hj5k32"); + + //***check so outputwindow says: EE: Failed to find "DefaultConfigTestNotExists.ini"! Relying on hardcoded default values! + auto m_Config2 = ResourceManager::Load("ConfigTestNotExists.ini"); + + //set value/savetodisk/load/checkvalue... + m_Config->SaveToDisk(); + m_Config->Set("Test.4321", 145); + m_Config->SaveToDisk(); + auto m_Config3 = ResourceManager::Load("ConfigTest.ini"); + auto getSomething5 = m_Config->Get("Test.4321", 0); + BOOST_CHECK(getSomething5 == 145); + + //***check so outputwindow says: EE: Failed to parse "DefaultConfigTestFailed.ini" + //***check so outputwindow says: EE: Failed to parse "ConfigTestFailed.ini": + auto m_Config4 = ResourceManager::Load("ConfigTestFailed.ini"); + + //reload,onchildreload unimplemented + + //NOTE:still massive amount of memoryleaks from this method + _CrtDumpMemoryLeaks(); +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/Tests/EventFixture.h b/src/Tests/EventFixture.h new file mode 100644 index 00000000..42258932 --- /dev/null +++ b/src/Tests/EventFixture.h @@ -0,0 +1,54 @@ +#ifndef EVENTFIXTURE_H +#define EVENTFIXTURE_H + +#include +#include "Core\EventBroker.h" + +template +struct EventFixture +{ + EventFixture() + { + this->ventBroker = new EventBroker(); + m_EEventType = decltype(m_EEventType)(std::bind(&EventFixture::OnEvent, this, std::placeholders::_1)); + this->ventBroker->Subscribe(m_EEventType); + Run(); + Check(); + } + ~EventFixture() + { + this->ventBroker->Unsubscribe(m_EEventType); + delete this->ventBroker; + } + + EventBroker* ventBroker = nullptr; + EventRelay m_EEventType; + bool m_EventRecieved = false; + EventType Before; + EventType After; + + bool OnEvent(const EventType& event) + { + m_EventRecieved = true; + After = event; + + return true; + } + + void Run() + { + // Publish the event + this->ventBroker->Publish(Before); + // Clear to swap buffers + this->ventBroker->Swap(); + // Process the event + this->ventBroker->template Process(); + } + + void Check() + { + BOOST_CHECK(m_EventRecieved); + } +}; + +#endif \ No newline at end of file diff --git a/src/Tests/EventTest.cpp b/src/Tests/EventTest.cpp new file mode 100644 index 00000000..4a030055 --- /dev/null +++ b/src/Tests/EventTest.cpp @@ -0,0 +1,19 @@ +#include +#include "EventFixture.h" + +struct ETestEvent : public Event +{ + int Int = 5; + float Float = 1.33333f; + double Double = 1.33333; + std::string String = "Hello World"; +}; + +BOOST_AUTO_TEST_CASE(EventBrokerTest) +{ + EventFixture f; + BOOST_CHECK(f.Before.Int == f.After.Int); + BOOST_CHECK_CLOSE(f.Before.Float, f.After.Float, 0.00001f); + BOOST_CHECK_CLOSE(f.Before.Double, f.After.Double, 0.00001f); + BOOST_CHECK(f.Before.String == f.After.String); +} \ No newline at end of file diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp new file mode 100644 index 00000000..84d6199d --- /dev/null +++ b/src/Tests/HealthSystemTest.cpp @@ -0,0 +1,114 @@ +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; + +#include "HealthSystemTest.h" +#include "Game/HealthSystem.h" + +BOOST_AUTO_TEST_SUITE(HealthSystemSuite) + +BOOST_AUTO_TEST_CASE(HealthSystemTest) +{ + //this tests 2 healthevents and the healthsystem + GameHealthSystemTest game; + //100 loops will be more than enough to do the test + int loops = 100; + bool success = false; + while (loops > 0) { + game.Tick(); + if (game.TestSucceeded) { + success = true; + break; + } + loops--; + } + //The system will process the events, hence it will take a while before we can read anything + BOOST_TEST(success); +} +BOOST_AUTO_TEST_SUITE_END() + +GameHealthSystemTest::GameHealthSystemTest() +{ + ResourceManager::RegisterType("ConfigFile"); + ResourceManager::RegisterType("EntityXMLFile"); + + m_Config = ResourceManager::Load("Config.ini"); + LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); + + // Create the core event broker + m_EventBroker = new EventBroker(); + + // Create a world + m_World = new World(); + std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); + if (!mapToLoad.empty()) { + ResourceManager::Load(mapToLoad)->PopulateWorld(m_World); + } + + // Create system pipeline + m_SystemPipeline = new SystemPipeline(m_EventBroker); + m_SystemPipeline->AddSystem(0); + m_SystemPipeline->AddSystem(0); + + //The Test + //create entity which has transorm,player,model,health in it. i.e. is a player + EntityID playerID = m_World->CreateEntity(); + ComponentWrapper transform = m_World->AttachComponent(playerID, "Transform"); + ComponentWrapper model = m_World->AttachComponent(playerID, "Model"); + model["Resource"] = "Models/Core/UnitSphere.obj"; + ComponentWrapper player = m_World->AttachComponent(playerID, "Player"); + ComponentWrapper health = m_World->AttachComponent(playerID, "Health"); + healthsID = playerID; + double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"]; + + //heal player with 40 + Events::PlayerHealthPickup e3; + e3.HealthAmount = 40.0f; + e3.PlayerHealedID = healthsID; + m_EventBroker->Publish(e3); + //damage player with 50 + Events::PlayerDamage e; + e.DamageAmount = 50.0f; + e.PlayerDamagedID = healthsID; + m_EventBroker->Publish(e); + //heal some other player with 40 + Events::PlayerHealthPickup e2; + e2.HealthAmount = 40.0f; + e2.PlayerHealedID = healthsID+1; + m_EventBroker->Publish(e2); + + EntityID playerID2 = m_World->CreateEntity(); + ComponentWrapper transform2 = m_World->AttachComponent(playerID2, "Transform"); + ComponentWrapper model2 = m_World->AttachComponent(playerID2, "Model"); + model2["Resource"] = "Models/Core/UnitSphere.obj"; + ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); + ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); + //END TEST +} + +GameHealthSystemTest::~GameHealthSystemTest() +{ + delete m_SystemPipeline; + delete m_World; + delete m_EventBroker; +} + +void GameHealthSystemTest::Tick() +{ + glfwPollEvents(); + + double currentTime = glfwGetTime(); + double dt = currentTime - m_LastTime; + m_LastTime = currentTime; + + // Iterate through systems and update world! + m_SystemPipeline->Update(m_World, dt); + + m_EventBroker->Swap(); + m_EventBroker->Clear(); + + //if health reaches 90 then we know the test has succeeded (start with 100hp, remove 50hp, add 40hp) + double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"]; + if (currentHealth==90) + TestSucceeded = true; +} diff --git a/src/Tests/HealthSystemTest.h b/src/Tests/HealthSystemTest.h new file mode 100644 index 00000000..664d2ef3 --- /dev/null +++ b/src/Tests/HealthSystemTest.h @@ -0,0 +1,40 @@ +#ifndef HealthTest_h__ +#define HealthTest_h__ + +#include "Core/ResourceManager.h" +#include "Core/ConfigFile.h" +#include "Core/EventBroker.h" +#include "Rendering/Renderer.h" +#include "Core/InputManager.h" +#include "GUI/Frame.h" +#include "Core/World.h" +#include "Rendering/RenderQueueFactory.h" +#include "Input/InputProxy.h" +#include "Input/KeyboardInputHandler.h" +#include "Input/MouseInputHandler.h" +#include "Core/EKeyDown.h" +#include "Core/EntityXMLFile.h" +#include "Core/SystemPipeline.h" +#include "RaptorCopterSystem.h" +#include "PlayerSystem.h" +#include "Editor/EditorSystem.h" + +class GameHealthSystemTest +{ +public: + GameHealthSystemTest(); + ~GameHealthSystemTest(); + + void Tick(); + bool TestSucceeded = false; + +private: + double m_LastTime; + ConfigFile* m_Config = nullptr; + EventBroker* m_EventBroker; + World* m_World; + SystemPipeline* m_SystemPipeline; + int healthsID; +}; + +#endif diff --git a/src/Tests/InputManagerTest.cpp b/src/Tests/InputManagerTest.cpp new file mode 100644 index 00000000..5b447c2b --- /dev/null +++ b/src/Tests/InputManagerTest.cpp @@ -0,0 +1,12 @@ +#include + +#include "Engine\Core\InputManager.h" + +BOOST_AUTO_TEST_SUITE(inputManagerTests) + +BOOST_AUTO_TEST_CASE(inputManagerTest) +{ + //already tested eventbroker so inputManager is indirectly already tested +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/Tests/OctTreeTestAnders.cpp b/src/Tests/OctTreeTestAnders.cpp index 12a122ea..b61caead 100644 --- a/src/Tests/OctTreeTestAnders.cpp +++ b/src/Tests/OctTreeTestAnders.cpp @@ -34,16 +34,12 @@ BOOST_AUTO_TEST_CASE(octTreeTest) BOOST_CHECK(someAABB.MaxCorner() == maxCorner); BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner)); - //simple OctTree constructor check - //OctTree someOctTree(someAABB, 5); - //BOOST_CHECK(someOctTree.m_Children[0] != nullptr); - //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure } BOOST_AUTO_TEST_CASE(octTreeTest2) { - //octtree ritningen osv + //octtree draw etc Game game(0, nullptr); while (game.Running()) { game.Tick(); diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp index 1a7deb3f..10d4d6a5 100644 --- a/src/Tests/OctTreeTestGameClass.cpp +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -5,6 +5,8 @@ Game::Game(int argc, char* argv[]) : someOctTree(AABB(-0.5f*worldSize, 0.5f*worl ResourceManager::RegisterType("ConfigFile"); ResourceManager::RegisterType("Model"); ResourceManager::RegisterType("Texture"); + ResourceManager::RegisterType("EntityXMLFile"); + ResourceManager::RegisterType("ShaderProgram"); m_Config = ResourceManager::Load("Config.ini"); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); @@ -25,9 +27,14 @@ Game::Game(int argc, char* argv[]) : someOctTree(AABB(-0.5f*worldSize, 0.5f*worl m_Config->Get("Video.Height", 720) )); m_Renderer->Initialize(); + m_Renderer->Camera()->SetFOV(glm::radians(m_Config->Get("Video.FOV", 90.f))); // Create input manager m_InputManager = new InputManager(m_Renderer->Window(), m_EventBroker); + m_InputProxy = new InputProxy(m_EventBroker); + m_InputProxy->AddHandler(); + m_InputProxy->AddHandler(); + m_InputProxy->LoadBindings("Input.ini"); // Create the root level GUI frame m_FrameStack = new GUI::Frame(m_EventBroker); @@ -37,6 +44,9 @@ Game::Game(int argc, char* argv[]) : someOctTree(AABB(-0.5f*worldSize, 0.5f*worl // Create a TEST WORLD m_World = new HardcodedTestWorld(); + m_SystemPipeline = new SystemPipeline(m_EventBroker); + m_SystemPipeline->AddSystem(0); + m_LastTime = glfwGetTime(); } @@ -52,9 +62,14 @@ void Game::Tick() double dt = currentTime - m_LastTime; m_LastTime = currentTime; + // Handle input in a weird looking but responsive way + m_EventBroker->Process(); m_EventBroker->Swap(); m_InputManager->Update(dt); - m_Renderer->Update(dt); + m_EventBroker->Swap(); + m_InputProxy->Update(dt); + m_EventBroker->Swap(); + m_InputProxy->Process(); m_EventBroker->Swap(); #define TEST1 @@ -70,7 +85,7 @@ void Game::Tick() AABB boxi; boxi.CreateFromCenter(pos, maxPos - minPos); frameCounter++; - if (frameCounter > 50) { + if (frameCounter > 1) { m_World->someOctTree.ClearDynamicObjects(); m_World->someOctTree.AddDynamicObject(boxi); frameCounter = 0; @@ -149,8 +164,8 @@ void Game::Tick() if (someOctTree.BoxCollides(redBox, AABB())) { //this checks AABB vs AABB //if (Collision::AABBVsAABB(redBox, aabb)) { - m_Renderer->Camera()->SetPosition(m_PrevPos); - m_Renderer->Camera()->SetOrientation(m_PrevOri); + //m_Renderer->Camera()->SetPosition(m_PrevPos); + //m_Renderer->Camera()->SetOrientation(m_PrevOri); model["Color"] = greenCol; } else { @@ -163,8 +178,14 @@ void Game::Tick() m_RenderQueueFactory->Update(m_World); #endif - m_Renderer->Draw(m_RenderQueueFactory->RenderQueues()); + // Iterate through systems and update world! + m_SystemPipeline->Update(m_World, dt); + m_Renderer->Update(dt); + m_RenderQueueFactory->Update(m_World); + GLERROR("Game::Tick m_RenderQueueFactory->Update"); + m_Renderer->Draw(m_RenderQueueFactory->RenderQueues()); + GLERROR("Game::Tick m_Renderer->Draw"); m_EventBroker->Swap(); m_EventBroker->Clear(); diff --git a/src/Tests/OctTreeTestGameClass.h b/src/Tests/OctTreeTestGameClass.h index 985d34d4..6dc9404e 100644 --- a/src/Tests/OctTreeTestGameClass.h +++ b/src/Tests/OctTreeTestGameClass.h @@ -9,11 +9,19 @@ #include "GUI/Frame.h" #include "Core/World.h" #include "Rendering/RenderQueueFactory.h" +#include "Input/InputProxy.h" +#include "Input/KeyboardInputHandler.h" +#include "Input/MouseInputHandler.h" +#include "Core/EKeyDown.h" +#include "Core/EntityXMLFile.h" +#include "Core/SystemPipeline.h" +#include "RaptorCopterSystem.h" +#include "PlayerSystem.h" +#include "Editor/EditorSystem.h" #include "OctTreeTestHardCodedTestWorld.h" #include "Collision/Collision.h" - class Game { public: @@ -32,6 +40,8 @@ private: GUI::Frame* m_FrameStack; HardcodedTestWorld* m_World; RenderQueueFactory* m_RenderQueueFactory; + InputProxy* m_InputProxy; + SystemPipeline* m_SystemPipeline; //Test1 int frameCounter = 0; diff --git a/src/Tests/ResourceManagerTest.cpp b/src/Tests/ResourceManagerTest.cpp new file mode 100644 index 00000000..9d62fa93 --- /dev/null +++ b/src/Tests/ResourceManagerTest.cpp @@ -0,0 +1,35 @@ +#include +#include "Core/World.h" + +//private->public hack doesnt work, tons of link errors +//so there is currently no good way to test this class +//#define private public +#include "Core/ResourceManager.h" +#include "Core/ConfigFile.h" +#include "Rendering/Renderer.h" +#include "Engine\Rendering\Texture.h" + +BOOST_AUTO_TEST_SUITE(resourceManagerTests) + +BOOST_AUTO_TEST_CASE(resourceManagerTest) +{ + World m_World; + + //private static metoder/variabler + + ResourceManager::RegisterType("ConfigFile"); + BOOST_CHECK(!ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini")); + auto m_Config = ResourceManager::Load("Config.ini"); + BOOST_CHECK(ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini")); + ResourceManager::Release("ConfigFile", "Config.ini"); + BOOST_CHECK(!ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini")); + + //configfile without register + //check so output says "EE failed to load: type not registered..." + auto m_ScreenQuadNoRegister = ResourceManager::Load("Models/Core/ScreenQuad.obj"); + BOOST_CHECK(!ResourceManager::IsResourceLoaded("Model", "Models/Core/ScreenQuad.obj")); + + //there is no error feedback to check if you try to release the wrong resources - hence that cant be tested either +} + +BOOST_AUTO_TEST_SUITE_END()