From 713ebbe18c21a098010a5496813b9dff3ce45947 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 19 Jan 2016 14:50:46 +0100 Subject: [PATCH] Can add any objects that inherit from AABB into the Octree. --- .../Engine/Collision/CollidableOctreeSystem.h | 4 +- include/Engine/Collision/CollisionSystem.h | 4 +- include/Engine/Collision/TriggerSystem.h | 4 +- include/Engine/Core/Octree.h | 258 +++++++++++++----- include/Game/Game.h | 4 +- src/Engine/Collision/CollisionSystem.cpp | 2 +- src/Engine/Core/Octree.cpp | 133 ++------- src/Game/Game.cpp | 4 +- 8 files changed, 224 insertions(+), 189 deletions(-) diff --git a/include/Engine/Collision/CollidableOctreeSystem.h b/include/Engine/Collision/CollidableOctreeSystem.h index 8fa1f0a4..c61e774c 100644 --- a/include/Engine/Collision/CollidableOctreeSystem.h +++ b/include/Engine/Collision/CollidableOctreeSystem.h @@ -8,7 +8,7 @@ class CollidableOctreeSystem : public ImpureSystem, public PureSystem { public: - CollidableOctreeSystem(EventBroker* eventBroker, Octree* octree) + CollidableOctreeSystem(EventBroker* eventBroker, Octree* octree) : System(eventBroker) , PureSystem("Collidable") , m_Octree(octree) @@ -18,7 +18,7 @@ public: virtual void UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt) override; private: - Octree* m_Octree; + Octree* m_Octree; }; #endif \ No newline at end of file diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h index 561c5158..7815254d 100644 --- a/include/Engine/Collision/CollisionSystem.h +++ b/include/Engine/Collision/CollisionSystem.h @@ -13,7 +13,7 @@ class CollisionSystem : public PureSystem { public: - CollisionSystem(EventBroker* eventBroker, Octree* octree) + CollisionSystem(EventBroker* eventBroker, Octree* octree) : System(eventBroker) , PureSystem("Collidable") , m_Octree(octree) @@ -26,7 +26,7 @@ public: virtual void UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt) override; private: - Octree* m_Octree; + Octree* m_Octree; bool zPress; EventRelay m_EKeyUp; diff --git a/include/Engine/Collision/TriggerSystem.h b/include/Engine/Collision/TriggerSystem.h index 65e7c271..1b423e76 100644 --- a/include/Engine/Collision/TriggerSystem.h +++ b/include/Engine/Collision/TriggerSystem.h @@ -14,7 +14,7 @@ class AABB; class TriggerSystem : public PureSystem { public: - TriggerSystem(EventBroker* eventBroker, Octree* octree) + TriggerSystem(EventBroker* eventBroker, Octree* octree) : System(eventBroker) , PureSystem("Trigger") , m_Octree(octree) @@ -27,7 +27,7 @@ public: virtual void UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt) override; private: - Octree* m_Octree; + Octree* m_Octree; std::unordered_map> m_EntitiesTouchingTrigger; std::unordered_map> m_EntitiesCompletelyInTrigger; diff --git a/include/Engine/Core/Octree.h b/include/Engine/Core/Octree.h index 954dbcbc..bbd27b3c 100644 --- a/include/Engine/Core/Octree.h +++ b/include/Engine/Core/Octree.h @@ -1,19 +1,27 @@ #ifndef Octree_h__ #define Octree_h__ +#include + #include "../Common.h" #include "AABB.h" +//Fwd declarations. class Ray; +namespace OctSpace +{ +struct Output; +struct ContainedObject; +struct Child; +} + +//T needs to be AABB, or inherit from AABB. +//T also needs to have a default constructor. +template class Octree { public: - struct Output - { - float CollideDistance; - }; - Octree() = delete; ~Octree(); //For the root Octree, [octreeBounds] should be a box containing the entire level. @@ -25,81 +33,201 @@ public: Octree(const Octree&& other) = delete; Octree& operator= (const Octree& other) = delete; //Add a dynamic object (one that moves around) into the tree. - void AddDynamicObject(const AABB& box); + void AddDynamicObject(const T& object); //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); + void AddStaticObject(const T& object); + //Get the objects that are in the same area as the input [box], the objects are put in [outObjects]. + //The type Box must be AABB, or inherit from AABB. + template + void ObjectsInSameRegion(const Box& box, std::vector& outObjects); //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(); //Returns true if the ray collides with something in the tree. Result is written to [data]. - bool RayCollides(const Ray& ray, Output& data); + bool RayCollides(const Ray& ray, OctSpace::Output& data); //Returns true if the box collides with something in the tree. //On collision with a box, that box is written to [outBoxIntersected]. - //Note: More efficient than calling BoxesInSameRegion from outside and testing there. + //Note: More efficient than calling ObjectsInSameRegion from outside and testing there. bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected); private: - struct Child; //Fwd declaration; - struct ContainedObject - { - ContainedObject() - : Box(AABB()) - , Checked(false) - {} - ContainedObject(AABB box) - : Box(box) - , Checked(false) - {} - AABB Box; - bool Checked; - }; - Child* 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; + OctSpace::Child* m_Root; + std::vector m_StaticObjects; + std::vector m_DynamicObjects; void falsifyObjectChecks(); - - struct Child - { - ~Child(); - Child(const AABB& octTreeBounds, - int subDivisions, - std::vector& staticObjects, - std::vector& dynamicObjects); - Child(const Child& other) = delete; - Child(const Child&& other) = delete; - Child& operator= (const Child& 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; - - Child* m_Children[8]; - //Indices into the lists in Octree. - std::vector m_StaticObjIndices; - std::vector m_DynamicObjIndices; - AABB m_Box; - //Reference to the lists in Octree. - 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; - }; }; +namespace OctSpace +{ + +struct Output +{ + float CollideDistance; +}; + +struct ContainedObject +{ + ContainedObject() + : Box(nullptr) + , Checked(false) + {} + template + ContainedObject(const BoxlikeObject& box) + : Box(new BoxlikeObject(box)) + , Checked(false) + {} + std::unique_ptr Box; + bool Checked; +}; + +struct Child +{ + ~Child(); + Child(const AABB& octTreeBounds, + int subDivisions, + std::vector& staticObjects, + std::vector& dynamicObjects); + Child(const Child& other) = delete; + Child(const Child&& other) = delete; + Child& operator= (const Child& other) = delete; + void AddDynamicObject(const AABB& box); + void AddStaticObject(const AABB& box); + template + void ObjectsInSameRegion(const Box& box, std::vector& outObjects) const; + void ClearObjects(); + void ClearDynamicObjects(); + bool RayCollides(const Ray& ray, Output& data) const; + bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const; + + Child* m_Children[8]; + //Indices into the lists in Octree. + std::vector m_StaticObjIndices; + std::vector m_DynamicObjIndices; + AABB m_Box; + //Reference to the lists in Octree. + 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; +}; + +} + +template +Octree::Octree(const AABB& octTreeBounds, int subDivisions) + : m_Root(new OctSpace::Child(octTreeBounds, subDivisions, m_StaticObjects, m_DynamicObjects)) +{ + static_assert(std::is_base_of::value, "template argument type T in Octree must be a subclass of AABB."); +} + +template +Octree::~Octree() +{ + delete m_Root; +} + +template +void Octree::AddDynamicObject(const T& object) +{ + m_Root->AddDynamicObject(object); + m_DynamicObjects.emplace_back(object); +} + +template +void Octree::AddStaticObject(const T& object) +{ + m_Root->AddStaticObject(object); + m_StaticObjects.emplace_back(object); +} + +template +template +void Octree::ObjectsInSameRegion(const Box& box, std::vector& outObjects) +{ + static_assert(std::is_base_of::value, "template argument type Box in Octree::ObjectsInSameRegion must be a subclass of AABB."); + falsifyObjectChecks(); + m_Root->ObjectsInSameRegion(box, outObjects); +} + +template +void Octree::ClearObjects() +{ + m_StaticObjects.clear(); + m_DynamicObjects.clear(); + m_Root->ClearObjects(); +} + +template +void Octree::ClearDynamicObjects() +{ + m_DynamicObjects.clear(); + m_Root->ClearDynamicObjects(); +} + +template +bool Octree::RayCollides(const Ray& ray, OctSpace::Output& data) +{ + falsifyObjectChecks(); + data.CollideDistance = -1; + return m_Root->RayCollides(ray, data); +} + +template +bool Octree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) +{ + falsifyObjectChecks(); + return m_Root->BoxCollides(boxToTest, outBoxIntersected); +} + +template +void Octree::falsifyObjectChecks() +{ + for (auto& obj : m_StaticObjects) { + obj.Checked = false; + } + for (auto& obj : m_DynamicObjects) { + obj.Checked = false; + } +} + +template +void OctSpace::Child::ObjectsInSameRegion(const Box& box, std::vector& outObjects) const +{ + if (hasChildren()) { + for (auto i : childIndicesContainingBox(box)) { + m_Children[i]->ObjectsInSameRegion(box, outObjects); + } + } else { + size_t startIndex = outObjects.size(); + int numDuplicates = 0; + outObjects.resize(outObjects.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; + outObjects[startIndex + i - numDuplicates] = *static_cast(obj.Box.get()); + } + } + 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; + outObjects[startIndex + i - numDuplicates] = *static_cast(obj.Box.get()); + } + } + for (size_t i = 0; i < numDuplicates; ++i) { + outObjects.pop_back(); + } + } +} #endif \ No newline at end of file diff --git a/include/Game/Game.h b/include/Game/Game.h index dbc2ed45..d3a79328 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -47,8 +47,8 @@ private: InputProxy* m_InputProxy; GUI::Frame* m_FrameStack; World* m_World; - Octree* m_OctreeCollision; - Octree* m_OctreeFrustrumCulling; + Octree* m_OctreeCollision; + Octree* m_OctreeFrustrumCulling; SystemPipeline* m_SystemPipeline; RenderFrame* m_RenderFrame; // Network variables diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index d841c75e..a9d125ac 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -23,7 +23,7 @@ void CollisionSystem::UpdateComponent(World* world, EntityWrapper& entity, Compo // Collide against octree std::vector octreeResult; - m_Octree->BoxesInSameRegion(*boundingBox, octreeResult); + m_Octree->ObjectsInSameRegion(*boundingBox, octreeResult); for (auto& boxB : octreeResult) { glm::vec3 resolutionVector; if (Collision::IsSameBoxProbably(boxA, boxB)) { diff --git a/src/Engine/Core/Octree.cpp b/src/Engine/Core/Octree.cpp index 58501117..d7feba45 100644 --- a/src/Engine/Core/Octree.cpp +++ b/src/Engine/Core/Octree.cpp @@ -21,72 +21,11 @@ bool isFirstLower(const ChildInfo& first, const ChildInfo& second) } -Octree::Octree(const AABB& octTreeBounds, int subDivisions) - : m_Root(new Child(octTreeBounds, subDivisions, m_StaticObjects, m_DynamicObjects)) - , m_UpdatedOnce(false) -{ } - -Octree::~Octree() +namespace OctSpace { - delete m_Root; -} -void Octree::AddDynamicObject(const AABB& box) -{ - m_Root->AddDynamicObject(box); - m_DynamicObjects.push_back(box); -} - -void Octree::AddStaticObject(const AABB& box) -{ - m_Root->AddStaticObject(box); - m_StaticObjects.push_back(box); -} - -void Octree::BoxesInSameRegion(const AABB& box, std::vector& outBoxes) -{ - falsifyObjectChecks(); - m_Root->BoxesInSameRegion(box, outBoxes); -} - -void Octree::ClearObjects() -{ - m_StaticObjects.clear(); - m_DynamicObjects.clear(); - m_Root->ClearObjects(); -} - -void Octree::ClearDynamicObjects() -{ - m_DynamicObjects.clear(); - m_Root->ClearDynamicObjects(); -} - -bool Octree::RayCollides(const Ray& ray, Output& data) -{ - falsifyObjectChecks(); - data.CollideDistance = -1; - return m_Root->RayCollides(ray, data); -} - -bool Octree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) -{ - falsifyObjectChecks(); - return m_Root->BoxCollides(boxToTest, outBoxIntersected); -} - -void Octree::falsifyObjectChecks() -{ - for (auto& obj : m_StaticObjects) { - obj.Checked = false; - } - for (auto& obj : m_DynamicObjects) { - obj.Checked = false; - } -} - -Octree::Child::Child(const AABB& octTreeBounds, - int subDivisions, +Child::Child(const AABB& octTreeBounds, + int subDivisions, std::vector& staticObjects, std::vector& dynamicObjects) : m_Box(octTreeBounds) @@ -135,7 +74,7 @@ Octree::Child::Child(const AABB& octTreeBounds, } } -Octree::Child::~Child() +Child::~Child() { for (Child*& c : m_Children) { if (c != nullptr) { @@ -145,7 +84,7 @@ Octree::Child::~Child() } } -bool Octree::Child::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const +bool Child::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const { if (hasChildren()) { for (int i : childIndicesContainingBox(boxToTest)) { @@ -155,7 +94,7 @@ bool Octree::Child::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) } else { for (int i : m_StaticObjIndices) { if (!m_StaticObjectsRef[i].Checked) { - const AABB& objBox = m_StaticObjectsRef[i].Box; + const AABB& objBox = *m_StaticObjectsRef[i].Box; if (Collision::AABBVsAABB(boxToTest, objBox)) { outBoxIntersected = objBox; return true; @@ -165,7 +104,7 @@ bool Octree::Child::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) } for (int i : m_DynamicObjIndices) { if (!m_DynamicObjectsRef[i].Checked) { - const AABB& objBox = m_DynamicObjectsRef[i].Box; + const AABB& objBox = *m_DynamicObjectsRef[i].Box; if (!Collision::IsSameBoxProbably(boxToTest, objBox) && Collision::AABBVsAABB(boxToTest, objBox)) { outBoxIntersected = objBox; @@ -178,7 +117,7 @@ bool Octree::Child::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) return false; } -bool Octree::Child::RayCollides(const Ray& ray, Output& data) const +bool Child::RayCollides(const Ray& ray, OctSpace::Output& data) const { //If the node AABB is missed, everything it contains is missed. if (Collision::RayAABBIntr(ray, m_Box)) { @@ -205,7 +144,7 @@ bool Octree::Child::RayCollides(const Ray& ray, Output& data) const 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)) { + Collision::RayVsAABB(ray, *m_StaticObjectsRef[i].Box, dist)) { minDist = std::min(dist, minDist); intersected = true; } @@ -215,7 +154,7 @@ bool Octree::Child::RayCollides(const Ray& ray, Output& data) const 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)) { + Collision::RayVsAABB(ray, *m_DynamicObjectsRef[i].Box, dist)) { minDist = std::min(dist, minDist); intersected = true; } @@ -230,7 +169,7 @@ bool Octree::Child::RayCollides(const Ray& ray, Output& data) const } -void Octree::Child::AddDynamicObject(const AABB& box) +void Child::AddDynamicObject(const AABB& box) { if (hasChildren()) { for (auto i : childIndicesContainingBox(box)) { @@ -242,7 +181,7 @@ void Octree::Child::AddDynamicObject(const AABB& box) } } -void Octree::Child::AddStaticObject(const AABB& box) +void Child::AddStaticObject(const AABB& box) { if (hasChildren()) { for (auto i : childIndicesContainingBox(box)) { @@ -254,41 +193,7 @@ void Octree::Child::AddStaticObject(const AABB& box) } } -void Octree::Child::BoxesInSameRegion(const AABB& box, std::vector& outBoxes) const -{ - if (hasChildren()) { - for (auto i : childIndicesContainingBox(box)) { - m_Children[i]->BoxesInSameRegion(box, outBoxes); - } - } else { - size_t 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 Octree::Child::ClearObjects() +void Child::ClearObjects() { if (hasChildren()) { for (Child*& c : m_Children) { @@ -300,11 +205,11 @@ void Octree::Child::ClearObjects() } } -void Octree::Child::ClearDynamicObjects() +void Child::ClearDynamicObjects() { if (hasChildren()) { for (Child*& c : m_Children) { - c->ClearObjects(); + c->ClearDynamicObjects(); } } else { m_DynamicObjIndices.clear(); @@ -323,13 +228,13 @@ void Octree::Child::ClearDynamicObjects() // x : - - - - + + + + // y : - - + + - - + + // z : - + - + - + - + -int Octree::Child::childIndexContainingPoint(const glm::vec3& point) const +int Child::childIndexContainingPoint(const glm::vec3& point) const { const glm::vec3& c = m_Box.Origin(); return (1 << 2) * (point.x >= c.x) | (1 << 1) * (point.y >= c.y) | (point.z >= c.z); } -std::vector Octree::Child::childIndicesContainingBox(const AABB& box) const +std::vector Child::childIndicesContainingBox(const AABB& box) const { int minInd = childIndexContainingPoint(box.MinCorner()); int maxInd = childIndexContainingPoint(box.MaxCorner()); @@ -371,7 +276,9 @@ std::vector Octree::Child::childIndicesContainingBox(const AABB& box) const } } -inline bool Octree::Child::hasChildren() const +inline bool Child::hasChildren() const { return m_Children[0] != nullptr; +} + } \ No newline at end of file diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 54f7b69d..6da3ff5c 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -68,8 +68,8 @@ Game::Game(int argc, char* argv[]) m_Renderer->m_World = m_World; // Create Octrees - m_OctreeCollision = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); - m_OctreeFrustrumCulling = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); + m_OctreeCollision = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); + m_OctreeFrustrumCulling = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); // Create system pipeline m_SystemPipeline = new SystemPipeline(m_EventBroker);