From 2f13f5da7a99ed5137f54d77d5d7cc878d93ba4b Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 3 Dec 2015 17:03:27 +0100 Subject: [PATCH] Relocated AABB and Ray in Collision.h to their own files. Started implementation on OctTree. --- include/Engine/Core/AABB.h | 24 ++++ include/Engine/Core/Collision.h | 26 +---- include/Engine/Core/OctTree.h | 29 +++++ include/Engine/Core/Ray.h | 12 ++ src/Engine/Core/AABB.cpp | 11 ++ src/Engine/Core/Collision.cpp | 10 +- src/Engine/Core/OctTree.cpp | 191 ++++++++++++++++++++++++++++++++ src/Engine/Core/Ray.cpp | 0 8 files changed, 272 insertions(+), 31 deletions(-) create mode 100644 include/Engine/Core/AABB.h create mode 100644 include/Engine/Core/Ray.h create mode 100644 src/Engine/Core/AABB.cpp create mode 100644 src/Engine/Core/Ray.cpp diff --git a/include/Engine/Core/AABB.h b/include/Engine/Core/AABB.h new file mode 100644 index 00000000..7d0a46ea --- /dev/null +++ b/include/Engine/Core/AABB.h @@ -0,0 +1,24 @@ +#ifndef AABB_h__ +#define AABB_h__ + +#include "../GLM.h" + +class AABB +{ +public: + AABB() = default; + AABB(const glm::vec3& minPos, const glm::vec3& maxPos); + virtual ~AABB(); + + const glm::vec3& MinCorner() const { return m_MinCorner; } + const glm::vec3& MaxCorner() const { return m_MaxCorner; } + const glm::vec3& Center() const { return m_Center; } + const glm::vec3& HalfSize() const { return m_HalfSize; } +private: + glm::vec3 m_MinCorner; + glm::vec3 m_MaxCorner; + glm::vec3 m_Center; + glm::vec3 m_HalfSize; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index 113a4a39..8cc98043 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -1,34 +1,14 @@ #ifndef Collision_h__ #define Collision_h__ -#include "../GLM.h" +#include "Core/Ray.h" +#include "Core/AABB.h" namespace Collision { -struct Ray -{ - glm::vec3 Origin; - glm::vec3 Direction; -}; - -class AABB -{ -public: - AABB() = default; - AABB(const glm::vec3& minPos, const glm::vec3& maxPos); - - const glm::vec3& MinCorner() const { return m_MinCorner; } - const glm::vec3& MaxCorner() const { return m_MaxCorner; } - const glm::vec3& Center() const { return m_Center; } - const glm::vec3& HalfSize() const { return m_HalfSize; } -private: - glm::vec3 m_MinCorner; - glm::vec3 m_MaxCorner; - glm::vec3 m_Center; - glm::vec3 m_HalfSize; -}; bool RayAABBIntr(const Ray& ray, const AABB& box); + } #endif \ No newline at end of file diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 5fd553d6..0554b12f 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -1,5 +1,34 @@ #ifndef OctTree_h__ #define OctTree_h__ +#include "Core/Collision.h" + +class OctTree +{ +public: + struct Output + { + float CollideDistance; + }; + OctTree(); + ~OctTree(); + //For the root OctTree, [octTreeBounds] should be a box containing the entire level. + OctTree(const AABB& octTreeBounds, int subDivisions); + void AddBox(const AABB& box); + void ClearBoxes(); + //Returns true if the ray collides with something in the tree. Result is written to [data]. + bool RayCollides(const Ray& ray, Output& data) const; + +private: + OctTree* m_Children[8]; + std::vector m_ContainingBoxes; + //TODO: Do derived class from 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. + AABB m_Box; + + bool rayCollides(const Ray& ray, Output& data, const OctTree* const tree) const; + inline bool hasChildren() const; + int childIndexContainingPoint(const glm::vec3& point) const; +}; #endif \ No newline at end of file diff --git a/include/Engine/Core/Ray.h b/include/Engine/Core/Ray.h new file mode 100644 index 00000000..64392cbc --- /dev/null +++ b/include/Engine/Core/Ray.h @@ -0,0 +1,12 @@ +#ifndef Ray_h__ +#define Ray_h__ + +#include "../GLM.h" + +struct Ray +{ + glm::vec3 Origin; + glm::vec3 Direction; +}; + +#endif // Ray_h__ diff --git a/src/Engine/Core/AABB.cpp b/src/Engine/Core/AABB.cpp new file mode 100644 index 00000000..8a364ce5 --- /dev/null +++ b/src/Engine/Core/AABB.cpp @@ -0,0 +1,11 @@ +#include "Core/AABB.h" + +AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos) + : m_MinCorner(minPos) + , m_MaxCorner(maxPos) + , m_Center(0.5f * (maxPos + minPos)) + , m_HalfSize(0.5f * (maxPos - minPos)) +{} + +AABB::~AABB() +{} diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index 4ccd1ccd..f16dd0df 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -1,15 +1,9 @@ -#include "Core\Collision.h" +#include "Core/Collision.h" +#include "Engine/GLM.h" namespace Collision { -AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos) - : m_MinCorner(minPos) - , m_MaxCorner(maxPos) - , m_Center(0.5f * (maxPos + minPos)) - , m_HalfSize(0.5f * (maxPos - minPos)) -{ } - bool RayAABBIntr(const Ray& ray, const AABB& box) { glm::vec3 w = 75.0f * ray.Direction; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index e69de29b..29575ac7 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -0,0 +1,191 @@ +#include +#include +#include + +#include "Core/OctTree.h" + +namespace +{ +//To be able to sort nodes based on distance to ray origin. +struct ChildInfo +{ + int Index; + float Distance; +}; + +bool isFirstLower(const ChildInfo& first, const ChildInfo& second) +{ + return first.Distance < second.Distance; +} + +} + +OctTree::OctTree() + : OctTree(AABB(), 0) +{} + +OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) + : m_Box(octTreeBounds) +{ + if (subDivisions == 0) { + for (OctTree*& c : m_Children) { + c = nullptr; + } + } else { + --subDivisions; + for (int i = 0; i < 8; ++i) { + glm::vec3 minPos, maxPos; + const glm::vec3& parentMin = m_Box.MinCorner(); + const glm::vec3& parentMax = m_Box.MaxCorner(); + const glm::vec3& parentCenter = m_Box.MaxCorner(); + std::bitset<3> bits(i); + //If child is 4,5,6,7. + if (bits.test(2)) { + minPos.x = parentCenter.x; + maxPos.x = parentMax.x; + } else { + minPos.x = parentMin.x; + maxPos.x = parentCenter.x; + } + + //If child is 2,3,6,7 + if (bits.test(1)) { + minPos.y = parentCenter.y; + maxPos.y = parentMax.y; + } else { + minPos.y = parentMin.y; + maxPos.y = parentCenter.y; + } + //If child is 1,3,5,7 + if (bits.test(0)) { + minPos.z = parentCenter.z; + maxPos.z = parentMax.z; + } else { + minPos.z = parentMin.z; + maxPos.z = parentCenter.z; + } + m_Children[i] = new OctTree(AABB(minPos, maxPos), subDivisions); + } + } +} + +OctTree::~OctTree() +{ + for (OctTree*& c : m_Children) { + if (c != nullptr) { + delete c; + c = nullptr; + } + } +} + + +bool OctTree::RayCollides(const Ray& ray, Output& data) const +{ + data.CollideDistance = -1; + return rayCollides(ray, data, this); +} + +//Currently all Nodes must have exactly 0 or 8 children, and objectdata should only exist in the last bottom nodes. +bool OctTree::rayCollides(const Ray& ray, Output& data, const OctTree* const tree) const +{ + //If the node AABB is missed, everything it contains is missed. + if (Collision::RayAABBIntr(ray, tree->m_Box)) { + //If the ray shoots the tree, and it is a parent to 8 children :o + if (tree->hasChildren()) { + //Sort children according to their distance from the ray origin. + std::vector childInfos; + childInfos.reserve(8); + for (int i = 0; i < 8; ++i) { + childInfos.push_back({ i, glm::distance(ray.Origin, tree->m_Children[i]->m_Box.Center()) }); + } + std::sort(childInfos.begin(), childInfos.end(), isFirstLower); + //Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit. + for (const ChildInfo& info : childInfos) { + if (rayCollides(ray, data, tree->m_Children[info.Index])) { + return true; + } + } + } else { + ////TODO: Check against objects in the node. + //float minDist = INFINITY; + //for (const auto& obj : m_ObjectsInBox) { + // float dist = Collide(ray, obj); + // minDist = min(dist, minDist); + //} + //data.CollideDistance = minDist; + //if minDist != Collide()'s non-collide value: return false; + return true; + } + } + return false; +} + +void OctTree::AddBox(const AABB& box) +{ + if (hasChildren()) { + int minInd = childIndexContainingPoint(box.MinCorner()); + int maxInd = childIndexContainingPoint(box.MaxCorner()); + std::bitset<3> bits(minInd ^ maxInd); + switch (bits.count()) { + case 0: //Box contained completely in one child. + m_Children[minInd]->AddBox(box); + break; + case 1: //Two children. + m_Children[minInd]->AddBox(box); + m_Children[maxInd]->AddBox(box); + break; + case 2: //Four children. + bits.flip(); + for (int c = 0; c < 8; ++c) { + if ((bits & std::bitset<3>(c))[0]) { + m_Children[c]->AddBox(box); + } + } + break; + case 3: //Eight children. + for (OctTree*& c : m_Children) { + c->AddBox(box); + } + break; + default: + break; + } + } else { + m_ContainingBoxes.push_back(box); + } +} + +void OctTree::ClearBoxes() +{ + if (hasChildren()) { + for (OctTree*& c : m_Children) { + c->ClearBoxes(); + } + } else { + m_ContainingBoxes.clear(); + } +} + +//: 3 7 +//: +//: 2 6 +//: | +//: 1 5 \ y +//: z +//: 0 4 0 x--> +// +// child: 0 1 2 3 4 5 6 7 +// x : - - - - + + + + +// y : - - + + - - + + +// z : - + - + - + - + +int OctTree::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); +} + +inline bool OctTree::hasChildren() const +{ + return m_Children[0] != nullptr; +} \ No newline at end of file diff --git a/src/Engine/Core/Ray.cpp b/src/Engine/Core/Ray.cpp new file mode 100644 index 00000000..e69de29b