diff --git a/assets b/assets index 4bd902b6..b3746822 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4bd902b697b8eef063da800102e6e9c3b29353eb +Subproject commit b37468222e45ec0b2116f1543c578cb9784d43f2 diff --git a/cmake/FindXerces.cmake b/cmake/FindXerces.cmake index 8ea0fb1d..651453bd 100644 --- a/cmake/FindXerces.cmake +++ b/cmake/FindXerces.cmake @@ -1,13 +1,13 @@ -# XERCES_FOUND -# XERCES_INCLUDE_DIRS -# XERCES_LIBRARIES +# Xerces_FOUND +# Xerces_INCLUDE_DIRS +# Xerces_LIBRARIES -find_path(XERCES_INCLUDE_DIR xercesc/dom/dom.hpp +find_path(Xerces_INCLUDE_DIR xercesc/dom/dom.hpp /usr/local/include /usr/include ) -find_library(XERCES_LIBRARY +find_library(Xerces_LIBRARY NAMES xerces-c_3 xerces-c_3D @@ -16,8 +16,8 @@ find_library(XERCES_LIBRARY /usr/lib ) -set(XERCES_INCLUDE_DIRS ${XERCES_INCLUDE_DIR}) -set(XERCES_LIBRARIES ${XERCES_LIBRARY}) +set(Xerces_INCLUDE_DIRS ${Xerces_INCLUDE_DIR}) +set(Xerces_LIBRARIES ${Xerces_LIBRARY}) -find_package_handle_standard_args(Xerces DEFAULT_MSG XERCES_LIBRARY XERCES_INCLUDE_DIR) -mark_as_advanced(Xerces_FOUND XERCES_INCLUDE_DIR XERCES_LIBRARY) \ No newline at end of file +find_package_handle_standard_args(Xerces DEFAULT_MSG Xerces_LIBRARY Xerces_INCLUDE_DIR) +mark_as_advanced(Xerces_FOUND Xerces_INCLUDE_DIR Xerces_LIBRARY) diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 95a31240..56ae943f 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -2,7 +2,7 @@ #define ComponentWrapper_h__ #include "../Common.h" -#include "EntityWrapper.h" +#include "Entity.h" #include "ComponentInfo.h" #include "Util/Any.h" @@ -11,7 +11,7 @@ struct ComponentWrapper ComponentWrapper(const ComponentInfo& componentInfo, char* data) : Info(componentInfo) , EntityID(*reinterpret_cast<::EntityID*>(data)) - , Data(data + sizeof(EntityID)) + , Data(data + sizeof(::EntityID)) { } const ComponentInfo& Info; diff --git a/include/Engine/Core/Entity.h b/include/Engine/Core/Entity.h new file mode 100644 index 00000000..c6a475e3 --- /dev/null +++ b/include/Engine/Core/Entity.h @@ -0,0 +1,6 @@ +#ifndef Entity_h__ +#define Entity_h__ + +typedef unsigned int EntityID; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityFile.h b/include/Engine/Core/EntityFile.h deleted file mode 100644 index 7fa423d6..00000000 --- a/include/Engine/Core/EntityFile.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "ResourceManager.h" - -class EntityFile : public Resource -{ - friend class ResourceManager; - -private: - EntityFile(std::string path); - -public: - -}; \ No newline at end of file diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h deleted file mode 100644 index d5e723ca..00000000 --- a/include/Engine/Core/EntityWrapper.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef Entity_h__ -#define Entity_h__ - -typedef unsigned int EntityID; - -struct EntityWrapper -{ - EntityWrapper(EntityID entityID) - : ID(entityID) - { } - - EntityID ID; -}; - -#endif \ No newline at end of file diff --git a/include/Engine/Core/EntityXMLFile.h b/include/Engine/Core/EntityXMLFile.h new file mode 100644 index 00000000..6fa71dad --- /dev/null +++ b/include/Engine/Core/EntityXMLFile.h @@ -0,0 +1,141 @@ +#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/OctTree.h b/include/Engine/Core/OctTree.h index 7379b202..a3715480 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -13,7 +13,8 @@ public: struct Output { float CollideDistance; - }; + }; + OctTree(); ~OctTree(); //For the root OctTree, [octTreeBounds] should be a box containing the entire level. @@ -24,43 +25,84 @@ public: OctTree(const OctTree& other) = delete; OctTree(const OctTree&& other) = delete; OctTree& operator= (const OctTree& other) = delete; + //Add a dynamic object (one that moves around) into the tree. + void AddDynamicObject(const AABB& box); + //Add a static object (that does not move) into the tree. + void AddStaticObject(const AABB& box); + //Get the boxes that are in the same area as the input [box], the boxes are put in [outBoxes]. + void BoxesInSameRegion(const AABB& box, std::vector& outBoxes); + //Empty the tree of all objects, static and dynamic. + void ClearObjects(); + //Empty the tree of all dynamic objects. Static objects remain in the tree. + void ClearDynamicObjects(); //Collision test function. WTODO: Probably remove or relocate elsewhere, Collision system? void Update(float dt, World* world, Camera* cam); - - void AddDynamicObject(const AABB& box); - void AddStaticObject(const AABB& box); - - void BoxesInSameRegion(const AABB& box, std::vector& outBoxes) const; - - void ClearObjects(); - void ClearDynamicObjects(); - //Returns true if the ray collides with something in the tree. Result is written to [data]. - bool RayCollides(const Ray& ray, Output& data) const; + bool RayCollides(const Ray& ray, Output& data); //Returns true if the box collides with something in the tree. //On collision with a box, that box is written to [outBoxIntersected]. - bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const; + //Note: More efficient than calling BoxesInSameRegion from outside and testing there. + bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected); private: - OctTree* m_Children[8]; - //WTODO: Do -derived class from AABB- struct containing AABB, with a bool Tested, falsify at - //start of Collision test, set on check, don't check if set already. Solves duplicate boxes in tree. - //Store indices in the struct, pointing to grand ancestor list of boxes, need the same AABB not copies to save Tested. - //WTODO: Boxes collide with themselves? Fix somehow, maybe float epsilon stuff. - std::vector m_StaticObjects; - std::vector m_DynamicObjects; - AABB m_Box; + struct OctChild; //Fwd declaration; + struct ContainedObject + { + ContainedObject() + : Box(AABB()) + , Checked(false) + {} + ContainedObject(AABB box) + : Box(box) + , Checked(false) + {} + AABB Box; + bool Checked; + }; + OctChild* m_Root; + std::vector m_StaticObjects; + std::vector m_DynamicObjects; bool m_UpdatedOnce; unsigned int m_BoxID; glm::vec3 m_PrevPos; glm::quat m_PrevOri; - inline bool hasChildren() const; -public: - int childIndexContainingPoint(const glm::vec3& point) const; - std::vector childIndicesContainingBox(const AABB& box) const; + void falsifyObjectChecks(); + + struct OctChild + { + ~OctChild(); + OctChild(const AABB& octTreeBounds, + int subDivisions, + std::vector& staticObjects, + std::vector& dynamicObjects); + OctChild(const OctChild& other) = delete; + OctChild(const OctChild&& other) = delete; + OctChild& operator= (const OctChild& other) = delete; + void AddDynamicObject(const AABB& box); + void AddStaticObject(const AABB& box); + void BoxesInSameRegion(const AABB& box, std::vector& outBoxes) const; + void ClearObjects(); + void ClearDynamicObjects(); + bool RayCollides(const Ray& ray, Output& data) const; + bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const; + + OctChild* m_Children[8]; + //Indices into the lists in OctTree. + std::vector m_StaticObjIndices; + std::vector m_DynamicObjIndices; + AABB m_Box; + //Reference to the lists in OctTree. + std::vector& m_StaticObjectsRef; + std::vector& m_DynamicObjectsRef; + + inline bool hasChildren() const; + int childIndexContainingPoint(const glm::vec3& point) const; + std::vector childIndicesContainingBox(const AABB& box) const; + }; }; + #endif \ No newline at end of file diff --git a/include/Engine/Core/ResourceManager.h b/include/Engine/Core/ResourceManager.h index d408f5af..86ed6254 100644 --- a/include/Engine/Core/ResourceManager.h +++ b/include/Engine/Core/ResourceManager.h @@ -81,6 +81,8 @@ public: @param resourceName Fully qualified name of the resource to reload. */ static void Reload(std::string resourceName); + + static void Release(std::string resourceType, std::string resourceName); static void Update(); diff --git a/include/Engine/Core/System.h b/include/Engine/Core/System.h new file mode 100644 index 00000000..8e8c71b9 --- /dev/null +++ b/include/Engine/Core/System.h @@ -0,0 +1,25 @@ +#ifndef System_h__ +#define System_h__ + +#include "EventBroker.h" +#include "World.h" +#include "ComponentWrapper.h" + +class System +{ + friend class SystemPipeline; + +public: + System(const EventBroker* eventBroker, std::string componentType) + : m_EventBroker(eventBroker) + , m_ComponentType(componentType) + { } + + virtual void Update(World* world, ComponentWrapper& component, double dt) = 0; + +private: + const EventBroker* m_EventBroker; + std::string m_ComponentType; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/SystemPipeline.h b/include/Engine/Core/SystemPipeline.h new file mode 100644 index 00000000..24f98121 --- /dev/null +++ b/include/Engine/Core/SystemPipeline.h @@ -0,0 +1,58 @@ +#ifndef SystemPipeline_h__ +#define SystemPipeline_h__ + +#include "../Common.h" +#include "EventBroker.h" +#include "System.h" +#include "World.h" + +class SystemPipeline +{ +public: + SystemPipeline(const EventBroker* eventBroker) + : m_EventBroker(eventBroker) + { } + ~SystemPipeline() + { + for (auto& pair : m_Systems) { + for (auto& system : pair.second) { + delete system; + } + } + } + + template + void AddSystem(Arguments... args) + { + System* system = new T(m_EventBroker, args...); + if (!system->m_ComponentType.empty()) { + m_Systems[system->m_ComponentType].push_back(system); + } else { + LOG_ERROR("Failed to add system \"%s\": Missing component type!", typeid(T).name()); + delete system; + } + } + + void Update(World* world, double dt) + { + for (auto& pair : m_Systems) { + 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->Update(world, component, dt); + } + } + } + } + +private: + const EventBroker* m_EventBroker; + std::unordered_map> m_Systems; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 20bbe288..65dc3be7 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -2,7 +2,7 @@ #define World_h__ #include "../Common.h" -#include "EntityWrapper.h" +#include "Entity.h" #include "ObjectPool.h" #include "ComponentPool.h" @@ -12,6 +12,7 @@ public: World() = default; ~World(); + // Create empty entity EntityID CreateEntity(EntityID parent = 0); // Register a component type and allocate space for it @@ -21,10 +22,12 @@ public: // Get a component of an entity ComponentWrapper GetComponent(EntityID entity, std::string componentType); // Get all components of the specified type - const ComponentPool& GetComponents(std::string componentType); + const ComponentPool* GetComponents(std::string componentType); + // Get entity parent + EntityID GetParent(EntityID entity); private: - EntityID m_CurrentEntityID = 0; + EntityID m_CurrentEntityID = 1; std::unordered_map m_EntityParents; std::unordered_multimap m_EntityChildren; diff --git a/include/Engine/Rendering/RenderQueue.h b/include/Engine/Rendering/RenderQueue.h index 982b1813..2942c743 100644 --- a/include/Engine/Rendering/RenderQueue.h +++ b/include/Engine/Rendering/RenderQueue.h @@ -7,7 +7,7 @@ #include "../Common.h" #include "../GLM.h" #include "../Core/Util/Rectangle.h" -#include "../Core/EntityWrapper.h" +#include "../Core/Entity.h" class Model; class Skeleton; diff --git a/include/Engine/Rendering/RenderQueueFactory.h b/include/Engine/Rendering/RenderQueueFactory.h index 918692ac..b273b672 100644 --- a/include/Engine/Rendering/RenderQueueFactory.h +++ b/include/Engine/Rendering/RenderQueueFactory.h @@ -22,7 +22,10 @@ private: void FillLights(World* world, RenderQueue* renderQueue); glm::mat4 ModelMatrix(World* world, EntityID entity); - glm::vec3 GetAbsolutePosition(World* world, ComponentWrapper transformComponent); + + glm::vec3 AbsolutePosition(World* world, EntityID entity); + glm::quat AbsoluteOrientation(World* world, EntityID entity); + glm::vec3 AbsoluteScale(World* world, EntityID entity); }; #endif \ No newline at end of file diff --git a/include/Game/Game.h b/include/Game/Game.h index 8667b8c8..500ecf3a 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -9,8 +9,10 @@ #include "GUI/Frame.h" #include "Core/World.h" #include "Rendering/RenderQueueFactory.h" - -class OctTree; +#include "Core/EKeyDown.h" +#include "Core/EntityXMLFile.h" +#include "Core/SystemPipeline.h" +#include "RaptorCopterSystem.h" class Game { @@ -29,8 +31,14 @@ private: InputManager* m_InputManager; GUI::Frame* m_FrameStack; World* m_World; + SystemPipeline* m_SystemPipeline; RenderQueueFactory* m_RenderQueueFactory; - OctTree* m_OctTree; + + EventRelay m_EKeyUp; + bool testOnKeyUp(const Events::KeyUp& e); + + void testIntialize(); + void testTick(double dt); }; #endif diff --git a/include/Game/HardcodedTestWorld.h b/include/Game/HardcodedTestWorld.h deleted file mode 100644 index 5f7cc5f6..00000000 --- a/include/Game/HardcodedTestWorld.h +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include "GLM.h" -#include "Core/World.h" -#include "Core/Util/Any.h" - -class HardcodedTestWorld : public World -{ -public: - HardcodedTestWorld() - : World() - { - registerTestComponents(); - createTestEntities(); - } - -private: - void registerTestComponents() - { - ComponentWrapperFactory f; - - - f = ComponentWrapperFactory("Test"); - f.AddProperty("TestInteger", 1337); - f.AddProperty("TestFloat", 13.37f); - f.AddProperty("TestString", std::string("Carlito")); - RegisterComponent(f); - - f = ComponentWrapperFactory("Debug"); - f.AddProperty("Name", std::string("Unnamed")); - RegisterComponent(f); - - f = ComponentWrapperFactory("Transform"); - f.AddProperty("Position", glm::vec3(0.f, 0.f, 0.f)); - f.AddProperty("Orientation", glm::quat()); - f.AddProperty("Scale", glm::vec3(1.f, 1.f, 1.f)); - RegisterComponent(f); - - f = ComponentWrapperFactory("Model"); - f.AddProperty("Resource", std::string()); - f.AddProperty("Color", glm::vec4(1.f, 1.f, 1.f, 1.f)); - f.AddProperty("Visible", true); - RegisterComponent(f); - - f = ComponentWrapperFactory("Collision"); - f.AddProperty("BoxCenter", glm::vec3(0.f, 0.f, 0.f)); - f.AddProperty("BoxSize", glm::vec3(1.f, 1.f, 1.f)); - RegisterComponent(f); - } - - void createTestEntities() - { - World& world = *this; - - // Create an entity - EntityID e = world.CreateEntity(); - - // Attach a Debug component - ComponentWrapper debug = world.AttachComponent(e, "Debug"); - // Set the Name field of the Debug component using subscript operator - debug["Name"] = "Carlito"; - - // Attach a Transform component - world.AttachComponent(e, "Transform"); - // Fetch the component based on EntityID and component type - ComponentWrapper transform = world.GetComponent(e, "Transform"); - // Set the fields of the Transform component - transform["Position"] = glm::vec3(0.f, 0.f, 0.f); - transform["Scale"] = glm::vec3(1.f, 1.f, 1.f); - - // Move on the X axis by fetching field as reference - ((glm::vec3&)transform["Position"]).x += 10.f; - // Shrink by a factor of 100 - ((glm::vec3&)transform["Scale"]) /= 100.f; - - // Loop through all Transform components and print them - for (auto& transform : world.GetComponents("Transform")) { - glm::vec3 pos = transform["Position"]; - std::cout << "Position: " << pos.x << " " << pos.y << " " << pos.z << std::endl; - glm::vec3 scale = transform["Scale"]; - std::cout << "Scale: " << scale.x << " " << scale.y << " " << scale.z << std::endl; - - // Fetch the Debug component also present in this entity - ComponentWrapper debug = world.GetComponent(transform.EntityID, "Debug"); - std::cout << "Name: " << (std::string)debug["Name"] << std::endl; - } - - //Create some test widgets - { - EntityID entityScaleWidget = world.CreateEntity(); - ComponentWrapper transform = world.AttachComponent(entityScaleWidget, "Transform"); - transform["Position"] = glm::vec3(-1.5f, 0.f, 0.f); - ComponentWrapper model = world.AttachComponent(entityScaleWidget, "Model"); - model["Resource"] = "Models/ScaleWidget.obj"; - } - { - EntityID entityRotationWidget = world.CreateEntity(); - ComponentWrapper transform = world.AttachComponent(entityRotationWidget, "Transform"); - transform["Position"] = glm::vec3(1.5f, 0.f, 0.f); - ComponentWrapper model = world.AttachComponent(entityRotationWidget, "Model"); - model["Resource"] = "Models/RotationWidget.obj"; - } - { - EntityID entityDummyScene = world.CreateEntity(); - ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); - transform["Position"] = glm::vec3(0, 0.f, 0.f); - ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/DummyScene.obj"; - } - { - EntityID entityCollisionBox = world.CreateEntity(); - ComponentWrapper transform = world.AttachComponent(entityCollisionBox, "Transform"); - transform["Position"] = glm::vec3(0.f, 2.f, 0.f); - ComponentWrapper model = world.AttachComponent(entityCollisionBox, "Model"); - model["Resource"] = "Models/Core/UnitBox.obj"; - - ComponentWrapper collision = world.AttachComponent(entityCollisionBox, "Collision"); - glm::vec3 pos = transform["Position"]; - glm::vec3 scale = transform["Scale"]; - glm::quat ori = transform["Orientation"]; - Model* modelRes = ResourceManager::Load(model["Resource"]); - - //WTODO: This only works for objects that never moves/scales/rotates since modelMatrix don't change. - //For dynamic objects, should save a collision box in modelspace and transform box with modelMatrix per collision check. - glm::mat4 modelMatrix = modelRes->m_Matrix * glm::translate(glm::mat4(), pos) * glm::toMat4(ori) * glm::scale(scale); - - glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY); - glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY); - for (const auto& v : modelRes->m_Vertices) { - const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1); - maxi.x = std::max(wPos.x, maxi.x); - maxi.y = std::max(wPos.y, maxi.y); - maxi.z = std::max(wPos.z, maxi.z); - mini.x = std::min(wPos.x, mini.x); - mini.y = std::min(wPos.y, mini.y); - mini.z = std::min(wPos.z, mini.z); - } - collision["BoxCenter"] = 0.5f * (maxi + mini); - collision["BoxSize"] = maxi - mini; - } - - - - } -}; \ No newline at end of file diff --git a/include/Game/RaptorCopterSystem.h b/include/Game/RaptorCopterSystem.h new file mode 100644 index 00000000..55647004 --- /dev/null +++ b/include/Game/RaptorCopterSystem.h @@ -0,0 +1,16 @@ +#include "Common.h" +#include "Core/System.h" + +class RaptorCopterSystem : public System +{ +public: + RaptorCopterSystem(const EventBroker* eventBroker) + : System(eventBroker, "RaptorCopter") + { } + + virtual void Update(World* world, ComponentWrapper& raptorCopter, double dt) override + { + ComponentWrapper& transform = world->GetComponent(raptorCopter.EntityID, "Transform"); + (glm::vec3&)transform["Orientation"] += (float)(double)raptorCopter["Speed"] * (float)dt * (glm::vec3)raptorCopter["Axis"]; + } +}; \ No newline at end of file diff --git a/resources/DefaultConfig.ini b/resources/DefaultConfig.ini index ac7e9ec2..535e109b 100644 --- a/resources/DefaultConfig.ini +++ b/resources/DefaultConfig.ini @@ -1,5 +1,6 @@ [Debug] LogLevel=1 +LoadMap= [Video] Fullscreen=false diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 41564706..d4160700 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -2,5 +2,7 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/Model.xml b/resources/Schema/Components/Model.xml new file mode 100644 index 00000000..bd20147f --- /dev/null +++ b/resources/Schema/Components/Model.xml @@ -0,0 +1,5 @@ + + + + true + \ No newline at end of file diff --git a/resources/Schema/Components/Model.xsd b/resources/Schema/Components/Model.xsd new file mode 100644 index 00000000..fb0774f8 --- /dev/null +++ b/resources/Schema/Components/Model.xsd @@ -0,0 +1,24 @@ + + + + + + + + A visible model loaded from disk + + + + + Model file + + + Color tint + + + Wether the model is visible or not + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/RaptorCopter.xml b/resources/Schema/Components/RaptorCopter.xml new file mode 100644 index 00000000..cc1ece52 --- /dev/null +++ b/resources/Schema/Components/RaptorCopter.xml @@ -0,0 +1,4 @@ + + 0 + + \ No newline at end of file diff --git a/resources/Schema/Components/RaptorCopter.xsd b/resources/Schema/Components/RaptorCopter.xsd new file mode 100644 index 00000000..23ee8a96 --- /dev/null +++ b/resources/Schema/Components/RaptorCopter.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Test.xml b/resources/Schema/Components/Test.xml index ccf459fe..9e49d37a 100644 --- a/resources/Schema/Components/Test.xml +++ b/resources/Schema/Components/Test.xml @@ -2,5 +2,5 @@ 1 1.333 - + \ No newline at end of file diff --git a/resources/Schema/Components/Transform.xml b/resources/Schema/Components/Transform.xml index e093c41a..276b89e7 100644 --- a/resources/Schema/Components/Transform.xml +++ b/resources/Schema/Components/Transform.xml @@ -1,5 +1,5 @@ - + - + - \ No newline at end of file + \ No newline at end of file diff --git a/resources/Schema/Components/Transform.xsd b/resources/Schema/Components/Transform.xsd index d28ff601..b8d24777 100644 --- a/resources/Schema/Components/Transform.xsd +++ b/resources/Schema/Components/Transform.xsd @@ -12,7 +12,7 @@ The position vector - + diff --git a/resources/Schema/Entities/Test.xml b/resources/Schema/Entities/Test.xml index 8ca6b684..2c3ea18e 100644 --- a/resources/Schema/Entities/Test.xml +++ b/resources/Schema/Entities/Test.xml @@ -3,18 +3,114 @@ - - - + - - 12 - 11.11 - Hello World - + + Models/DummyScene.obj + - - + + + + + + + + Models/ScaleWidget.obj + + + + + + + + + + Models/RotationWidget.obj + + + + + + + + + + + + + + + + + + + + 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/Types.xsd b/resources/Schema/Types.xsd index 939c88da..cd464201 100644 --- a/resources/Schema/Types.xsd +++ b/resources/Schema/Types.xsd @@ -3,6 +3,10 @@ + + + + diff --git a/resources/Schema/Types/Color.xsd b/resources/Schema/Types/Color.xsd new file mode 100644 index 00000000..4fb9572e --- /dev/null +++ b/resources/Schema/Types/Color.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 2f0a6311..695ae7d1 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -7,11 +7,13 @@ - + + + diff --git a/src/Engine/Core/EntityXMLFile.cpp b/src/Engine/Core/EntityXMLFile.cpp new file mode 100644 index 00000000..b96e366e --- /dev/null +++ b/src/Engine/Core/EntityXMLFile.cpp @@ -0,0 +1,481 @@ +#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) }, + { "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_float, status); + if (val == nullptr) { + LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", XSTR(element->getTagName()), attribute); + return 0.f; + } else { + return val->fData.fValue.f_float; + } +} + +void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string typeName, char* outData) +{ + using namespace xercesc; + + XSValue::DataType dataType = XSValue::getDataType(XSTR(typeName.c_str())); + if (dataType == XSValue::DataType::dt_MAXCOUNT) { + 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 (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)); + } +} + diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 582e3acd..63eba63e 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -21,6 +21,16 @@ bool isFirstLower(const ChildInfo& first, const ChildInfo& second) return first.Distance < second.Distance; } +bool isSameBoxProbably(const AABB& first, const AABB& second) +{ + const float EPS = 0.0001f; + const auto& ma = first.MaxCorner(); + const auto& mi = first.MinCorner(); + return (std::abs(ma.x - mi.x) < EPS) && + (std::abs(ma.z - mi.z) < EPS) && + (std::abs(ma.y - mi.y) < EPS); +} + } OctTree::OctTree() @@ -28,11 +38,79 @@ OctTree::OctTree() {} OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) - : m_Box(octTreeBounds) + : m_Root(new OctChild(octTreeBounds, subDivisions, m_StaticObjects, m_DynamicObjects)) , m_UpdatedOnce(false) +{} + +OctTree::~OctTree() +{ + delete m_Root; +} + +void OctTree::AddDynamicObject(const AABB& box) +{ + m_Root->AddDynamicObject(box); + m_DynamicObjects.push_back(box); +} + +void OctTree::AddStaticObject(const AABB& box) +{ + m_Root->AddStaticObject(box); + m_StaticObjects.push_back(box); +} + +void OctTree::BoxesInSameRegion(const AABB& box, std::vector& outBoxes) +{ + falsifyObjectChecks(); + m_Root->BoxesInSameRegion(box, outBoxes); +} + +void OctTree::ClearObjects() +{ + m_StaticObjects.clear(); + m_DynamicObjects.clear(); + m_Root->ClearObjects(); +} + +void OctTree::ClearDynamicObjects() +{ + m_DynamicObjects.clear(); + m_Root->ClearDynamicObjects(); +} + +bool OctTree::RayCollides(const Ray& ray, Output& data) +{ + falsifyObjectChecks(); + data.CollideDistance = -1; + return m_Root->RayCollides(ray, data); +} + +bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) +{ + falsifyObjectChecks(); + return m_Root->BoxCollides(boxToTest, outBoxIntersected); +} + +void OctTree::falsifyObjectChecks() +{ + for (auto& obj : m_StaticObjects) { + obj.Checked = false; + } + for (auto& obj : m_DynamicObjects) { + obj.Checked = false; + } +} + +OctTree::OctChild::OctChild(const AABB& octTreeBounds, + int subDivisions, + std::vector& staticObjects, + std::vector& dynamicObjects) + : m_Box(octTreeBounds) + , m_StaticObjectsRef(staticObjects) + , m_DynamicObjectsRef(dynamicObjects) { if (subDivisions == 0) { - for (OctTree*& c : m_Children) { + for (OctChild*& c : m_Children) { c = nullptr; } } else { @@ -68,14 +146,14 @@ OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) minPos.z = parentMin.z; maxPos.z = parentCenter.z; } - m_Children[i] = new OctTree(AABB(minPos, maxPos), subDivisions); + m_Children[i] = new OctChild(AABB(minPos, maxPos), subDivisions, m_StaticObjectsRef, m_DynamicObjectsRef); } } } -OctTree::~OctTree() +OctTree::OctChild::~OctChild() { - for (OctTree*& c : m_Children) { + for (OctChild*& c : m_Children) { if (c != nullptr) { delete c; c = nullptr; @@ -85,19 +163,22 @@ OctTree::~OctTree() void OctTree::Update(float dt, World* world, Camera* cam) { - AABB aabb; for (ComponentWrapper& c : world->GetComponents("Collision")) { + AABB aabb; aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]); - AddStaticObject(aabb); + AddDynamicObject(aabb); } const glm::vec4 redCol = glm::vec4(1, 0.2f, 0, 1); const glm::vec4 greenCol = glm::vec4(0.1f, 1.0f, 0.25f, 1); - const glm::vec3 boxSize = 0.1f*glm::vec3(1.0f, 1.0f, 1.0f); + const glm::vec4 blueCol = glm::vec4(0.1f, 0.05f, 0.95f, 1); + const glm::vec4 cyanCol = glm::vec4(0.1f, 0.9f, 0.85f, 1); + const glm::vec3 boxSize = 0.05f*glm::vec3(1.0f, 1.0f, 1.0f); if (!m_UpdatedOnce) { m_BoxID = world->CreateEntity(); ComponentWrapper transform = world->AttachComponent(m_BoxID, "Transform"); transform["Scale"] = boxSize; + ComponentWrapper model = world->AttachComponent(m_BoxID, "Model"); model["Resource"] = "Models/Core/UnitBox.obj"; m_UpdatedOnce = true; @@ -109,21 +190,24 @@ void OctTree::Update(float dt, World* world, Camera* cam) ComponentWrapper transform = world->GetComponent(m_BoxID, "Transform"); transform["Position"] = boxPos; ComponentWrapper model = world->GetComponent(m_BoxID, "Model"); - if (BoxCollides(box, AABB())) { - //if (Collision::AABBVsAABB(box, aabb)) { + bool collBox = BoxCollides(box, AABB()); + if (collBox) { cam->SetPosition(m_PrevPos); cam->SetOrientation(m_PrevOri); - model["Color"] = greenCol; + bool collRay = RayCollides({ cam->Position(), cam->Forward() }, Output()); + model["Color"] = collRay ? cyanCol : greenCol; + } else if (RayCollides({ cam->Position(), cam->Forward() }, Output())) { + model["Color"] = blueCol; } else { model["Color"] = redCol; } m_PrevPos = cam->Position(); m_PrevOri = cam->Orientation(); - ClearObjects(); + ClearDynamicObjects(); } -bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const +bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const { if (hasChildren()) { for (int i : childIndicesContainingBox(boxToTest)) { @@ -131,23 +215,32 @@ bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const return true; } } else { - std::vector> objVectors = { - m_StaticObjects, - m_DynamicObjects - }; - for (const auto& objVector : objVectors) { - for (const auto& obj : objVector) { - if (Collision::AABBVsAABB(boxToTest, obj)) { - outBoxIntersected = obj; + for (int i : m_StaticObjIndices) { + if (!m_StaticObjectsRef[i].Checked) { + const AABB& objBox = m_StaticObjectsRef[i].Box; + if (Collision::AABBVsAABB(boxToTest, objBox)) { + outBoxIntersected = objBox; return true; } + m_StaticObjectsRef[i].Checked = true; + } + } + for (int i : m_DynamicObjIndices) { + if (!m_DynamicObjectsRef[i].Checked) { + const AABB& objBox = m_DynamicObjectsRef[i].Box; + if (!isSameBoxProbably(boxToTest, objBox) && + Collision::AABBVsAABB(boxToTest, objBox)) { + outBoxIntersected = objBox; + return true; + } + m_DynamicObjectsRef[i].Checked = true; } } } return false; } -bool OctTree::RayCollides(const Ray& ray, Output& data) const +bool OctTree::OctChild::RayCollides(const Ray& ray, Output& data) const { //If the node AABB is missed, everything it contains is missed. if (Collision::RayAABBIntr(ray, m_Box)) { @@ -170,18 +263,25 @@ bool OctTree::RayCollides(const Ray& ray, Output& data) const //Check against boxes in the node. float minDist = INFINITY; bool intersected = false; - std::vector> objVectors = { - m_StaticObjects, - m_DynamicObjects - }; - for (const auto& objVector : objVectors) { - for (const auto& obj : objVector) { - float dist; - if (Collision::RayVsAABB(ray, obj, dist)) { - minDist = std::min(dist, minDist); - intersected = true; - } + for (int i : m_StaticObjIndices) { + float dist; + //If we haven't tested against this object before, and the ray hits. + if (!m_StaticObjectsRef[i].Checked && + Collision::RayVsAABB(ray, m_StaticObjectsRef[i].Box, dist)) { + minDist = std::min(dist, minDist); + intersected = true; } + m_StaticObjectsRef[i].Checked = true; + } + for (int i : m_DynamicObjIndices) { + float dist; + //If we haven't tested against this object before, and the ray hits. + if (!m_DynamicObjectsRef[i].Checked && + Collision::RayVsAABB(ray, m_DynamicObjectsRef[i].Box, dist)) { + minDist = std::min(dist, minDist); + intersected = true; + } + m_DynamicObjectsRef[i].Checked = true; } data.CollideDistance = minDist; @@ -192,60 +292,84 @@ bool OctTree::RayCollides(const Ray& ray, Output& data) const } -void OctTree::AddDynamicObject(const AABB& box) +void OctTree::OctChild::AddDynamicObject(const AABB& box) { if (hasChildren()) { for (auto i : childIndicesContainingBox(box)) { m_Children[i]->AddDynamicObject(box); } } else { - m_DynamicObjects.push_back(box); + //Since it hasn't been added yet to the real object list, the index is after the last =size. + m_DynamicObjIndices.push_back(m_DynamicObjectsRef.size()); } } -void OctTree::AddStaticObject(const AABB& box) +void OctTree::OctChild::AddStaticObject(const AABB& box) { if (hasChildren()) { for (auto i : childIndicesContainingBox(box)) { m_Children[i]->AddStaticObject(box); } } else { - m_StaticObjects.push_back(box); + //Since it hasn't been added yet to the real object list, the index is after the last =size. + m_StaticObjIndices.push_back(m_StaticObjectsRef.size()); } } -void OctTree::BoxesInSameRegion(const AABB& box, std::vector& outBoxes) const +void OctTree::OctChild::BoxesInSameRegion(const AABB& box, std::vector& outBoxes) const { if (hasChildren()) { for (auto i : childIndicesContainingBox(box)) { m_Children[i]->BoxesInSameRegion(box, outBoxes); } } else { - outBoxes.insert(outBoxes.end(), m_StaticObjects.begin(), m_StaticObjects.end()); - outBoxes.insert(outBoxes.end(), m_DynamicObjects.begin(), m_DynamicObjects.end()); + int startIndex = outBoxes.size(); + int numDuplicates = 0; + outBoxes.resize(outBoxes.size() + m_StaticObjIndices.size() + m_DynamicObjIndices.size()); + for (size_t i = 0; i < m_StaticObjIndices.size(); ++i){ + ContainedObject& obj = m_StaticObjectsRef[m_StaticObjIndices[i]]; + if (obj.Checked) { + ++numDuplicates; + } else { + obj.Checked = true; + outBoxes[startIndex + i - numDuplicates] = obj.Box; + } + } + for (size_t i = 0; i < m_DynamicObjIndices.size(); ++i) { + ContainedObject& obj = m_DynamicObjectsRef[m_DynamicObjIndices[i]]; + if (obj.Checked) { + ++numDuplicates; + } else { + obj.Checked = true; + outBoxes[startIndex + i - numDuplicates] = obj.Box; + } + } + for (size_t i = 0; i < numDuplicates; ++i) { + outBoxes.pop_back(); + } } } -void OctTree::ClearObjects() +void OctTree::OctChild::ClearObjects() { if (hasChildren()) { - for (OctTree*& c : m_Children) { + for (OctChild*& c : m_Children) { c->ClearObjects(); } } else { - m_DynamicObjects.clear(); - m_StaticObjects.clear(); + m_DynamicObjIndices.clear(); + m_StaticObjIndices.clear(); } } -void OctTree::ClearDynamicObjects() +void OctTree::OctChild::ClearDynamicObjects() { if (hasChildren()) { - for (OctTree*& c : m_Children) { + for (OctChild*& c : m_Children) { c->ClearObjects(); } } else { - m_DynamicObjects.clear(); + m_DynamicObjIndices.clear(); } } @@ -261,13 +385,13 @@ void OctTree::ClearDynamicObjects() // x : - - - - + + + + // y : - - + + - - + + // z : - + - + - + - + -int OctTree::childIndexContainingPoint(const glm::vec3& point) const +int OctTree::OctChild::childIndexContainingPoint(const glm::vec3& point) const { const glm::vec3& c = m_Box.Center(); return (1 << 2) * (point.x >= c.x) | (1 << 1) * (point.y >= c.y) | (point.z >= c.z); } -std::vector OctTree::childIndicesContainingBox(const AABB& box) const +std::vector OctTree::OctChild::childIndicesContainingBox(const AABB& box) const { int minInd = childIndexContainingPoint(box.MinCorner()); int maxInd = childIndexContainingPoint(box.MaxCorner()); @@ -305,7 +429,7 @@ std::vector OctTree::childIndicesContainingBox(const AABB& box) const } } -inline bool OctTree::hasChildren() const +inline bool OctTree::OctChild::hasChildren() const { return m_Children[0] != nullptr; } \ No newline at end of file diff --git a/src/Engine/Core/ResourceManager.cpp b/src/Engine/Core/ResourceManager.cpp index 325bfab8..81823dbf 100644 --- a/src/Engine/Core/ResourceManager.cpp +++ b/src/Engine/Core/ResourceManager.cpp @@ -35,6 +35,20 @@ void ResourceManager::Reload(std::string resourceName) } } + +void ResourceManager::Release(std::string resourceType, std::string resourceName) +{ + auto key = std::make_pair(resourceType, resourceName); + if (m_ResourceCache.find(key) == m_ResourceCache.end()) { + return; + } + auto resource = m_ResourceCache.at(key); + m_ResourceCache.erase(key); + m_ResourceFromName.erase(resourceName); + m_ResourceParents.erase(resource); + delete resource; +} + unsigned int ResourceManager::GetNewResourceID(unsigned int typeID) { return m_ResourceCount[typeID]++; diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 26d3f1e4..dfab23cd 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -41,9 +41,16 @@ ComponentWrapper World::GetComponent(EntityID entity, std::string componentType) return pool->GetByEntity(entity); } -const ComponentPool& World::GetComponents(std::string componentType) +const ComponentPool* World::GetComponents(std::string componentType) { - return *m_ComponentPools.at(componentType); + auto it = m_ComponentPools.find(componentType); + return (it != m_ComponentPools.end()) ? it->second : nullptr; +} + + +EntityID World::GetParent(EntityID entity) +{ + return m_EntityParents.at(entity); } EntityID World::generateEntityID() diff --git a/src/Engine/Rendering/RawModel.cpp b/src/Engine/Rendering/RawModel.cpp index c14fec08..962c278f 100644 --- a/src/Engine/Rendering/RawModel.cpp +++ b/src/Engine/Rendering/RawModel.cpp @@ -34,10 +34,10 @@ RawModel::RawModel(std::string fileName) numIndices += face.mNumIndices; } } - LOG_DEBUG("Vertex count %i", numVertices); - LOG_DEBUG("Index count %i", numIndices); + //LOG_DEBUG("Vertex count %i", numVertices); + //LOG_DEBUG("Index count %i", numIndices); - LOG_DEBUG("Model has %i embedded textures", scene->mNumTextures); + //LOG_DEBUG("Model has %i embedded textures", scene->mNumTextures); std::vector> boneInfo; std::map boneNameMapping; @@ -132,35 +132,35 @@ RawModel::RawModel(std::string fileName) matGroup.EndIndex = m_Indices.size() - 1; // Material shininess material->Get(AI_MATKEY_SHININESS, matGroup.Shininess); - LOG_DEBUG("Shininess: %f", matGroup.Shininess); + //LOG_DEBUG("Shininess: %f", matGroup.Shininess); // Diffuse texture - LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE)); + //LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE)); if (material->GetTextureCount(aiTextureType_DIFFUSE)) { aiString path; aiTextureMapping mapping; material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping); std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str()); + //LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str()); matGroup.Texture = std::shared_ptr(ResourceManager::Load(absolutePath)); } // Normal map - LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); + //LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); if (material->GetTextureCount(aiTextureType_HEIGHT)) { aiString path; aiTextureMapping mapping; material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping); std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - LOG_DEBUG("Normal map: %s", absolutePath.c_str()); + //LOG_DEBUG("Normal map: %s", absolutePath.c_str()); matGroup.NormalMap = std::shared_ptr(ResourceManager::Load(absolutePath)); } // Specular map - LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); + //LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); if (material->GetTextureCount(aiTextureType_SPECULAR)) { aiString path; aiTextureMapping mapping; material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping); std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - LOG_DEBUG("Specular map: %s", absolutePath.c_str()); + //LOG_DEBUG("Specular map: %s", absolutePath.c_str()); matGroup.SpecularMap = std::shared_ptr(ResourceManager::Load(absolutePath)); } TextureGroups.push_back(matGroup); @@ -216,20 +216,20 @@ RawModel::RawModel(std::string fileName) m_Skeleton = new Skeleton(); CreateSkeleton(boneInfo, boneNameMapping, scene->mRootNode, -1); int numBones = m_Skeleton->Bones.size(); - LOG_DEBUG("Bone count: %i", numBones); + //LOG_DEBUG("Bone count: %i", numBones); if (numBones > 0) { m_Skeleton->PrintSkeleton(); } } // Animations - LOG_DEBUG("Animation count: %i", scene->mNumAnimations); + //LOG_DEBUG("Animation count: %i", scene->mNumAnimations); for (int i = 0; i < scene->mNumAnimations; ++i) { auto animation = scene->mAnimations[i]; std::string animationName = animation->mName.C_Str(); - LOG_DEBUG("Animation: %s", animationName.c_str()); - LOG_DEBUG("Duration: %f", animation->mDuration); - LOG_DEBUG("Ticks per second: %f", animation->mTicksPerSecond); + //LOG_DEBUG("Animation: %s", animationName.c_str()); + //LOG_DEBUG("Duration: %f", animation->mDuration); + //LOG_DEBUG("Ticks per second: %f", animation->mTicksPerSecond); Skeleton::Animation skelAnim; skelAnim.Name = animationName; @@ -303,7 +303,7 @@ void RawModel::CreateSkeleton(std::vector> &b // Find the bone by name in the bone info list if (boneNameMapping.find(nodeName) == boneNameMapping.end()) { - LOG_DEBUG("Node \"%s\" was not a bone", nodeName.c_str()); + //LOG_DEBUG("Node \"%s\" was not a bone", nodeName.c_str()); } else { glm::mat4 offsetMatrix; int ID = boneNameMapping[nodeName]; diff --git a/src/Engine/Rendering/RenderQueueFactory.cpp b/src/Engine/Rendering/RenderQueueFactory.cpp index 9a7d89a1..81d7391e 100644 --- a/src/Engine/Rendering/RenderQueueFactory.cpp +++ b/src/Engine/Rendering/RenderQueueFactory.cpp @@ -15,49 +15,87 @@ void RenderQueueFactory::Update(World* world) glm::mat4 RenderQueueFactory::ModelMatrix(World* world, EntityID entity) { - //should really return absolute model matrix based on parents position, scale and orientation - //GetAbsolutePosition(World* world, ComponentWrapper transformComponent) + glm::vec3 position = AbsolutePosition(world, entity); + glm::quat orientation = AbsoluteOrientation(world, entity); + glm::vec3 scale = AbsoluteScale(world, entity); - ComponentWrapper transformComponent = world->GetComponent(entity, "Transform"); - glm::vec3 position = transformComponent["Position"]; - glm::vec3 scale = transformComponent["Scale"]; - glm::quat oritentation = transformComponent["Orientation"]; - - glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(oritentation) * glm::scale(scale); + glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(orientation) * glm::scale(scale); return modelMatrix; } -glm::vec3 GetAbsolutePosition(World* world, ComponentWrapper transformComponent) + +glm::vec3 RenderQueueFactory::AbsolutePosition(World* world, EntityID entity) { - // positionComponent.EntityID - return glm::vec3(); + glm::vec3 position; + + do { + ComponentWrapper transform = world->GetComponent(entity, "Transform"); + position += AbsoluteOrientation(world, entity) * (glm::vec3)transform["Position"]; + entity = world->GetParent(entity); + } 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) +{ + ComponentWrapper transform = world->GetComponent(entity, "Transform"); + glm::vec3 scale = (glm::vec3)transform["Scale"]; + + EntityID parent = world->GetParent(entity); + if (parent != 0) { + return AbsoluteScale(world, parent) * scale; + } else { + return scale; + } } void RenderQueueFactory::FillModels(World* world, RenderQueue* renderQueue) { - for(auto& modelC : world->GetComponents("Model")) { - ModelJob job; - std::string resource = modelC["Resource"]; - glm::vec4 color = modelC["Color"]; - Model* model = ResourceManager::Load(resource); + auto models = world->GetComponents("Model"); + if (models == nullptr) { + return; + } - for (auto texGroup : model->TextureGroups) { - 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->m_Matrix * ModelMatrix(world, modelC.EntityID); - job.Color = color; + for (auto& modelC : *models) { + std::string resource = modelC["Resource"]; + if (resource.empty()) { + continue; + } + glm::vec4 color = modelC["Color"]; + Model* model = ResourceManager::Load(resource); - //TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this - job.Entity = modelC.EntityID; + 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->m_Matrix * ModelMatrix(world, modelC.EntityID); + job.Color = color; - renderQueue->Add(job); - } - } + //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/Game/Game.cpp b/src/Game/Game.cpp index 03e71dce..15dde3a0 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,12 +1,11 @@ #include "Game.h" -#include "HardcodedTestWorld.h" -#include "Core/OctTree.h" Game::Game(int argc, char* argv[]) { ResourceManager::RegisterType("ConfigFile"); ResourceManager::RegisterType("Model"); ResourceManager::RegisterType("Texture"); + ResourceManager::RegisterType("EntityXMLFile"); m_Config = ResourceManager::Load("Config.ini"); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); @@ -36,21 +35,26 @@ Game::Game(int argc, char* argv[]) m_FrameStack->Width = m_Renderer->Resolution().Width; m_FrameStack->Height = m_Renderer->Resolution().Height; - // Create a TEST WORLD - m_World = new HardcodedTestWorld(); - - //WTODO: Current worldsize is temp. - glm::vec3 worldSize = glm::vec3(50, 50, 50); - m_OctTree = new OctTree(AABB(-0.5f*worldSize, 0.5f*worldSize), 2); + // 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(); m_LastTime = glfwGetTime(); + + testIntialize(); } Game::~Game() { delete m_FrameStack; delete m_EventBroker; - delete m_OctTree; } void Game::Tick() @@ -62,13 +66,13 @@ void Game::Tick() m_EventBroker->Swap(); m_InputManager->Update(dt); m_Renderer->Update(dt); - - m_OctTree->Update(dt, m_World, m_Renderer->Camera()); - m_EventBroker->Swap(); - m_RenderQueueFactory->Update(m_World); + // Iterate through systems and update world! + m_SystemPipeline->Update(m_World, dt); + testTick(dt); + m_RenderQueueFactory->Update(m_World); m_Renderer->Draw(m_RenderQueueFactory->RenderQueues()); m_EventBroker->Swap(); @@ -76,3 +80,29 @@ void Game::Tick() glfwPollEvents(); } + + +bool Game::testOnKeyUp(const Events::KeyUp& e) +{ + if (e.KeyCode == GLFW_KEY_R) { + std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); + if (!mapToLoad.empty()) { + delete m_World; + m_World = new World(); + ResourceManager::Release("EntityXMLFile", mapToLoad); + ResourceManager::Load(mapToLoad)->PopulateWorld(m_World); + } + } + + return false; +} + +void Game::testIntialize() +{ + EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &Game::testOnKeyUp); +} + +void Game::testTick(double dt) +{ + m_EventBroker->Process(); +} diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index b0b5c25d..23a09fcd 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -45,4 +45,24 @@ BOOST_AUTO_TEST_CASE(octTreeTest2) } } +BOOST_AUTO_TEST_CASE(octSameRegionTest) +{ + glm::vec3 mini = glm::vec3(-1, -1, -1); + glm::vec3 maxi = glm::vec3(1, 1, 1); + OctTree tree(AABB(mini, maxi), 2); + AABB firstQuadrant(mini, 0.8f*mini); + tree.AddStaticObject(firstQuadrant); + AABB testBox(0.9f*mini, 0.8f*mini); + std::vector region; + tree.BoxesInSameRegion(testBox, region); + BOOST_REQUIRE(region.size() == 1); + AABB& box = region[0]; + BOOST_CHECK_CLOSE_FRACTION(box.Center().x, firstQuadrant.Center().x, 0.00001f); + BOOST_CHECK_CLOSE_FRACTION(box.Center().y, firstQuadrant.Center().y, 0.00001f); + BOOST_CHECK_CLOSE_FRACTION(box.Center().z, firstQuadrant.Center().z, 0.00001f); + BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().x, firstQuadrant.HalfSize().x, 0.00001f); + BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().y, firstQuadrant.HalfSize().y, 0.00001f); + BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().z, firstQuadrant.HalfSize().z, 0.00001f); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/Tests/WorldTest.cpp b/src/Tests/WorldTest.cpp index 4fd4ceed..8d92a328 100644 --- a/src/Tests/WorldTest.cpp +++ b/src/Tests/WorldTest.cpp @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(WorldTestMultipleAllocations, * utf::tolerance(0.00001)) // Loop through them and check data int i = 0; - for (auto& c : w.GetComponents("Test")) { + for (auto& c : *w.GetComponents("Test")) { BOOST_TEST((int)c["TestInteger"] == i); i++; }