From 1d19c6901eadce9dc0b40fe552020164a9a4f134 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 2 Dec 2015 11:18:52 +0100 Subject: [PATCH 01/61] Added collision files. --- include/Engine/Core/Collision.h | 9 +++++++++ src/Engine/Core/Collision.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 include/Engine/Core/Collision.h create mode 100644 src/Engine/Core/Collision.cpp diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h new file mode 100644 index 00000000..407f2096 --- /dev/null +++ b/include/Engine/Core/Collision.h @@ -0,0 +1,9 @@ +#ifndef Collision_h__ +#define Collision_h__ + +namespace Collision +{ +//bool RayAABBIntr(float3 origin, float3 direction, float3 center, float3 h); +} + +#endif \ No newline at end of file diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp new file mode 100644 index 00000000..699d3b81 --- /dev/null +++ b/src/Engine/Core/Collision.cpp @@ -0,0 +1,27 @@ + +namespace Collision +{ + +/*bool RayAABBIntr(float3 origin, float3 direction, float3 center, float3 h) +{ +float3 w = 75.0f * direction; +float3 v = abs(w); +float3 c = origin - center + w; + +//This is better if we hit often, do comparisons simultanously. +//if (any(abs(c) > v + h)) +// return false; +// +//return !(any(abs(c.yxx*w.zzy - c.zzy*w.yxx) > h.yxx*v.zzy + h.zzy*v.yxx)); + +//This is better if we miss often, reject test and exit early. +if (abs(c.x) > v.x + h.x) return false; +if (abs(c.y) > v.y + h.y) return false; +if (abs(c.z) > v.z + h.z) return false; + +if (abs(c.y*w.z - c.z*w.y) > h.y*v.z + h.z*v.y) return false; +if (abs(c.x*w.z - c.z*w.x) > h.x*v.z + h.z*v.x) return false; +return !(abs(c.x*w.y - c.y*w.x) > h.x*v.y + h.y*v.x); +}*/ + +} \ No newline at end of file From fa0ace8c68dc0c25b8f8298d63e8b98086ee9d8d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 2 Dec 2015 11:32:56 +0100 Subject: [PATCH 02/61] Added empty OctTree files. --- include/Engine/Core/OctTree.h | 5 +++++ src/Engine/Core/OctTree.cpp | 0 2 files changed, 5 insertions(+) create mode 100644 include/Engine/Core/OctTree.h create mode 100644 src/Engine/Core/OctTree.cpp diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h new file mode 100644 index 00000000..5fd553d6 --- /dev/null +++ b/include/Engine/Core/OctTree.h @@ -0,0 +1,5 @@ +#ifndef OctTree_h__ +#define OctTree_h__ + + +#endif \ No newline at end of file diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp new file mode 100644 index 00000000..e69de29b From c698149bddcb79c4076c125e02901136c0174ded Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 2 Dec 2015 14:24:21 +0100 Subject: [PATCH 03/61] Added Ray, AABB structs, and basic rayVsAABB test function. --- include/Engine/Core/Collision.h | 27 +++++++++++++++++- src/Engine/Core/Collision.cpp | 50 ++++++++++++++++++++------------- tools/CodeRules.h | 3 +- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index 407f2096..113a4a39 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -1,9 +1,34 @@ #ifndef Collision_h__ #define Collision_h__ +#include "../GLM.h" + namespace Collision { -//bool RayAABBIntr(float3 origin, float3 direction, float3 center, float3 h); +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/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index 699d3b81..4ccd1ccd 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -1,27 +1,39 @@ +#include "Core\Collision.h" namespace Collision { -/*bool RayAABBIntr(float3 origin, float3 direction, float3 center, float3 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)) +{ } + +bool RayAABBIntr(const Ray& ray, const AABB& box) { -float3 w = 75.0f * direction; -float3 v = abs(w); -float3 c = origin - center + w; + glm::vec3 w = 75.0f * ray.Direction; + glm::vec3 v = glm::abs(w); + glm::vec3 c = ray.Origin - box.Center() + w; + glm::vec3 half = box.HalfSize(); + + if (abs(c.x) > v.x + half.x) { + return false; + } + if (abs(c.y) > v.y + half.y) { + return false; + } + if (abs(c.z) > v.z + half.z) { + return false; + } -//This is better if we hit often, do comparisons simultanously. -//if (any(abs(c) > v + h)) -// return false; -// -//return !(any(abs(c.yxx*w.zzy - c.zzy*w.yxx) > h.yxx*v.zzy + h.zzy*v.yxx)); - -//This is better if we miss often, reject test and exit early. -if (abs(c.x) > v.x + h.x) return false; -if (abs(c.y) > v.y + h.y) return false; -if (abs(c.z) > v.z + h.z) return false; - -if (abs(c.y*w.z - c.z*w.y) > h.y*v.z + h.z*v.y) return false; -if (abs(c.x*w.z - c.z*w.x) > h.x*v.z + h.z*v.x) return false; -return !(abs(c.x*w.y - c.y*w.x) > h.x*v.y + h.y*v.x); -}*/ + if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) { + return false; + } + if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) { + return false; + } + return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); +} } \ No newline at end of file diff --git a/tools/CodeRules.h b/tools/CodeRules.h index f09ae81d..8d72a6f2 100755 --- a/tools/CodeRules.h +++ b/tools/CodeRules.h @@ -86,8 +86,7 @@ T* ClassType::PublicMemberFunction(bool value) if (m_PrivateMember2->PublicMember == 1) { return new T(); - } - else { + } else { return nullptr; } } From fd99562c7a7f32301724d92eec105853884156fa Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 2 Dec 2015 15:03:55 +0100 Subject: [PATCH 04/61] yrryr567yry567 --- include/Engine/Core/Collision.h | 3 +++ src/Engine/Core/Collision.cpp | 23 +++++++++++++++++++++++ src/Tests/CollisionTest.cpp | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 src/Tests/CollisionTest.cpp diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index 113a4a39..f67a1c67 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -2,11 +2,13 @@ #define Collision_h__ #include "../GLM.h" +#include namespace Collision { struct Ray { +public: glm::vec3 Origin; glm::vec3 Direction; }; @@ -29,6 +31,7 @@ private: }; bool RayAABBIntr(const Ray& ray, const AABB& box); +bool RayVsAABB(const Ray& ray, const AABB& box); } #endif \ No newline at end of file diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index 4ccd1ccd..fd79b8bc 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -1,4 +1,5 @@ #include "Core\Collision.h" +#include namespace Collision { @@ -36,4 +37,26 @@ bool RayAABBIntr(const Ray& ray, const AABB& box) return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); } +bool RayVsAABB(const Ray& ray, const AABB& box) +{ + glm::vec3 invdir = 1.0f / ray.Direction; + glm::vec3 bMin = box.MinCorner(); + glm::vec3 bMax = box.MaxCorner(); + + float t1 = (bMin.x - ray.Origin.x)*invdir.x; + float t2 = (bMax.x - ray.Origin.x)*invdir.x; + float t3 = (bMin.y - ray.Origin.y)*invdir.y; + float t4 = (bMax.y - ray.Origin.y)*invdir.y; + float t5 = (bMin.z - ray.Origin.z)*invdir.z; + float t6 = (bMax.z - ray.Origin.z)*invdir.z; + + float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); + float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); + + if (tmax < 0 || tmin > tmax) + return false; + + return true; +} + } \ No newline at end of file diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp new file mode 100644 index 00000000..ef02cb66 --- /dev/null +++ b/src/Tests/CollisionTest.cpp @@ -0,0 +1,18 @@ +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; +#include + +BOOST_AUTO_TEST_SUITE(collisionTests) + +BOOST_AUTO_TEST_CASE(collisionTest) +{ + Collision::Ray ray; + ray.Origin = glm::vec3(0, 0, 0); + ray.Direction = glm::vec3(0, 0, 0); + auto someAABB = Collision::AABB(glm::vec3(0, 0, 0), glm::vec3(0, 0, 0)); + bool z = Collision::RayVsAABB(ray, someAABB); +} + +BOOST_AUTO_TEST_SUITE_END() + From af294f78b7622ac5ec0ad21faa2ef571bb9fb5c3 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 2 Dec 2015 16:26:30 +0100 Subject: [PATCH 05/61] czdxvxdfvxvxvc --- src/Engine/Core/Collision.cpp | 16 ++++----- src/Tests/CollisionTest.cpp | 64 ++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index fd79b8bc..ff38c8a2 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -40,15 +40,13 @@ bool RayAABBIntr(const Ray& ray, const AABB& box) bool RayVsAABB(const Ray& ray, const AABB& box) { glm::vec3 invdir = 1.0f / ray.Direction; - glm::vec3 bMin = box.MinCorner(); - glm::vec3 bMax = box.MaxCorner(); - - float t1 = (bMin.x - ray.Origin.x)*invdir.x; - float t2 = (bMax.x - ray.Origin.x)*invdir.x; - float t3 = (bMin.y - ray.Origin.y)*invdir.y; - float t4 = (bMax.y - ray.Origin.y)*invdir.y; - float t5 = (bMin.z - ray.Origin.z)*invdir.z; - float t6 = (bMax.z - ray.Origin.z)*invdir.z; + + float t1 = (box.MinCorner().x - ray.Origin.x)*invdir.x; + float t2 = (box.MaxCorner().x - ray.Origin.x)*invdir.x; + float t3 = (box.MinCorner().y - ray.Origin.y)*invdir.y; + float t4 = (box.MaxCorner().y - ray.Origin.y)*invdir.y; + float t5 = (box.MinCorner().z - ray.Origin.z)*invdir.z; + float t6 = (box.MaxCorner().z - ray.Origin.z)*invdir.z; float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index ef02cb66..426b9722 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -2,16 +2,72 @@ using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include +#include //srand BOOST_AUTO_TEST_SUITE(collisionTests) BOOST_AUTO_TEST_CASE(collisionTest) { + //fixed seed + srand(2); Collision::Ray ray; - ray.Origin = glm::vec3(0, 0, 0); - ray.Direction = glm::vec3(0, 0, 0); - auto someAABB = Collision::AABB(glm::vec3(0, 0, 0), glm::vec3(0, 0, 0)); - bool z = Collision::RayVsAABB(ray, someAABB); + Collision::AABB someAABB; + glm::vec3 minPos; + glm::vec3 maxPos; + bool z; + int test = 0; + for (size_t i = 0; i < 1000000; i++) + { + ray.Origin.x = rand() % 100; + ray.Origin.y = rand() % 100; + ray.Origin.z = rand() % 100; + ray.Direction.x = rand() % 100; + ray.Direction.y = rand() % 100; + ray.Direction.z = rand() % 100; + minPos.x = rand() % 100; + minPos.y = rand() % 100; + minPos.z = rand() % 100; + maxPos.x = rand() % 100; + maxPos.y = rand() % 100; + maxPos.z = rand() % 100; + + someAABB = Collision::AABB(minPos, maxPos); + z = Collision::RayVsAABB(ray, someAABB); + if (z) ++test; + } + BOOST_CHECK(test >= 0); +} + +BOOST_AUTO_TEST_CASE(collisionTest2) +{ + //fixed seed + srand(2); + Collision::Ray ray; + Collision::AABB someAABB; + glm::vec3 minPos; + glm::vec3 maxPos; + bool z; + int test = 0; + for (size_t i = 0; i < 1000000; i++) + { + ray.Origin.x = rand() % 100; + ray.Origin.y = rand() % 100; + ray.Origin.z = rand() % 100; + ray.Direction.x = rand() % 100; + ray.Direction.y = rand() % 100; + ray.Direction.z = rand() % 100; + minPos.x = rand() % 100; + minPos.y = rand() % 100; + minPos.z = rand() % 100; + maxPos.x = rand() % 100; + maxPos.y = rand() % 100; + maxPos.z = rand() % 100; + + someAABB = Collision::AABB(minPos, maxPos); + z = Collision::RayAABBIntr(ray, someAABB); + if (z) ++test; + } + BOOST_CHECK(test >= 0); } BOOST_AUTO_TEST_SUITE_END() From 464650c370e78ef13baa01b38fc9c3ff6972f695 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 2 Dec 2015 16:31:39 +0100 Subject: [PATCH 06/61] more tests added --- src/Tests/ObjectPoolTest.cpp | 476 +++++++++++++++++++++++++++++++++++ 1 file changed, 476 insertions(+) create mode 100644 src/Tests/ObjectPoolTest.cpp diff --git a/src/Tests/ObjectPoolTest.cpp b/src/Tests/ObjectPoolTest.cpp new file mode 100644 index 00000000..adea33eb --- /dev/null +++ b/src/Tests/ObjectPoolTest.cpp @@ -0,0 +1,476 @@ +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; +#include "../wmem/MemPrototype/ObjectPool.h" +#include + +struct S +{ + S() = default; + S(int i, float ff) : k(i), f(ff) { } + ~S() { } + int k; + float f; +}; + +//BOOST_GLOBAL_FIXTURE(S); +BOOST_AUTO_TEST_SUITE(memProtoTypeTestSuite) + +BOOST_AUTO_TEST_CASE(testPool) +{ + ObjectPool pool(32);//32 true/false values = 32 slots + BOOST_CHECK(pool.empty() == true); + + const size_t size = 12;//12 platser i structen addresses, som håller en int, en float vardera + S* addresses[size]; + addresses[0] = pool.New(7, 0.035f); + //"Not empty after allocating one element." + BOOST_CHECK(!pool.empty()); + + //"Element created correctly with k==7" + BOOST_CHECK(addresses[0]->k == 7); + //"Element created correctly with f==0.035f" + BOOST_CHECK_CLOSE_FRACTION(addresses[0]->f, 0.035f, 0.0001f); + addresses[0]->k = 5; + BOOST_CHECK(addresses[0]->k == 5); + + //"Empty after delete" + pool.Delete(addresses[0]); + BOOST_CHECK(pool.empty()); + + addresses[0] = pool.New(7, 0.035f); + addresses[1] = pool.New(5, 0.035f); + pool.Delete(addresses[1]); + BOOST_CHECK(!pool.empty()); + pool.Delete(addresses[0]); + BOOST_CHECK(pool.empty()); + + //INT32_MAX, FLT_MAX test + addresses[0] = pool.New(INT32_MAX, FLT_MAX); + BOOST_CHECK(!pool.empty()); + BOOST_CHECK(addresses[0]->k == INT32_MAX); + BOOST_CHECK_CLOSE_FRACTION(addresses[0]->f, FLT_MAX, 0.0001f); +} +/* +BOOST_AUTO_TEST_CASE(testPoolArray) +{ +ObjectPool pool(32); +S* addresses; + +//Add array size 5 to pool." +addresses = pool.NewArray(5);// <-> addresses = new S[5]; +addresses[0] = S(12, 0.030f); +addresses[1] = S(13, 0.031f); +addresses[2] = S(14, 0.032f); +addresses[3] = S(15, 0.033f); +addresses[4] = S(16, 0.034f); + +//"Not empty after allocating +BOOST_CHECK(!pool.empty()); +//"Element created correctly with k==12" +BOOST_CHECK(addresses->k == 12); +//"Element created correctly with f==0.030f" +BOOST_CHECK_CLOSE_FRACTION(addresses->f, 0.030f, 0.0001f); + +//add a few other structs so it becomes bigger than the original size (32), +//which means it must push back the rest of the values into a vector +S* test2, *test3, *test4, *test5; +test2 = pool.NewArray(5);// <-> test2 = new S[5]; +test3 = pool.NewArray(40);//+40 +test4 = pool.NewArray(40);//+40 +test5 = pool.NewArray(40);//+40=120 +BOOST_CHECK(pool.ExtraSize() == 120); +BOOST_CHECK(pool.PoolSize() == 10); +BOOST_CHECK(pool.size() == 120 + 10); + +//testar "perfekt delete", dvs bryr mig inte om att testa att deleta bara 38 om storleken egentligen är 40 osv +pool.DeleteArray(test2, 5);//callar destructorn på test2 också +pool.DeleteArray(test3, 40); + +pool.DeleteArray(addresses, 5); + +//add / del array +S* another = pool.NewArray(64); +for (int i = 0; i < 64; ++i) +another[i] = S(i, 0.1f*i); +pool.DeleteArray(another, 64); +} +*/ +BOOST_AUTO_TEST_CASE(testIterationNormal) +{ + //extra vector check + S* test4, *test5; + ObjectPool pool(4); + test4 = pool.New(); + test5 = pool.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : pool) + o.k = 14; + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test4[i].k == 14); + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test5[i].k == 14); +} + +BOOST_AUTO_TEST_CASE(testOutOfScopeDelete) +{ + //extra vector check + S* test4, *test5; + { + ObjectPool pool(4); + test4 = pool.New(); + test5 = pool.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : pool) + o.k = 14; + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test4[i].k == 14); + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test5[i].k == 14); + } + //pool goes out of scope here, and thus the test4 values become undefined (memory is killed at out of scope) + BOOST_CHECK(test4[0].k != 14); + BOOST_CHECK(test5[0].k != 15); +} + +BOOST_AUTO_TEST_CASE(testIterationOneExtra) +{ + //extra vector check + ObjectPool pool(1); + S* test4, *test5; + test4 = pool.New(); + test5 = pool.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : pool) + o.k = 14; + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test4[i].k == 14); + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test5[i].k == 14); +} + +BOOST_AUTO_TEST_CASE(testIterationTwoExtra) +{ + //extra vector check + ObjectPool pool(1); + S* test4, *test5; + test4 = pool.New(); + test5 = pool.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : pool) + o.k = 14; + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test4[i].k == 14); + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test5[i].k == 14); +} +/* +BOOST_AUTO_TEST_CASE(releaseModeTest_RandomAllocateDeallocate) +{ +//run this in releasemode +struct I +{ +I() = default; +I(size_t i, size_t ff) : k(i), f(ff) { } +~I() { } +size_t k; +size_t f; +}; +srand((unsigned int)time(nullptr)); + +const size_t SIZE = 128; +ObjectPool pool(SIZE); +I* addresses[SIZE]; +std::vector allocated(SIZE, false); +std::vector arrSizes(SIZE, 0); +size_t slotsAlloced = 0; +size_t superCount = 0; +size_t slot; +size_t i; + +while (superCount++ < 1000) { +if (rand() % 2 == 0) { +i = 0; +//ta slumpmässig slot som inte är allokerad +do { +slot = (size_t)((SIZE - 1) * ((float)rand() / RAND_MAX)); +} while (allocated[slot] && ++i < 512); + +if (i < 512) { +arrSizes[slot] = 1 + (size_t)((24 - 1) * ((float)rand() / RAND_MAX)); +addresses[slot] = pool.NewArray(arrSizes[slot]); +for (size_t a = 0; a < arrSizes[slot]; ++a) +addresses[slot][a] = I(slot, a); +allocated[slot] = true; +++slotsAlloced; +} +} +//Deallocate +else { +i = 0; +//ta slumpmässig slot som är allokerad +do { +slot = (size_t)((SIZE - 1) * ((float)rand() / RAND_MAX)); +} while (!allocated[slot] && ++i < 512); + +if (i < 512) { +pool.DeleteArray(addresses[slot], arrSizes[slot]); +arrSizes[slot] = 0; +allocated[slot] = false; +--slotsAlloced; +} +} +//Check content. +for (size_t a = 0; a < SIZE; ++a) { +if (allocated[a]) { +for (size_t e = 0; e < arrSizes[a]; ++e) { +BOOST_CHECK(!(addresses[a][e].k != a || addresses[a][e].f != e)); +} +} +} +} +} +*/ + +BOOST_AUTO_TEST_CASE(testConstructors) +{ + //http://stackoverflow.com/questions/357929/is-it-important-to-unit-test-a-constructor + //"If your constructor has, for example, an if (condition), you need to test both flows (true,false). + //If your constructor does some kind of job before setting. You should check the job is done" + + //testing the constructors with different T values and a small check so size is initialized to 0 + MemoryPool memPoolI; + BOOST_CHECK(memPoolI.empty()); + BOOST_CHECK(memPoolI.size() == 0); + MemoryPool memPoolF; + BOOST_CHECK(memPoolF.empty()); + BOOST_CHECK(memPoolF.size() == 0); + MemoryPool memPoolD; + BOOST_CHECK(memPoolD.empty()); + BOOST_CHECK(memPoolD.size() == 0); + MemoryPool memPoolS; + BOOST_CHECK(memPoolS.empty()); + BOOST_CHECK(memPoolS.size() == 0); + + ObjectPool objPoolI(64); + BOOST_CHECK(objPoolI.empty()); + BOOST_CHECK(objPoolI.size() == 0); + ObjectPool objPoolF(32); + BOOST_CHECK(objPoolF.empty()); + BOOST_CHECK(objPoolF.size() == 0); + ObjectPool objPoolD(16); + BOOST_CHECK(objPoolD.empty()); + BOOST_CHECK(objPoolD.size() == 0); + ObjectPool objPoolS(128); + BOOST_CHECK(objPoolS.empty()); + BOOST_CHECK(objPoolS.size() == 0); +} + +BOOST_AUTO_TEST_CASE(testOperators) +{ + ObjectPool pool(100); + // S* s[12] = pool.NewArray(12); + S* s[12]; + s[0] = pool.New(); + s[11] = pool.New(); + + s[0]->k = 2; + s[11]->k = 3; + //testing operators: ++i,!= + auto& iter = pool.begin(); + for (iter; iter != pool.end(); ++iter) { + //testing operators:*,== + auto dereferencedIterator = *iter; + if (iter == pool.begin()) { + BOOST_CHECK(dereferencedIterator.k == 2); + } + if (iter == pool.end()) { + BOOST_CHECK(dereferencedIterator.k == 3); + } + //testing operators:-> + iter->k += 2; + } + BOOST_CHECK(s[0]->k == 4); + BOOST_CHECK(s[11]->k == 5); + BOOST_CHECK(iter == pool.end()); + + //testing operators:i++ + s[0]->k = 2; + s[11]->k = 2; + for (auto& iter = pool.begin(); iter != pool.end(); iter++) + iter->k += 2; + BOOST_CHECK(s[0]->k == 4); + BOOST_CHECK(s[11]->k == 4); +} +/* +BOOST_AUTO_TEST_CASE(testBranchFree) +{ +//testing Free , which is the only untested +//via delete/deletearray + +//1. no extra memory delete +ObjectPool pool(32);//32 true/false values = 32 slots +S* addresses[12]; +addresses[0] = pool.New(7, 0.035f); +pool.Delete(addresses[0]); +BOOST_CHECK(pool.empty()); + +//1b. no extra memory deleteArray +ObjectPool pool1b(32);//32 true/false values = 32 slots +S* test1b; +test1b = pool1b.NewArray(5);// <-> test2 = new S[5]; +BOOST_CHECK(pool1b.size() == 5); +pool1b.DeleteArray(test1b, 5);//callar destructorn på test2 också +BOOST_CHECK(pool1b.empty()); + +//2. extra memory delete +ObjectPool pool2(2); +S* addresses2[12]; +addresses2[0] = pool2.New(7, 0.035f); +addresses2[1] = pool2.New(7, 0.035f); +addresses2[2] = pool2.New(7, 0.035f); +addresses2[3] = pool2.New(7, 0.035f); +addresses2[4] = pool2.New(7, 0.035f); +BOOST_CHECK(pool2.size() == 5); +pool2.Delete(addresses2[0]); +BOOST_CHECK(pool2.size() == 4); +pool2.Delete(addresses2[1]); +BOOST_CHECK(pool2.size() == 3); +pool2.Delete(addresses2[2]); +BOOST_CHECK(pool2.size() == 2); +pool2.Delete(addresses2[3]); +BOOST_CHECK(pool2.size() == 1); +pool2.Delete(addresses2[4]); +BOOST_CHECK(pool2.empty()); +//reverse delete +addresses2[0] = pool2.New(7, 0.035f); +addresses2[1] = pool2.New(7, 0.035f); +addresses2[2] = pool2.New(7, 0.035f); +addresses2[3] = pool2.New(7, 0.035f); +addresses2[4] = pool2.New(7, 0.035f); +BOOST_CHECK(pool2.size() == 5); +pool2.Delete(addresses2[4]); +BOOST_CHECK(pool2.size() == 4); +pool2.Delete(addresses2[3]); +BOOST_CHECK(pool2.size() == 3); +pool2.Delete(addresses2[2]); +BOOST_CHECK(pool2.size() == 2); +pool2.Delete(addresses2[1]); +BOOST_CHECK(pool2.size() == 1); +pool2.Delete(addresses2[0]); +BOOST_CHECK(pool2.empty()); + +//2b. extra memory deleteArray +ObjectPool pool2b(32);//32 true/false values = 32 slots +S* test2b,*test2bb; +test2b = pool2b.NewArray(5);// <-> test2 = new S[5]; +BOOST_CHECK(pool2b.size() == 5); +test2bb = pool2b.NewArray(40);// <-> test2 = new S[5]; +BOOST_CHECK(pool2b.size() == 45); +pool2b.DeleteArray(test2b, 5);//callar destructorn på test2 också +BOOST_CHECK(pool2b.size() == 40); +pool2b.DeleteArray(test2bb, 40);//callar destructorn på test2 också +BOOST_CHECK(pool2b.empty()); +} +*/ +BOOST_AUTO_TEST_CASE(testBranchAllocate) +{ + //1 slot else many slots + //see testBranchFree + + //out of mem vs not out of mem allocate + //see testBranchFree +} +BOOST_AUTO_TEST_CASE(testEdgeCase) +{ + //test with a very small pool + ObjectPool pool(1); + BOOST_CHECK(pool.empty()); + S* test4; + test4 = pool.New(7, 0.035f); + BOOST_CHECK(!pool.empty()); + BOOST_CHECK(test4->k == 7); + BOOST_CHECK_CLOSE_FRACTION(test4->f, 0.035f, 0.0001f); + + //test with a very small pool and array, iterating + ObjectPool poolA(1); + S* test5; + test5 = poolA.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : poolA) + o.k = 14; + for (size_t i = 0; i < 1; ++i) + BOOST_CHECK(test5[i].k == 14); +} + +BOOST_AUTO_TEST_CASE(testBadlyAlignedData) +{ + //small test with non-aligned data 4+1bytes + struct S + { + S() = default; + S(float f, char c) : m_f(f), m_c(c) { } + ~S() { } + float m_f; + char m_c; + }; + MemoryPool memPoolS; + BOOST_CHECK(memPoolS.empty()); + BOOST_CHECK(memPoolS.size() == 0); + ObjectPool objPoolS(64); + BOOST_CHECK(objPoolS.empty()); + BOOST_CHECK(objPoolS.size() == 0); + S* test4; + test4 = objPoolS.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : objPoolS) { + o.m_c = 'v'; + o.m_f = 0.15534543f; + } + for (size_t i = 0; i < 1; ++i) { + BOOST_CHECK(test4[i].m_c == 'v'); + BOOST_CHECK_CLOSE_FRACTION(test4[i].m_f, 0.15534543f, 0.0001f); + } +} +BOOST_AUTO_TEST_CASE(testBadlyAlignedData2) +{ + //small test with non-aligned data 1+1+1bytes + struct S + { + S() = default; + S(char c, char c2, char c3) : m_c(c), m_c2(c2), m_c3(c3) { } + ~S() { } + char m_c; + char m_c2; + char m_c3; + }; + MemoryPool memPoolS; + BOOST_CHECK(memPoolS.empty()); + BOOST_CHECK(memPoolS.size() == 0); + ObjectPool objPoolS(64); + BOOST_CHECK(objPoolS.empty()); + BOOST_CHECK(objPoolS.size() == 0); + S* test4; + test4 = objPoolS.New(); + //Check so iterate over pool doesn't throw compile-time errors. + for (auto &o : objPoolS) { + o.m_c = 'v'; + o.m_c2 = 'w'; + o.m_c3 = 'x'; + } + for (size_t i = 0; i < 1; ++i) { + BOOST_CHECK(test4[i].m_c == 'v'); + BOOST_CHECK(test4[i].m_c2 == 'w'); + BOOST_CHECK(test4[i].m_c3 == 'x'); + } +} + +BOOST_AUTO_TEST_CASE(testWrongData) +{ +} +BOOST_AUTO_TEST_CASE(testFillDeleteFillAgain) { + //already done in BOOST_AUTO_TEST_CASE(releaseModeTest_RandomAllocateDeallocate) + +} + +BOOST_AUTO_TEST_SUITE_END() From 2f13f5da7a99ed5137f54d77d5d7cc878d93ba4b Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 3 Dec 2015 17:03:27 +0100 Subject: [PATCH 07/61] 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 From af5b3a93028d84911fde5b69fb5c00e1b38f96ff Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 3 Dec 2015 17:24:53 +0100 Subject: [PATCH 08/61] Fixed wrong paths and added an outDistance to RayVsAABB. --- include/Engine/Core/Collision.h | 3 ++- src/Engine/Core/Collision.cpp | 7 +++++++ src/Tests/CollisionTest.cpp | 14 ++++++++------ src/Tests/ObjectPoolTest.cpp | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index bbcad66e..67a9e808 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -3,13 +3,14 @@ #include "Core/Ray.h" #include "Core/AABB.h" -#include namespace Collision { bool RayAABBIntr(const Ray& ray, const AABB& box); bool RayVsAABB(const Ray& ray, const AABB& box); +bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance); + } #endif \ No newline at end of file diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index bce61aeb..fda895a4 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -32,6 +32,12 @@ bool RayAABBIntr(const Ray& ray, const AABB& box) } bool RayVsAABB(const Ray& ray, const AABB& box) +{ + float dummy; + return RayVsAABB(ray, box, dummy); +} + +bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) { glm::vec3 invdir = 1.0f / ray.Direction; @@ -48,6 +54,7 @@ bool RayVsAABB(const Ray& ray, const AABB& box) if (tmax < 0 || tmin > tmax) return false; + outDistance = (tmin > 0) ? tmin : tmax; return true; } diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 426b9722..c8260e39 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -2,6 +2,8 @@ using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include +#include "Engine/Core/AABB.h" +#include "Engine/Core/Ray.h" #include //srand BOOST_AUTO_TEST_SUITE(collisionTests) @@ -10,8 +12,8 @@ BOOST_AUTO_TEST_CASE(collisionTest) { //fixed seed srand(2); - Collision::Ray ray; - Collision::AABB someAABB; + Ray ray; + AABB someAABB; glm::vec3 minPos; glm::vec3 maxPos; bool z; @@ -31,7 +33,7 @@ BOOST_AUTO_TEST_CASE(collisionTest) maxPos.y = rand() % 100; maxPos.z = rand() % 100; - someAABB = Collision::AABB(minPos, maxPos); + someAABB = AABB(minPos, maxPos); z = Collision::RayVsAABB(ray, someAABB); if (z) ++test; } @@ -42,8 +44,8 @@ BOOST_AUTO_TEST_CASE(collisionTest2) { //fixed seed srand(2); - Collision::Ray ray; - Collision::AABB someAABB; + Ray ray; + AABB someAABB; glm::vec3 minPos; glm::vec3 maxPos; bool z; @@ -63,7 +65,7 @@ BOOST_AUTO_TEST_CASE(collisionTest2) maxPos.y = rand() % 100; maxPos.z = rand() % 100; - someAABB = Collision::AABB(minPos, maxPos); + someAABB = AABB(minPos, maxPos); z = Collision::RayAABBIntr(ray, someAABB); if (z) ++test; } diff --git a/src/Tests/ObjectPoolTest.cpp b/src/Tests/ObjectPoolTest.cpp index adea33eb..e136da70 100644 --- a/src/Tests/ObjectPoolTest.cpp +++ b/src/Tests/ObjectPoolTest.cpp @@ -1,7 +1,7 @@ #include using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; -#include "../wmem/MemPrototype/ObjectPool.h" +#include "Engine/Core/ObjectPool.h" #include struct S From 28a290600bf74f9f2a94a5446f12a31866be20d9 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Thu, 3 Dec 2015 17:30:12 +0100 Subject: [PATCH 09/61] BOOST MEM LEAK VS VISUAL STUDIO MEM LEAKS COMMENTED TESTS --- src/Tests/CollisionTest.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 426b9722..90eb8e47 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -1,13 +1,25 @@ +//#define BOOST_TEST_MODULE collTest #include +#include using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include #include //srand +//vs memleaks +//#define _CRTDBG_MAP_ALLOC +//#include +//#include +//#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) +//#define new DEBUG_CLIENTBLOCK + BOOST_AUTO_TEST_SUITE(collisionTests) BOOST_AUTO_TEST_CASE(collisionTest) { + //memleak + int* globalLeak = new int[5]; + //fixed seed srand(2); Collision::Ray ray; @@ -16,7 +28,7 @@ BOOST_AUTO_TEST_CASE(collisionTest) glm::vec3 maxPos; bool z; int test = 0; - for (size_t i = 0; i < 1000000; i++) + for (size_t i = 0; i < 10; i++) { ray.Origin.x = rand() % 100; ray.Origin.y = rand() % 100; @@ -36,6 +48,8 @@ BOOST_AUTO_TEST_CASE(collisionTest) if (z) ++test; } BOOST_CHECK(test >= 0); + + //_CrtDumpMemoryLeaks(); } BOOST_AUTO_TEST_CASE(collisionTest2) From 8e21b1fe35579f5cc54f8eddb30269247020258d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 3 Dec 2015 19:25:40 +0100 Subject: [PATCH 10/61] Added ray collision detection in leaf nodes. Fixed possible bug in OctTree::AddBox --- include/Engine/Core/OctTree.h | 2 +- src/Engine/Core/OctTree.cpp | 36 ++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 0554b12f..2c54513e 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -26,7 +26,7 @@ private: //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; + bool rayCollides(const Ray& ray, Output& data) const; inline bool hasChildren() const; int childIndexContainingPoint(const glm::vec3& point) const; }; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 29575ac7..af222039 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -79,43 +79,45 @@ OctTree::~OctTree() } } - bool OctTree::RayCollides(const Ray& ray, Output& data) const { data.CollideDistance = -1; - return rayCollides(ray, data, this); + return rayCollides(ray, data); } //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 +bool OctTree::rayCollides(const Ray& ray, Output& data) const { //If the node AABB is missed, everything it contains is missed. - if (Collision::RayAABBIntr(ray, tree->m_Box)) { + if (Collision::RayAABBIntr(ray, m_Box)) { //If the ray shoots the tree, and it is a parent to 8 children :o - if (tree->hasChildren()) { + if (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()) }); + childInfos.push_back({ i, glm::distance(ray.Origin, 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])) { + if (m_Children[info.Index]->rayCollides(ray, data)) { 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; + //Check against boxes in the node. + float minDist = INFINITY; + bool intersected = false; + for (const auto& objBox : m_ContainingBoxes) { + float dist; + if (Collision::RayVsAABB(ray, objBox, dist)) { + minDist = std::min(dist, minDist); + intersected = true; + } + } + data.CollideDistance = minDist; + return intersected; } } return false; @@ -138,7 +140,7 @@ void OctTree::AddBox(const AABB& box) case 2: //Four children. bits.flip(); for (int c = 0; c < 8; ++c) { - if ((bits & std::bitset<3>(c))[0]) { + if ((bits & std::bitset<3>(c)).any()) { m_Children[c]->AddBox(box); } } From f0e407bec29d01242daec3939be1aa64c2ef4e9c Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Mon, 7 Dec 2015 10:03:36 +0100 Subject: [PATCH 11/61] Started on OctTreeTest (Merging with OctTree) --- src/Tests/OctTreeTest.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/Tests/OctTreeTest.cpp diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp new file mode 100644 index 00000000..5392e63c --- /dev/null +++ b/src/Tests/OctTreeTest.cpp @@ -0,0 +1,20 @@ +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; +#include //srand +#include + +BOOST_AUTO_TEST_SUITE(octTreeTests) + +BOOST_AUTO_TEST_CASE(octTreeTest) +{ + +} + +BOOST_AUTO_TEST_CASE(octTreeTest2) +{ + +} + +BOOST_AUTO_TEST_SUITE_END() + From e8d23f3b3f4ec58d1b4d0cbde079c340f836476e Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 7 Dec 2015 15:59:41 +0100 Subject: [PATCH 12/61] Added AABBVsAABB function. --- include/Engine/Core/Collision.h | 2 ++ src/Engine/Core/Collision.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index 67a9e808..2e90b8bf 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -11,6 +11,8 @@ bool RayAABBIntr(const Ray& ray, const AABB& box); bool RayVsAABB(const Ray& ray, const AABB& box); bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance); +bool AABBVsAABB(const AABB& a, const AABB& b); +bool AABBVsAABBTst(const AABB& a, const AABB& b); } #endif \ No newline at end of file diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index fda895a4..07e37085 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -58,4 +58,20 @@ bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) return true; } +bool AABBVsAABB(const AABB& a, const AABB& b) +{ + const glm::vec3& aCenter = a.Center(); + const glm::vec3& bCenter = b.Center(); + const glm::vec3& aHSize = a.HalfSize(); + const glm::vec3& bHSize = b.HalfSize(); + //Test will probably exit because of the X and Z axes more often, so test them first. + if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) { + return false; + } + if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) { + return false; + } + return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); +} + } \ No newline at end of file From 955c9ce5340327eed2be0a5558c604632e2c8013 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 7 Dec 2015 16:00:34 +0100 Subject: [PATCH 13/61] Removed superfluous function declaration. --- include/Engine/Core/Collision.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index 2e90b8bf..b23b33cc 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -12,7 +12,6 @@ bool RayVsAABB(const Ray& ray, const AABB& box); bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance); bool AABBVsAABB(const AABB& a, const AABB& b); -bool AABBVsAABBTst(const AABB& a, const AABB& b); } #endif \ No newline at end of file From 04a8eeebe8445df9a985d8488988115e79508349 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 7 Dec 2015 16:25:01 +0100 Subject: [PATCH 14/61] Added test get methods and some comments. --- include/Engine/Core/OctTree.h | 11 +++++++++-- src/Engine/Core/OctTree.cpp | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 2c54513e..6e80fa24 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -1,7 +1,9 @@ #ifndef OctTree_h__ #define OctTree_h__ -#include "Core/Collision.h" +#include "Core/AABB.h" + +struct Ray; class OctTree { @@ -19,13 +21,18 @@ public: //Returns true if the ray collides with something in the tree. Result is written to [data]. bool RayCollides(const Ray& ray, Output& data) const; + //Test Getters. + OctTree** Children() { return m_Children; } + const AABB& Box() { return m_Box; } + const std::vector& ContainingBoxes() { return m_ContainingBoxes; } + 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; inline bool hasChildren() const; int childIndexContainingPoint(const glm::vec3& point) const; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index af222039..f98720be 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -3,6 +3,7 @@ #include #include "Core/OctTree.h" +#include "Core/Collision.h" namespace { @@ -37,7 +38,7 @@ OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) 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(); + const glm::vec3& parentCenter = m_Box.Center(); std::bitset<3> bits(i); //If child is 4,5,6,7. if (bits.test(2)) { @@ -128,6 +129,8 @@ void OctTree::AddBox(const AABB& box) if (hasChildren()) { int minInd = childIndexContainingPoint(box.MinCorner()); int maxInd = childIndexContainingPoint(box.MaxCorner()); + //Because of the predictable ordering of the child indices, + //the number of bits set when xor:ing the indices will determine the number of children containing the box. std::bitset<3> bits(minInd ^ maxInd); switch (bits.count()) { case 0: //Box contained completely in one child. @@ -138,9 +141,14 @@ void OctTree::AddBox(const AABB& box) m_Children[maxInd]->AddBox(box); break; case 2: //Four children. + //Bit-hax to calculate the right 4 cildren containing the box. + //This works because of the childrens index determine what part of + //the dimensions they are responsible for (which octant). bits.flip(); + //At this point the bits necessarily have exactly one bit set. for (int c = 0; c < 8; ++c) { - if ((bits & std::bitset<3>(c)).any()) { + //If the child index have the same bit set as the bits, add box to it. + if (bits.to_ulong() & c) { m_Children[c]->AddBox(box); } } From 648ba502bb5df8f3e8a4eca78927a9220e351308 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 7 Dec 2015 17:27:57 +0100 Subject: [PATCH 15/61] Added small test for OctTree. --- include/Engine/Core/OctTree.h | 5 ----- src/Tests/CollisionTest.cpp | 13 +++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 6e80fa24..1ae447a9 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -21,11 +21,6 @@ public: //Returns true if the ray collides with something in the tree. Result is written to [data]. bool RayCollides(const Ray& ray, Output& data) const; - //Test Getters. - OctTree** Children() { return m_Children; } - const AABB& Box() { return m_Box; } - const std::vector& ContainingBoxes() { return m_ContainingBoxes; } - private: OctTree* m_Children[8]; std::vector m_ContainingBoxes; diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 4d6e9f8c..762fa24e 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -7,6 +7,7 @@ using boost::unit_test_framework::test_case; #include "Engine/Core/AABB.h" #include "Engine/Core/Ray.h" #include //srand +#include "Engine/Core/OctTree.h" //vs memleaks //#define _CRTDBG_MAP_ALLOC @@ -86,5 +87,17 @@ BOOST_AUTO_TEST_CASE(collisionTest2) BOOST_CHECK(test >= 0); } +BOOST_AUTO_TEST_CASE(octTest) +{ + glm::vec3 mini = glm::vec3(-1, -1, -1); + glm::vec3 maxi = glm::vec3(1, 1, 1); + OctTree tree(AABB(mini, maxi), 2); + tree.AddBox(AABB(mini, 0.1f*maxi)); + OctTree::Output data; + BOOST_CHECK(tree.RayCollides({ glm::vec3(0, 0, 0), glm::normalize(mini) }, data)); + tree.ClearBoxes(); + BOOST_CHECK(!tree.RayCollides({ glm::vec3(0, 0, 0), glm::normalize(mini) }, data)); +} + BOOST_AUTO_TEST_SUITE_END() From fa3816e55d0f111a71d2f46bb73818c14f9e672d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 8 Dec 2015 10:44:12 +0100 Subject: [PATCH 16/61] Fixed flawed unit test. --- src/Engine/Core/OctTree.cpp | 1 + src/Tests/CollisionTest.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index f98720be..e9aeb589 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -166,6 +166,7 @@ void OctTree::AddBox(const AABB& box) } } +//TODO: Only clear dynamic boxes, AddDynamic, AddStatic void OctTree::ClearBoxes() { if (hasChildren()) { diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 762fa24e..ab13e930 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -92,11 +92,12 @@ BOOST_AUTO_TEST_CASE(octTest) glm::vec3 mini = glm::vec3(-1, -1, -1); glm::vec3 maxi = glm::vec3(1, 1, 1); OctTree tree(AABB(mini, maxi), 2); - tree.AddBox(AABB(mini, 0.1f*maxi)); + tree.AddBox(AABB(mini, -0.9f*maxi)); OctTree::Output data; - BOOST_CHECK(tree.RayCollides({ glm::vec3(0, 0, 0), glm::normalize(mini) }, data)); + glm::vec3 origin = 3.0f * mini; + BOOST_CHECK(tree.RayCollides({origin , glm::normalize(mini - origin) }, data)); tree.ClearBoxes(); - BOOST_CHECK(!tree.RayCollides({ glm::vec3(0, 0, 0), glm::normalize(mini) }, data)); + BOOST_CHECK(!tree.RayCollides({ origin , glm::normalize(mini - origin) }, data)); } BOOST_AUTO_TEST_SUITE_END() From 06f56d13d14cd4b03617f7a2a8cbf6bea0fcc36c Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Tue, 8 Dec 2015 11:14:01 +0100 Subject: [PATCH 17/61] added some octTreeTest tests and a few comments in OctTree --- src/Engine/Core/OctTree.cpp | 2 ++ src/Tests/OctTreeTest.cpp | 43 ++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index f98720be..7481e770 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -74,6 +74,7 @@ OctTree::~OctTree() { for (OctTree*& c : m_Children) { if (c != nullptr) { + //recursively delete (this calls the deconstructor again) delete c; c = nullptr; } @@ -166,6 +167,7 @@ void OctTree::AddBox(const AABB& box) } } +//remove the content (boxes) in the tree, but dont rememove the tree-structure void OctTree::ClearBoxes() { if (hasChildren()) { diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index 5392e63c..aad95381 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -2,13 +2,55 @@ using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include //srand +//HACK! Needed for white box testing +//else we would have to "open up" the octTree class more with get/sets, public methods, etc. which is not good encapsulation-wise +//friend class and refactoringIntoNewClass is some extra work and needs to be updated when the original class is updated, and can contain bugs that +//isnt in the original class +//Reflection-inspection seems to be only available for C# +//http://stackoverflow.com/questions/6778496/how-to-do-unit-testing-on-private-members-and-methods-of-c-classes +//http://stackoverflow.com/questions/3676664/unit-testing-of-private-methods +#define private public + #include BOOST_AUTO_TEST_SUITE(octTreeTests) BOOST_AUTO_TEST_CASE(octTreeTest) { + //white box testing + //http://softwaretestingfundamentals.com/differences-between-black-box-testing-and-white-box-testing/ + //http://technologyconversations.com/2013/12/11/black-box-vs-white-box-testing/ + //simple AABB constructor check + auto minCorner = glm::vec3(0.0f, 0.0f, 0.0f); + auto maxCorner = glm::vec3(1.0f, 1.0f, 1.0f); + auto someAABB = AABB(minCorner,maxCorner); + BOOST_CHECK(someAABB.MinCorner() == minCorner); + BOOST_CHECK(someAABB.MaxCorner() == maxCorner); + BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner)); + + //simple OctTree constructor check + auto someOctTree = OctTree(someAABB, 5); + BOOST_CHECK(someOctTree.m_Children[0] != nullptr); + //TODO: a check so it split the tree properly + + + + + //advanced AddBox check + //add a boxcontainer - which crosses the mid-split + auto someAABB2 = AABB(glm::vec3(0.45f, 0.45f, 0.45f), glm::vec3(0.55f, 0.55f, 0.55f)); + someOctTree.AddBox(someAABB2); + //clear the boxcontainer + //need to check so it added the box properly + + + someOctTree.ClearBoxes(); + //add a boxcontainer + someOctTree.AddBox(someAABB2); + + + //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure } BOOST_AUTO_TEST_CASE(octTreeTest2) @@ -17,4 +59,3 @@ BOOST_AUTO_TEST_CASE(octTreeTest2) } BOOST_AUTO_TEST_SUITE_END() - From 32d2aa6a93c41a7ae8b9cf0ddbd534e3daf511d7 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 8 Dec 2015 17:01:50 +0100 Subject: [PATCH 18/61] AABB can also be created with center position and its size. --- include/Engine/Core/AABB.h | 4 ++++ src/Engine/Core/AABB.cpp | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/include/Engine/Core/AABB.h b/include/Engine/Core/AABB.h index 7d0a46ea..0a0adafa 100644 --- a/include/Engine/Core/AABB.h +++ b/include/Engine/Core/AABB.h @@ -7,12 +7,16 @@ class AABB { public: AABB() = default; + //No checks are made. Values in minPos must be less than values in maxPos, i.e. min.x < max.x, etc. AABB(const glm::vec3& minPos, const glm::vec3& maxPos); + //No checks are made. Size must consist of non-negative numbers. + virtual void CreateFromCenter(const glm::vec3& center, const glm::vec3& size); 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& Size() const { return 2.0f * m_HalfSize; } const glm::vec3& HalfSize() const { return m_HalfSize; } private: glm::vec3 m_MinCorner; diff --git a/src/Engine/Core/AABB.cpp b/src/Engine/Core/AABB.cpp index 8a364ce5..9da2bdb0 100644 --- a/src/Engine/Core/AABB.cpp +++ b/src/Engine/Core/AABB.cpp @@ -7,5 +7,13 @@ AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos) , m_HalfSize(0.5f * (maxPos - minPos)) {} +void AABB::CreateFromCenter(const glm::vec3& center, const glm::vec3& size) +{ + m_Center = center; + m_HalfSize = 0.5f * size; + m_MinCorner = m_Center - m_HalfSize; + m_MaxCorner = m_Center + m_HalfSize; +} + AABB::~AABB() {} From a7fb77d998bda9a745230c84c6d70c6f6c883784 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 8 Dec 2015 17:12:58 +0100 Subject: [PATCH 19/61] OctTree can check if a box collides with a box in the OctTree. --- include/Engine/Core/OctTree.h | 10 ++-- src/Engine/Core/OctTree.cpp | 98 +++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 1ae447a9..6279e93c 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -20,17 +20,21 @@ public: 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; + //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; 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. + //TODO: Boxes collide with themselves? Fix somehow, maybe float epsilon stuff. + std::vector m_ContainingBoxes; AABB m_Box; - - bool rayCollides(const Ray& ray, Output& data) const; + 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/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index e9aeb589..de38b143 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -48,7 +48,7 @@ OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) minPos.x = parentMin.x; maxPos.x = parentCenter.x; } - + //If child is 2,3,6,7 if (bits.test(1)) { minPos.y = parentCenter.y; @@ -80,14 +80,25 @@ OctTree::~OctTree() } } -bool OctTree::RayCollides(const Ray& ray, Output& data) const +bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const { - data.CollideDistance = -1; - return rayCollides(ray, data); + if (hasChildren()) { + for (int i : childIndicesContainingBox(boxToTest)) { + if (m_Children[i]->BoxCollides(boxToTest, outBoxIntersected)) + return true; + } + } else { + for (const auto& objBox : m_ContainingBoxes) { + if (Collision::AABBVsAABB(boxToTest, objBox)) { + outBoxIntersected = objBox; + return true; + } + } + } + return false; } -//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 +bool OctTree::RayCollides(const Ray& ray, Output& data) const { //If the node AABB is missed, everything it contains is missed. if (Collision::RayAABBIntr(ray, m_Box)) { @@ -102,7 +113,7 @@ bool OctTree::rayCollides(const Ray& ray, Output& data) const 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 (m_Children[info.Index]->rayCollides(ray, data)) { + if (m_Children[info.Index]->RayCollides(ray, data)) { return true; } } @@ -127,39 +138,8 @@ bool OctTree::rayCollides(const Ray& ray, Output& data) const void OctTree::AddBox(const AABB& box) { if (hasChildren()) { - int minInd = childIndexContainingPoint(box.MinCorner()); - int maxInd = childIndexContainingPoint(box.MaxCorner()); - //Because of the predictable ordering of the child indices, - //the number of bits set when xor:ing the indices will determine the number of children containing the box. - 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. - //Bit-hax to calculate the right 4 cildren containing the box. - //This works because of the childrens index determine what part of - //the dimensions they are responsible for (which octant). - bits.flip(); - //At this point the bits necessarily have exactly one bit set. - for (int c = 0; c < 8; ++c) { - //If the child index have the same bit set as the bits, add box to it. - if (bits.to_ulong() & c) { - m_Children[c]->AddBox(box); - } - } - break; - case 3: //Eight children. - for (OctTree*& c : m_Children) { - c->AddBox(box); - } - break; - default: - break; + for (auto i : childIndicesContainingBox(box)) { + m_Children[i]->AddBox(box); } } else { m_ContainingBoxes.push_back(box); @@ -196,6 +176,44 @@ int OctTree::childIndexContainingPoint(const glm::vec3& point) const 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 +{ + int minInd = childIndexContainingPoint(box.MinCorner()); + int maxInd = childIndexContainingPoint(box.MaxCorner()); + //Because of the predictable ordering of the child indices, + //the number of bits set when xor:ing the indices will determine the number of children containing the box. + std::bitset<3> bits(minInd ^ maxInd); + switch (bits.count()) { + //Box contained completely in one child. + case 0: + return{ minInd }; + //Two children. + case 1: + return{ minInd, maxInd }; + //Four children. + case 2: + { + std::vector ret; + //Bit-hax to calculate the right 4 cildren containing the box. + //This works because of the childrens index determine what part of + //the dimensions they are responsible for (which octant). + bits.flip(); + //At this point the bits necessarily have exactly one bit set. + for (int c = 0; c < 8; ++c) { + //If the child index have the same bit set as the bits, add box to it. + if (bits.to_ulong() & c) { + ret.push_back(c); + } + } + return ret; + } + case 3: //Eight children. + return{ 0,1,2,3,4,5,6,7 }; + default: + return std::vector(); + } +} + inline bool OctTree::hasChildren() const { return m_Children[0] != nullptr; From 75402b0f938eb166c5080c37cc7bb2df8750ec1b Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 9 Dec 2015 10:08:08 +0100 Subject: [PATCH 20/61] Working on OctTree tests --- src/Engine/Core/OctTree.cpp | 49 +++--- src/Tests/OctTreeTest.cpp | 6 +- src/Tests/OctTreeTestGameClass.cpp | 78 +++++++++ src/Tests/OctTreeTestGameClass.h | 35 ++++ src/Tests/OctTreeTestGameMain.cpp | 104 ++++++++++++ src/Tests/OctTreeTestHardCodedTestWorld.h | 195 ++++++++++++++++++++++ 6 files changed, 445 insertions(+), 22 deletions(-) create mode 100644 src/Tests/OctTreeTestGameClass.cpp create mode 100644 src/Tests/OctTreeTestGameClass.h create mode 100644 src/Tests/OctTreeTestGameMain.cpp create mode 100644 src/Tests/OctTreeTestHardCodedTestWorld.h diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index af6cc3a8..4e5cdfaa 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -7,17 +7,17 @@ namespace { -//To be able to sort nodes based on distance to ray origin. -struct ChildInfo -{ - int Index; - float Distance; -}; + //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; -} + bool isFirstLower(const ChildInfo& first, const ChildInfo& second) + { + return first.Distance < second.Distance; + } } @@ -32,28 +32,31 @@ OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) for (OctTree*& c : m_Children) { c = nullptr; } - } else { + } + else { --subDivisions; + const glm::vec3& parentMin = m_Box.MinCorner(); + const glm::vec3& parentMax = m_Box.MaxCorner(); + const glm::vec3& parentCenter = m_Box.Center(); 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.Center(); 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 { + } + 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 { + } + else { minPos.y = parentMin.y; maxPos.y = parentCenter.y; } @@ -61,7 +64,8 @@ OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) if (bits.test(0)) { minPos.z = parentCenter.z; maxPos.z = parentMax.z; - } else { + } + else { minPos.z = parentMin.z; maxPos.z = parentCenter.z; } @@ -107,7 +111,8 @@ bool OctTree::rayCollides(const Ray& ray, Output& data) const return true; } } - } else { + } + else { //Check against boxes in the node. float minDist = INFINITY; bool intersected = false; @@ -162,7 +167,8 @@ void OctTree::AddBox(const AABB& box) default: break; } - } else { + } + else { m_ContainingBoxes.push_back(box); } } @@ -175,7 +181,8 @@ void OctTree::ClearBoxes() for (OctTree*& c : m_Children) { c->ClearBoxes(); } - } else { + } + else { m_ContainingBoxes.clear(); } } diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index aad95381..fee2932f 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -2,6 +2,7 @@ using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include //srand +#include "OctTreeTestGameClass.h" //HACK! Needed for white box testing //else we would have to "open up" the octTree class more with get/sets, public methods, etc. which is not good encapsulation-wise //friend class and refactoringIntoNewClass is some extra work and needs to be updated when the original class is updated, and can contain bugs that @@ -10,7 +11,6 @@ using boost::unit_test_framework::test_case; //http://stackoverflow.com/questions/6778496/how-to-do-unit-testing-on-private-members-and-methods-of-c-classes //http://stackoverflow.com/questions/3676664/unit-testing-of-private-methods #define private public - #include BOOST_AUTO_TEST_SUITE(octTreeTests) @@ -55,6 +55,10 @@ BOOST_AUTO_TEST_CASE(octTreeTest) BOOST_AUTO_TEST_CASE(octTreeTest2) { + Game game(0, nullptr); + while (game.Running()) { + game.Tick(); + } } diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp new file mode 100644 index 00000000..4a6236c2 --- /dev/null +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -0,0 +1,78 @@ +#include "OctTreeTestGameClass.h" + +Game::Game(int argc, char* argv[]) +{ + ResourceManager::RegisterType("ConfigFile"); + ResourceManager::RegisterType("Model"); + ResourceManager::RegisterType("Texture"); + + m_Config = ResourceManager::Load("Config.ini"); + LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); + + // Create the core event broker + m_EventBroker = new EventBroker(); + + m_RenderQueueFactory = new RenderQueueFactory(); + + // Create the renderer + m_Renderer = new Renderer(); + m_Renderer->SetFullscreen(m_Config->Get("Video.Fullscreen", false)); + m_Renderer->SetVSYNC(m_Config->Get("Video.VSYNC", false)); + m_Renderer->SetResolution(Rectangle( + 0, + 0, + m_Config->Get("Video.Width", 1280), + m_Config->Get("Video.Height", 720) + )); + m_Renderer->Initialize(); + + // Create input manager + m_InputManager = new InputManager(m_Renderer->Window(), m_EventBroker); + + // Create the root level GUI frame + m_FrameStack = new GUI::Frame(m_EventBroker); + m_FrameStack->Width = m_Renderer->Resolution().Width; + m_FrameStack->Height = m_Renderer->Resolution().Height; + + // Create a TEST WORLD + m_World = new HardcodedTestWorld(); + + m_LastTime = glfwGetTime(); +} + +Game::~Game() +{ + delete m_FrameStack; + delete m_EventBroker; +} + +void Game::Tick() +{ + double currentTime = glfwGetTime(); + double dt = currentTime - m_LastTime; + m_LastTime = currentTime; + + m_EventBroker->Swap(); + m_InputManager->Update(dt); + m_Renderer->Update(dt); + m_EventBroker->Swap(); + + //movement + //auto transf = m_World->GetComponent(m_World->OctTreeEntityIdSaved, "Transform"); + //((glm::vec3&)transf["Position"]).x += 0.001f; + + m_RenderQueueFactory->Update(m_World); + + //wireframe + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + m_Renderer->Draw(m_RenderQueueFactory->RenderQueues()); + + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + + m_EventBroker->Swap(); + m_EventBroker->Clear(); + + glfwPollEvents(); +} diff --git a/src/Tests/OctTreeTestGameClass.h b/src/Tests/OctTreeTestGameClass.h new file mode 100644 index 00000000..9f4ae8c7 --- /dev/null +++ b/src/Tests/OctTreeTestGameClass.h @@ -0,0 +1,35 @@ +#ifndef Game_h__ +#define Game_h__ + +#include "Core/ResourceManager.h" +#include "Core/ConfigFile.h" +#include "Core/EventBroker.h" +#include "Rendering/Renderer.h" +#include "Core/InputManager.h" +#include "GUI/Frame.h" +#include "Core/World.h" +#include "Rendering/RenderQueueFactory.h" + +#include "OctTreeTestHardCodedTestWorld.h" + +class Game +{ +public: + Game(int argc, char* argv[]); + ~Game(); + + bool Running() const { return !glfwWindowShouldClose(m_Renderer->Window()); } + void Tick(); + +private: + double m_LastTime; + ConfigFile* m_Config = nullptr; + EventBroker* m_EventBroker; + IRenderer* m_Renderer; + InputManager* m_InputManager; + GUI::Frame* m_FrameStack; + HardcodedTestWorld* m_World; + RenderQueueFactory* m_RenderQueueFactory; +}; + +#endif diff --git a/src/Tests/OctTreeTestGameMain.cpp b/src/Tests/OctTreeTestGameMain.cpp new file mode 100644 index 00000000..ab13e930 --- /dev/null +++ b/src/Tests/OctTreeTestGameMain.cpp @@ -0,0 +1,104 @@ +//#define BOOST_TEST_MODULE collTest +#include +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; +#include +#include "Engine/Core/AABB.h" +#include "Engine/Core/Ray.h" +#include //srand +#include "Engine/Core/OctTree.h" + +//vs memleaks +//#define _CRTDBG_MAP_ALLOC +//#include +//#include +//#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) +//#define new DEBUG_CLIENTBLOCK + +BOOST_AUTO_TEST_SUITE(collisionTests) + +BOOST_AUTO_TEST_CASE(collisionTest) +{ + //memleak + int* globalLeak = new int[5]; + + //fixed seed + srand(2); + Ray ray; + AABB someAABB; + glm::vec3 minPos; + glm::vec3 maxPos; + bool z; + int test = 0; + for (size_t i = 0; i < 10; i++) + { + ray.Origin.x = rand() % 100; + ray.Origin.y = rand() % 100; + ray.Origin.z = rand() % 100; + ray.Direction.x = rand() % 100; + ray.Direction.y = rand() % 100; + ray.Direction.z = rand() % 100; + minPos.x = rand() % 100; + minPos.y = rand() % 100; + minPos.z = rand() % 100; + maxPos.x = rand() % 100; + maxPos.y = rand() % 100; + maxPos.z = rand() % 100; + + someAABB = AABB(minPos, maxPos); + z = Collision::RayVsAABB(ray, someAABB); + if (z) ++test; + } + BOOST_CHECK(test >= 0); + + //_CrtDumpMemoryLeaks(); +} + +BOOST_AUTO_TEST_CASE(collisionTest2) +{ + //fixed seed + srand(2); + Ray ray; + AABB someAABB; + glm::vec3 minPos; + glm::vec3 maxPos; + bool z; + int test = 0; + for (size_t i = 0; i < 1000000; i++) + { + ray.Origin.x = rand() % 100; + ray.Origin.y = rand() % 100; + ray.Origin.z = rand() % 100; + ray.Direction.x = rand() % 100; + ray.Direction.y = rand() % 100; + ray.Direction.z = rand() % 100; + minPos.x = rand() % 100; + minPos.y = rand() % 100; + minPos.z = rand() % 100; + maxPos.x = rand() % 100; + maxPos.y = rand() % 100; + maxPos.z = rand() % 100; + + someAABB = AABB(minPos, maxPos); + z = Collision::RayAABBIntr(ray, someAABB); + if (z) ++test; + } + BOOST_CHECK(test >= 0); +} + +BOOST_AUTO_TEST_CASE(octTest) +{ + glm::vec3 mini = glm::vec3(-1, -1, -1); + glm::vec3 maxi = glm::vec3(1, 1, 1); + OctTree tree(AABB(mini, maxi), 2); + tree.AddBox(AABB(mini, -0.9f*maxi)); + OctTree::Output data; + glm::vec3 origin = 3.0f * mini; + BOOST_CHECK(tree.RayCollides({origin , glm::normalize(mini - origin) }, data)); + tree.ClearBoxes(); + BOOST_CHECK(!tree.RayCollides({ origin , glm::normalize(mini - origin) }, data)); +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h new file mode 100644 index 00000000..f5caf308 --- /dev/null +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -0,0 +1,195 @@ +#include +#include +#include +#include "GLM.h" +#include "Core/World.h" +#include "Core/Util/Any.h" + +//octTree +//#include + +//last! +#define private public +#include + +class HardcodedTestWorld : public World +{ +public: + EntityID OctTreeEntityIdSaved; + + //constructor + 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); + } + + 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"; + //} + + //add octTree + { + //ComponentWrapper debug = world.AttachComponent(e, "Debug"); + //// Set the Name field of the Debug component using subscript operator + //debug["Name"] = "Blah"; + auto minCorner = glm::vec3(0.0f, 0.0f, 0.0f); + auto maxCorner = glm::vec3(1.0f, 1.0f, 1.0f); + auto someAABB = AABB(minCorner, maxCorner); + + auto someOctTree = OctTree(someAABB, 2); + + //auto min1 = someOctTree.m_Children[i]->m_Box.MinCorner(); + //auto max1 = someOctTree.m_Children[i]->m_Box.MaxCorner(); + + float boxDrawFactor = 1.05f; + auto halfSizeFactor = 0.0f; + halfSizeFactor = someAABB.HalfSize().x; + + //the first box first + EntityID entityDummyScene = world.CreateEntity(); + + ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); + transform["Position"] = someAABB.Center()*1.0f; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor; + + ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); + model["Resource"] = "Models/Core/UnitBox.obj"; + model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + + //just draw the first 8 children - looks nicer in code if i split it this way + for (size_t i = 0; i < 8; i++) + { + auto cen1 = someOctTree.m_Children[i]->m_Box.Center(); + halfSizeFactor = someOctTree.m_Children[i]->m_Box.HalfSize().x; + + EntityID entityDummyScene = world.CreateEntity(); + + ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); + transform["Position"] = cen1*1.0f; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*0.97f*halfSizeFactor*boxDrawFactor; + + ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); + model["Resource"] = "Models/Core/UnitBox.obj"; + model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + + //OutputDebugStringA((std::to_string(cen1.x) + std::to_string(cen1.y) + std::to_string(cen1.z)).c_str()); + //OctTreeEntityIdSaved = entityDummyScene; + } + + //then draw the childrens children + //for (size_t j = 0; j < 8; j++) + //{ + // auto someChild = someOctTree.m_Children[j]; + + // for (size_t i = 0; i < 8; i++) + // { + // auto cen1 = someChild->m_Children[i]->m_Box.Center(); + // auto boxScale = someChild->m_Children[i]->m_Box.HalfSize(); + + // EntityID entityDummyScene = world.CreateEntity(); + + // ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); + // transform["Position"] = cen1*1.0f; + // transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*0.97f*boxScale*boxDrawFactor; + + // ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); + // model["Resource"] = "Models/Core/UnitBox.obj"; + // model["Color"] = glm::vec4(0.0f, 1.0f, 0.0f, 1.0f); + // //OctTreeEntityIdSaved = entityDummyScene; + // } + + //} + + + } + + + } +}; \ No newline at end of file From 1d0810db1094e3c67aaefdf8c9a81a7ab6fc167b Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 9 Dec 2015 10:24:06 +0100 Subject: [PATCH 21/61] OctTree is now drawn perfectly --- src/Tests/OctTreeTestHardCodedTestWorld.h | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index f5caf308..4ce0c587 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -128,7 +128,7 @@ private: //auto min1 = someOctTree.m_Children[i]->m_Box.MinCorner(); //auto max1 = someOctTree.m_Children[i]->m_Box.MaxCorner(); - float boxDrawFactor = 1.05f; + float boxDrawFactor = 1.00f; auto halfSizeFactor = 0.0f; halfSizeFactor = someAABB.HalfSize().x; @@ -137,10 +137,10 @@ private: ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); transform["Position"] = someAABB.Center()*1.0f; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor*2.0f; ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox.obj"; + model["Resource"] = "Models/Core/UnitBox2.obj"; model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); //just draw the first 8 children - looks nicer in code if i split it this way @@ -153,10 +153,10 @@ private: ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); transform["Position"] = cen1*1.0f; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*0.97f*halfSizeFactor*boxDrawFactor; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor*2.0f; ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox.obj"; + model["Resource"] = "Models/Core/UnitBox2.obj"; model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); //OutputDebugStringA((std::to_string(cen1.x) + std::to_string(cen1.y) + std::to_string(cen1.z)).c_str()); @@ -164,28 +164,28 @@ private: } //then draw the childrens children - //for (size_t j = 0; j < 8; j++) - //{ - // auto someChild = someOctTree.m_Children[j]; + for (size_t j = 0; j < 8; j++) + { + auto someChild = someOctTree.m_Children[j]; - // for (size_t i = 0; i < 8; i++) - // { - // auto cen1 = someChild->m_Children[i]->m_Box.Center(); - // auto boxScale = someChild->m_Children[i]->m_Box.HalfSize(); + for (size_t i = 0; i < 8; i++) + { + auto cen1 = someChild->m_Children[i]->m_Box.Center(); + halfSizeFactor = someChild->m_Children[i]->m_Box.HalfSize().x; - // EntityID entityDummyScene = world.CreateEntity(); + EntityID entityDummyScene = world.CreateEntity(); - // ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); - // transform["Position"] = cen1*1.0f; - // transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*0.97f*boxScale*boxDrawFactor; + ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); + transform["Position"] = cen1*1.0f; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor*2.0f; - // ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - // model["Resource"] = "Models/Core/UnitBox.obj"; - // model["Color"] = glm::vec4(0.0f, 1.0f, 0.0f, 1.0f); - // //OctTreeEntityIdSaved = entityDummyScene; - // } + ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); + model["Resource"] = "Models/Core/UnitBox2.obj"; + model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + //OctTreeEntityIdSaved = entityDummyScene; + } - //} + } } From bad42fae1c00b2e5a6f9c3d996d75d94266dde71 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 9 Dec 2015 13:27:00 +0100 Subject: [PATCH 22/61] OctTree differentiates between static/dynamic objects now. Added a general BoxesInSameRegion method. --- include/Engine/Core/OctTree.h | 27 +++++++++--- src/Engine/Core/OctTree.cpp | 82 +++++++++++++++++++++++++++-------- src/Tests/CollisionTest.cpp | 10 +++-- 3 files changed, 92 insertions(+), 27 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 6279e93c..911375d5 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -16,8 +16,21 @@ public: ~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(); + + //We should only ever need one OctTree in the game, and it should not need to be copied. + //Define these if the OctTree suddenly needs to be copied, think of the children OctTree* ptrs. + OctTree(const OctTree& other) = delete; + OctTree(const OctTree&& other) = delete; + OctTree& operator= (const OctTree& 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(); + //Returns true if the ray collides with something in the tree. Result is written to [data]. bool RayCollides(const Ray& ray, Output& data) const; //Returns true if the box collides with something in the tree. @@ -26,10 +39,12 @@ public: private: OctTree* m_Children[8]; - //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. - //TODO: Boxes collide with themselves? Fix somehow, maybe float epsilon stuff. - std::vector m_ContainingBoxes; + //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; inline bool hasChildren() const; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index de38b143..66355580 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -88,10 +88,16 @@ bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const return true; } } else { - for (const auto& objBox : m_ContainingBoxes) { - if (Collision::AABBVsAABB(boxToTest, objBox)) { - outBoxIntersected = objBox; - return true; + std::vector> objVectors = { + m_StaticObjects, + m_DynamicObjects + }; + for (const auto& objVector : objVectors) { + for (const auto& obj : objVector) { + if (Collision::AABBVsAABB(boxToTest, obj)) { + outBoxIntersected = obj; + return true; + } } } } @@ -121,13 +127,20 @@ bool OctTree::RayCollides(const Ray& ray, Output& data) const //Check against boxes in the node. float minDist = INFINITY; bool intersected = false; - for (const auto& objBox : m_ContainingBoxes) { - float dist; - if (Collision::RayVsAABB(ray, objBox, dist)) { - minDist = std::min(dist, minDist); - intersected = true; + 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; + } } } + data.CollideDistance = minDist; return intersected; } @@ -135,26 +148,61 @@ bool OctTree::RayCollides(const Ray& ray, Output& data) const return false; } -void OctTree::AddBox(const AABB& box) + +void OctTree::AddDynamicObject(const AABB& box) { if (hasChildren()) { for (auto i : childIndicesContainingBox(box)) { - m_Children[i]->AddBox(box); + m_Children[i]->AddDynamicObject(box); } } else { - m_ContainingBoxes.push_back(box); + m_DynamicObjects.push_back(box); } } -//TODO: Only clear dynamic boxes, AddDynamic, AddStatic -void OctTree::ClearBoxes() +void OctTree::AddStaticObject(const AABB& box) +{ + if (hasChildren()) { + for (auto i : childIndicesContainingBox(box)) { + m_Children[i]->AddStaticObject(box); + } + } else { + m_StaticObjects.push_back(box); + } +} + +void OctTree::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()); + } +} + +void OctTree::ClearObjects() { if (hasChildren()) { for (OctTree*& c : m_Children) { - c->ClearBoxes(); + c->ClearObjects(); } } else { - m_ContainingBoxes.clear(); + m_DynamicObjects.clear(); + m_StaticObjects.clear(); + } +} + +void OctTree::ClearDynamicObjects() +{ + if (hasChildren()) { + for (OctTree*& c : m_Children) { + c->ClearObjects(); + } + } else { + m_DynamicObjects.clear(); } } @@ -194,7 +242,7 @@ std::vector OctTree::childIndicesContainingBox(const AABB& box) const case 2: { std::vector ret; - //Bit-hax to calculate the right 4 cildren containing the box. + //Bit-hax to calculate the correct 4 children containing the box. //This works because of the childrens index determine what part of //the dimensions they are responsible for (which octant). bits.flip(); diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index ab13e930..eef24e61 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -92,12 +92,14 @@ BOOST_AUTO_TEST_CASE(octTest) glm::vec3 mini = glm::vec3(-1, -1, -1); glm::vec3 maxi = glm::vec3(1, 1, 1); OctTree tree(AABB(mini, maxi), 2); - tree.AddBox(AABB(mini, -0.9f*maxi)); + tree.AddDynamicObject(AABB(mini, -0.9f*maxi)); OctTree::Output data; glm::vec3 origin = 3.0f * mini; - BOOST_CHECK(tree.RayCollides({origin , glm::normalize(mini - origin) }, data)); - tree.ClearBoxes(); - BOOST_CHECK(!tree.RayCollides({ origin , glm::normalize(mini - origin) }, data)); + bool rayIntersected = tree.RayCollides({ origin , glm::normalize(mini - origin) }, data); + BOOST_CHECK(rayIntersected); + tree.ClearDynamicObjects(); + rayIntersected = tree.RayCollides({ origin , glm::normalize(mini - origin) }, data); + BOOST_CHECK(!rayIntersected); } BOOST_AUTO_TEST_SUITE_END() From ed5cd392ab46c8cb1b681cbdb969ec258bfbd8a7 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 9 Dec 2015 13:29:15 +0100 Subject: [PATCH 23/61] Further work in testing OctTree --- src/Tests/OctTreeTestGameClass.cpp | 11 ++ src/Tests/OctTreeTestGameClass.h | 3 + src/Tests/OctTreeTestGameMain.cpp | 6 +- src/Tests/OctTreeTestHardCodedTestWorld.h | 163 ++++++---------------- 4 files changed, 60 insertions(+), 123 deletions(-) diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp index 4a6236c2..918782ec 100644 --- a/src/Tests/OctTreeTestGameClass.cpp +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -58,9 +58,20 @@ void Game::Tick() m_EventBroker->Swap(); //movement + minPos.x += 0.001f; + //frameCounter++; + //if (frameCounter > 50) { + // m_World->createTestEntities(AABB(minPos, glm::vec3(0.1f, 0.4f, 0.6f))); + // frameCounter = 0; + //} //auto transf = m_World->GetComponent(m_World->OctTreeEntityIdSaved, "Transform"); //((glm::vec3&)transf["Position"]).x += 0.001f; + //for (auto oneModel : m_World->allModels) + //{ + + //} + m_RenderQueueFactory->Update(m_World); //wireframe diff --git a/src/Tests/OctTreeTestGameClass.h b/src/Tests/OctTreeTestGameClass.h index 9f4ae8c7..887ad59b 100644 --- a/src/Tests/OctTreeTestGameClass.h +++ b/src/Tests/OctTreeTestGameClass.h @@ -30,6 +30,9 @@ private: GUI::Frame* m_FrameStack; HardcodedTestWorld* m_World; RenderQueueFactory* m_RenderQueueFactory; + + int frameCounter = 0; + glm::vec3 minPos = glm::vec3(-0.2f, 0.2f, 0.3f); }; #endif diff --git a/src/Tests/OctTreeTestGameMain.cpp b/src/Tests/OctTreeTestGameMain.cpp index ab13e930..9266754e 100644 --- a/src/Tests/OctTreeTestGameMain.cpp +++ b/src/Tests/OctTreeTestGameMain.cpp @@ -16,9 +16,9 @@ using boost::unit_test_framework::test_case; //#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) //#define new DEBUG_CLIENTBLOCK -BOOST_AUTO_TEST_SUITE(collisionTests) +BOOST_AUTO_TEST_SUITE(cTest) -BOOST_AUTO_TEST_CASE(collisionTest) +BOOST_AUTO_TEST_CASE(cTest) { //memleak int* globalLeak = new int[5]; @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(collisionTest) //_CrtDumpMemoryLeaks(); } -BOOST_AUTO_TEST_CASE(collisionTest2) +BOOST_AUTO_TEST_CASE(cTest2) { //fixed seed srand(2); diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index 4ce0c587..1e785994 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -8,6 +8,7 @@ //octTree //#include +#include //last! #define private public #include @@ -15,14 +16,20 @@ class HardcodedTestWorld : public World { public: + struct LinkOctTreeAndModel { + EntityID entId; + }; EntityID OctTreeEntityIdSaved; + //std::vector allModels; + //ComponentWrapper moveModel; + //OctTree* someOctTreePointer; //constructor HardcodedTestWorld() : World() { registerTestComponents(); - createTestEntities(); + //createTestEntities(); } private: @@ -54,142 +61,58 @@ private: RegisterComponent(f); } - void createTestEntities() + void createTestEntities(AABB anotherBox) { 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"; - //} - + //add octTree { - //ComponentWrapper debug = world.AttachComponent(e, "Debug"); - //// Set the Name field of the Debug component using subscript operator - //debug["Name"] = "Blah"; - auto minCorner = glm::vec3(0.0f, 0.0f, 0.0f); - auto maxCorner = glm::vec3(1.0f, 1.0f, 1.0f); - auto someAABB = AABB(minCorner, maxCorner); + auto someAABB = AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)); + OctTree someOctTree = OctTree(someAABB, 2); + //add a box + //auto anotherBox = AABB(glm::vec3(-0.2f, 0.2f, 0.3f), glm::vec3(0.1f, 0.4f, 0.6f)); + //note: have to delete the box in the tree first, since were trying to move the box + someOctTree.ClearBoxes(); + someOctTree.AddBox(anotherBox); - auto someOctTree = OctTree(someAABB, 2); + //the main box first + AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, someOctTree.m_ContainingBoxes.size()); - //auto min1 = someOctTree.m_Children[i]->m_Box.MinCorner(); - //auto max1 = someOctTree.m_Children[i]->m_Box.MaxCorner(); + //draw anotherbox + AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, 0); - float boxDrawFactor = 1.00f; - auto halfSizeFactor = 0.0f; - halfSizeFactor = someAABB.HalfSize().x; - - //the first box first - EntityID entityDummyScene = world.CreateEntity(); - - ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); - transform["Position"] = someAABB.Center()*1.0f; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor*2.0f; - - ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox2.obj"; - model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - - //just draw the first 8 children - looks nicer in code if i split it this way - for (size_t i = 0; i < 8; i++) - { - auto cen1 = someOctTree.m_Children[i]->m_Box.Center(); - halfSizeFactor = someOctTree.m_Children[i]->m_Box.HalfSize().x; - - EntityID entityDummyScene = world.CreateEntity(); - - ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); - transform["Position"] = cen1*1.0f; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor*2.0f; - - ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox2.obj"; - model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - - //OutputDebugStringA((std::to_string(cen1.x) + std::to_string(cen1.y) + std::to_string(cen1.z)).c_str()); - //OctTreeEntityIdSaved = entityDummyScene; - } - - //then draw the childrens children + //draw the octTree for (size_t j = 0; j < 8; j++) { + AddBoxModel(someOctTree.m_Children[j]->m_Box.Center(), + someOctTree.m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Children[j]->m_ContainingBoxes.size()); + auto someChild = someOctTree.m_Children[j]; for (size_t i = 0; i < 8; i++) { - auto cen1 = someChild->m_Children[i]->m_Box.Center(); - halfSizeFactor = someChild->m_Children[i]->m_Box.HalfSize().x; - - EntityID entityDummyScene = world.CreateEntity(); - - ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); - transform["Position"] = cen1*1.0f; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSizeFactor*boxDrawFactor*2.0f; - - ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox2.obj"; - model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - //OctTreeEntityIdSaved = entityDummyScene; + AddBoxModel(someChild->m_Children[i]->m_Box.Center(), + someChild->m_Children[i]->m_Box.HalfSize().x, someChild->m_Children[i]->m_ContainingBoxes.size()); } - } - - } + }//end CreateEnt + void AddBoxModel(const glm::vec3 ¢er, const float &halfSize, const int &contBoxes) { + World& world = *this; + EntityID entityDummyScene = world.CreateEntity(); + + ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); + transform["Position"] = center; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSize*2.0f; + ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); + model["Resource"] = "Models/Core/UnitBox2.obj"; + model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + if (contBoxes != 0) + model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + + //extra + //allModels.push_back(model); } }; \ No newline at end of file From 14bcb7ea0db5d54a76653d907692af1e8749aa67 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 9 Dec 2015 13:52:17 +0100 Subject: [PATCH 24/61] Added Update call in Game to test OctTree by using collision. --- include/Engine/Core/OctTree.h | 10 +++++++ include/Game/Game.h | 3 +++ include/Game/HardcodedTestWorld.h | 36 ++++++++++++++++++++++++++ src/Engine/Core/OctTree.cpp | 43 +++++++++++++++++++++++++++++++ src/Game/Game.cpp | 9 +++++++ 5 files changed, 101 insertions(+) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 911375d5..e5d5a83a 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -4,6 +4,8 @@ #include "Core/AABB.h" struct Ray; +class World; +class Camera; class OctTree { @@ -23,6 +25,9 @@ public: OctTree(const OctTree&& other) = delete; OctTree& operator= (const OctTree& other) = delete; + //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); @@ -47,6 +52,11 @@ private: std::vector m_DynamicObjects; AABB m_Box; + bool m_UpdatedOnce; + unsigned int m_BoxID; + glm::vec3 m_PrevPos; + glm::quat m_PrevOri; + inline bool hasChildren() const; int childIndexContainingPoint(const glm::vec3& point) const; std::vector childIndicesContainingBox(const AABB& box) const; diff --git a/include/Game/Game.h b/include/Game/Game.h index cd16a3dd..8667b8c8 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -10,6 +10,8 @@ #include "Core/World.h" #include "Rendering/RenderQueueFactory.h" +class OctTree; + class Game { public: @@ -28,6 +30,7 @@ private: GUI::Frame* m_FrameStack; World* m_World; RenderQueueFactory* m_RenderQueueFactory; + OctTree* m_OctTree; }; #endif diff --git a/include/Game/HardcodedTestWorld.h b/include/Game/HardcodedTestWorld.h index f183330f..5f7cc5f6 100644 --- a/include/Game/HardcodedTestWorld.h +++ b/include/Game/HardcodedTestWorld.h @@ -42,6 +42,11 @@ private: 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() @@ -103,6 +108,37 @@ private: 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; + } diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 66355580..82c1b554 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -4,6 +4,8 @@ #include "Core/OctTree.h" #include "Core/Collision.h" +#include "Core/World.h" +#include "Rendering/Camera.h" namespace { @@ -27,6 +29,7 @@ OctTree::OctTree() OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) : m_Box(octTreeBounds) + , m_UpdatedOnce(false) { if (subDivisions == 0) { for (OctTree*& c : m_Children) { @@ -80,6 +83,46 @@ OctTree::~OctTree() } } +void OctTree::Update(float dt, World* world, Camera* cam) +{ + AABB aabb; + for (ComponentWrapper& c : world->GetComponents("Collision")) { + aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]); + AddStaticObject(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); + + 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; + } + + AABB box; + auto boxPos = cam->Position() + 1.2f*cam->Forward(); + box.CreateFromCenter(boxPos, boxSize); + 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)) { + cam->SetPosition(m_PrevPos); + cam->SetOrientation(m_PrevOri); + model["Color"] = greenCol; + } else { + model["Color"] = redCol; + } + + m_PrevPos = cam->Position(); + m_PrevOri = cam->Orientation(); + ClearObjects(); +} + bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const { if (hasChildren()) { diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 416b054e..03e71dce 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,5 +1,6 @@ #include "Game.h" #include "HardcodedTestWorld.h" +#include "Core/OctTree.h" Game::Game(int argc, char* argv[]) { @@ -38,6 +39,10 @@ Game::Game(int argc, char* argv[]) // 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); + m_LastTime = glfwGetTime(); } @@ -45,6 +50,7 @@ Game::~Game() { delete m_FrameStack; delete m_EventBroker; + delete m_OctTree; } void Game::Tick() @@ -56,6 +62,9 @@ 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); From 8949db5456161fbc7a8814757b06c6a8a9c9e611 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 9 Dec 2015 13:56:59 +0100 Subject: [PATCH 25/61] Little more work with OctTreeTest --- src/Tests/OctTreeTest.cpp | 9 ++- src/Tests/OctTreeTestGameMain.cpp | 83 ----------------------- src/Tests/OctTreeTestHardCodedTestWorld.h | 14 ++-- 3 files changed, 11 insertions(+), 95 deletions(-) diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index fee2932f..962db3d4 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(octTreeTest) BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner)); //simple OctTree constructor check - auto someOctTree = OctTree(someAABB, 5); + OctTree someOctTree(someAABB, 5); BOOST_CHECK(someOctTree.m_Children[0] != nullptr); //TODO: a check so it split the tree properly @@ -40,14 +40,13 @@ BOOST_AUTO_TEST_CASE(octTreeTest) //advanced AddBox check //add a boxcontainer - which crosses the mid-split auto someAABB2 = AABB(glm::vec3(0.45f, 0.45f, 0.45f), glm::vec3(0.55f, 0.55f, 0.55f)); - someOctTree.AddBox(someAABB2); + someOctTree.AddDynamicObject(someAABB2); //clear the boxcontainer //need to check so it added the box properly - - someOctTree.ClearBoxes(); + someOctTree.ClearDynamicObjects(); //add a boxcontainer - someOctTree.AddBox(someAABB2); + someOctTree.AddDynamicObject(someAABB2); //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure diff --git a/src/Tests/OctTreeTestGameMain.cpp b/src/Tests/OctTreeTestGameMain.cpp index 9266754e..5dd23cf2 100644 --- a/src/Tests/OctTreeTestGameMain.cpp +++ b/src/Tests/OctTreeTestGameMain.cpp @@ -17,88 +17,5 @@ using boost::unit_test_framework::test_case; //#define new DEBUG_CLIENTBLOCK BOOST_AUTO_TEST_SUITE(cTest) - -BOOST_AUTO_TEST_CASE(cTest) -{ - //memleak - int* globalLeak = new int[5]; - - //fixed seed - srand(2); - Ray ray; - AABB someAABB; - glm::vec3 minPos; - glm::vec3 maxPos; - bool z; - int test = 0; - for (size_t i = 0; i < 10; i++) - { - ray.Origin.x = rand() % 100; - ray.Origin.y = rand() % 100; - ray.Origin.z = rand() % 100; - ray.Direction.x = rand() % 100; - ray.Direction.y = rand() % 100; - ray.Direction.z = rand() % 100; - minPos.x = rand() % 100; - minPos.y = rand() % 100; - minPos.z = rand() % 100; - maxPos.x = rand() % 100; - maxPos.y = rand() % 100; - maxPos.z = rand() % 100; - - someAABB = AABB(minPos, maxPos); - z = Collision::RayVsAABB(ray, someAABB); - if (z) ++test; - } - BOOST_CHECK(test >= 0); - - //_CrtDumpMemoryLeaks(); -} - -BOOST_AUTO_TEST_CASE(cTest2) -{ - //fixed seed - srand(2); - Ray ray; - AABB someAABB; - glm::vec3 minPos; - glm::vec3 maxPos; - bool z; - int test = 0; - for (size_t i = 0; i < 1000000; i++) - { - ray.Origin.x = rand() % 100; - ray.Origin.y = rand() % 100; - ray.Origin.z = rand() % 100; - ray.Direction.x = rand() % 100; - ray.Direction.y = rand() % 100; - ray.Direction.z = rand() % 100; - minPos.x = rand() % 100; - minPos.y = rand() % 100; - minPos.z = rand() % 100; - maxPos.x = rand() % 100; - maxPos.y = rand() % 100; - maxPos.z = rand() % 100; - - someAABB = AABB(minPos, maxPos); - z = Collision::RayAABBIntr(ray, someAABB); - if (z) ++test; - } - BOOST_CHECK(test >= 0); -} - -BOOST_AUTO_TEST_CASE(octTest) -{ - glm::vec3 mini = glm::vec3(-1, -1, -1); - glm::vec3 maxi = glm::vec3(1, 1, 1); - OctTree tree(AABB(mini, maxi), 2); - tree.AddBox(AABB(mini, -0.9f*maxi)); - OctTree::Output data; - glm::vec3 origin = 3.0f * mini; - BOOST_CHECK(tree.RayCollides({origin , glm::normalize(mini - origin) }, data)); - tree.ClearBoxes(); - BOOST_CHECK(!tree.RayCollides({ origin , glm::normalize(mini - origin) }, data)); -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index 1e785994..dbdbcb45 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -29,7 +29,7 @@ public: : World() { registerTestComponents(); - //createTestEntities(); + createTestEntities(AABB(glm::vec3(-0.2f, 0.2f, 0.3f), glm::vec3(0.1f, 0.4f, 0.6f))); } private: @@ -68,15 +68,15 @@ private: //add octTree { auto someAABB = AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)); - OctTree someOctTree = OctTree(someAABB, 2); + OctTree someOctTree(someAABB, 2); //add a box //auto anotherBox = AABB(glm::vec3(-0.2f, 0.2f, 0.3f), glm::vec3(0.1f, 0.4f, 0.6f)); //note: have to delete the box in the tree first, since were trying to move the box - someOctTree.ClearBoxes(); - someOctTree.AddBox(anotherBox); + someOctTree.ClearDynamicObjects(); + someOctTree.AddDynamicObject(anotherBox); //the main box first - AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, someOctTree.m_ContainingBoxes.size()); + AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, someOctTree.m_DynamicObjects.size()); //draw anotherbox AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, 0); @@ -85,14 +85,14 @@ private: for (size_t j = 0; j < 8; j++) { AddBoxModel(someOctTree.m_Children[j]->m_Box.Center(), - someOctTree.m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Children[j]->m_ContainingBoxes.size()); + someOctTree.m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Children[j]->m_DynamicObjects.size()); auto someChild = someOctTree.m_Children[j]; for (size_t i = 0; i < 8; i++) { AddBoxModel(someChild->m_Children[i]->m_Box.Center(), - someChild->m_Children[i]->m_Box.HalfSize().x, someChild->m_Children[i]->m_ContainingBoxes.size()); + someChild->m_Children[i]->m_Box.HalfSize().x, someChild->m_Children[i]->m_DynamicObjects.size()); } } } From e580540143b7827795f113f8aaaf54bef38ac114 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 9 Dec 2015 17:16:11 +0100 Subject: [PATCH 26/61] Made some optimizations in OctTree and added a unit test for BoxesInSameRegion. --- include/Engine/Core/OctTree.h | 9 +++--- src/Engine/Core/OctTree.cpp | 55 ++++++++++++++++++++++------------- src/Tests/OctTreeTest.cpp | 20 +++++++++++++ 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index e5d5a83a..27fbcba7 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -25,9 +25,6 @@ public: OctTree(const OctTree&& other) = delete; OctTree& operator= (const OctTree& other) = delete; - //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); @@ -36,10 +33,13 @@ public: void ClearObjects(); void ClearDynamicObjects(); + //Collision test function. WTODO: Probably remove or relocate elsewhere, Collision system? + void Update(float dt, World* world, Camera* cam); //Returns true if the ray collides with something in the tree. Result is written to [data]. bool RayCollides(const Ray& ray, Output& data) const; //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. bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const; private: @@ -47,11 +47,10 @@ private: //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; - + bool m_UpdatedOnce; unsigned int m_BoxID; glm::vec3 m_PrevPos; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 82c1b554..5fab6f04 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() @@ -131,16 +141,18 @@ 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; - return true; - } + for (const auto& obj : m_StaticObjects) { + if (Collision::AABBVsAABB(boxToTest, obj)) { + outBoxIntersected = obj; + return true; + } + } + for (const auto& obj : m_DynamicObjects) { + //If there is a collision and it is not testing against itself. + if (!isSameBoxProbably(boxToTest, obj) && + Collision::AABBVsAABB(boxToTest, obj)) { + outBoxIntersected = obj; + return true; } } } @@ -170,17 +182,18 @@ 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 (const auto& obj : m_StaticObjects) { + float dist; + if (Collision::RayVsAABB(ray, obj, dist)) { + minDist = std::min(dist, minDist); + intersected = true; + } + } + for (const auto& obj : m_DynamicObjects) { + float dist; + if (Collision::RayVsAABB(ray, obj, dist)) { + minDist = std::min(dist, minDist); + intersected = true; } } diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index 5392e63c..47036485 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -16,5 +16,25 @@ 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() From bc8a922b77f39259e4312c242a0325ceaa7a1787 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 9 Dec 2015 17:57:32 +0100 Subject: [PATCH 27/61] Merging octTreeTests a bit --- src/Tests/OctTreeTest.cpp | 18 +--- src/Tests/OctTreeTestGameClass.cpp | 101 ++++++++++++++++++---- src/Tests/OctTreeTestGameClass.h | 15 +++- src/Tests/OctTreeTestHardCodedTestWorld.h | 68 +++++++++------ 4 files changed, 142 insertions(+), 60 deletions(-) diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index 962db3d4..b0b5c25d 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -32,33 +32,17 @@ BOOST_AUTO_TEST_CASE(octTreeTest) //simple OctTree constructor check OctTree someOctTree(someAABB, 5); BOOST_CHECK(someOctTree.m_Children[0] != nullptr); - //TODO: a check so it split the tree properly - - - - - //advanced AddBox check - //add a boxcontainer - which crosses the mid-split - auto someAABB2 = AABB(glm::vec3(0.45f, 0.45f, 0.45f), glm::vec3(0.55f, 0.55f, 0.55f)); - someOctTree.AddDynamicObject(someAABB2); - //clear the boxcontainer - //need to check so it added the box properly - - someOctTree.ClearDynamicObjects(); - //add a boxcontainer - someOctTree.AddDynamicObject(someAABB2); - //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure } BOOST_AUTO_TEST_CASE(octTreeTest2) { + //octtree ritningen osv Game game(0, nullptr); while (game.Running()) { game.Tick(); } - } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp index 918782ec..e79f645b 100644 --- a/src/Tests/OctTreeTestGameClass.cpp +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -1,6 +1,6 @@ #include "OctTreeTestGameClass.h" -Game::Game(int argc, char* argv[]) +Game::Game(int argc, char* argv[]) : someOctTree(AABB(-0.5f*worldSize, 0.5f*worldSize), 2) { ResourceManager::RegisterType("ConfigFile"); ResourceManager::RegisterType("Model"); @@ -57,31 +57,100 @@ void Game::Tick() m_Renderer->Update(dt); m_EventBroker->Swap(); - //movement - minPos.x += 0.001f; - //frameCounter++; - //if (frameCounter > 50) { - // m_World->createTestEntities(AABB(minPos, glm::vec3(0.1f, 0.4f, 0.6f))); - // frameCounter = 0; - //} - //auto transf = m_World->GetComponent(m_World->OctTreeEntityIdSaved, "Transform"); - //((glm::vec3&)transf["Position"]).x += 0.001f; +#define TEST2 +#ifdef TEST1 + //add/move the trigger box + auto pos = m_Renderer->Camera()->Forward() + m_Renderer->Camera()->Position(); + AABB boxi; + boxi.CreateFromCenter(pos, maxPos - minPos); + frameCounter++; + if (frameCounter > 50) { + m_World->someOctTree.ClearDynamicObjects(); + m_World->someOctTree.AddDynamicObject(boxi); + frameCounter = 0; + } + ComponentWrapper transform = m_World->GetComponent(m_World->anotherBoxTransformId, "Transform"); + transform["Position"] = boxi.Center(); - //for (auto oneModel : m_World->allModels) - //{ + //check all children again in the tree if they have a box in them or not, and colormark them if they do + //contentboxarna får man ut - inte childboxarna! + std::vector boxIndex; + boxIndex = m_World->someOctTree.childIndicesContainingBox(boxi); - //} + for (auto& oneLinkedObject : m_World->linkOM) + { + ComponentWrapper model = m_World->GetComponent(oneLinkedObject.entId, "Model"); + model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + if (oneLinkedObject.child->m_DynamicObjects.size() != 0) { + model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + } + //next check if the childIndicesContainingBox method returns the correct boxes + //REQUIRED: childIndicesContainingBox must be public to test this! + for each (auto someBoxIndex in boxIndex) + { + glm::vec3 pos = m_World->someOctTree.m_Children[someBoxIndex]->m_Box.Center(); + if (abs(pos.x - oneLinkedObject.posxyz.x) < 0.005f && + abs(pos.y - oneLinkedObject.posxyz.y) < 0.005f && + abs(pos.z - oneLinkedObject.posxyz.z) < 0.005f) { + model["Color"] = glm::vec4(0.0f, 1.0f, 0.0f, 1.0f); + + } + } + } m_RenderQueueFactory->Update(m_World); //wireframe glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif +#ifdef TEST2 + + //only add 1 for now... + //grey box + AABB aabb; + aabb.CreateFromCenter(glm::vec3(0.f, 2.f, 0.f), glm::vec3(1.f, 1.f, 1.f)); + + 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); + + if (!m_UpdatedOnce) { + someOctTree.AddStaticObject(aabb); + m_BoxID = m_World->CreateEntity(); + ComponentWrapper transform = m_World->AttachComponent(m_BoxID, "Transform"); + transform["Scale"] = boxSize; + ComponentWrapper model = m_World->AttachComponent(m_BoxID, "Model"); + model["Resource"] = "Models/Core/UnitBox.obj"; + m_UpdatedOnce = true; + m_World->createTestEntitiesTest2(); + } + + //red box + AABB redBox; + auto boxPos = m_Renderer->Camera()->Position() + 1.2f*m_Renderer->Camera()->Forward(); + redBox.CreateFromCenter(boxPos, boxSize); + ComponentWrapper transform = m_World->GetComponent(m_BoxID, "Transform"); + transform["Position"] = boxPos; + ComponentWrapper model = m_World->GetComponent(m_BoxID, "Model"); + if (someOctTree.BoxCollides(redBox, AABB())) { + //if (Collision::AABBVsAABB(redBox, aabb)) { + m_Renderer->Camera()->SetPosition(m_PrevPos); + m_Renderer->Camera()->SetOrientation(m_PrevOri); + model["Color"] = greenCol; + } + else { + model["Color"] = redCol; + } + + m_PrevPos = m_Renderer->Camera()->Position(); + m_PrevOri = m_Renderer->Camera()->Orientation(); + someOctTree.ClearObjects(); + + m_RenderQueueFactory->Update(m_World); +#endif m_Renderer->Draw(m_RenderQueueFactory->RenderQueues()); - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - - m_EventBroker->Swap(); m_EventBroker->Clear(); diff --git a/src/Tests/OctTreeTestGameClass.h b/src/Tests/OctTreeTestGameClass.h index 887ad59b..8ca34510 100644 --- a/src/Tests/OctTreeTestGameClass.h +++ b/src/Tests/OctTreeTestGameClass.h @@ -11,6 +11,7 @@ #include "Rendering/RenderQueueFactory.h" #include "OctTreeTestHardCodedTestWorld.h" +#include "Core\Collision.h" class Game { @@ -31,8 +32,20 @@ private: HardcodedTestWorld* m_World; RenderQueueFactory* m_RenderQueueFactory; + //Test1 int frameCounter = 0; - glm::vec3 minPos = glm::vec3(-0.2f, 0.2f, 0.3f); + glm::vec3 minPos = glm::vec3(0.1f, 0.1f, 0.1f); + glm::vec3 maxPos = glm::vec3(0.2f, 0.2f, 0.2f); + + //Test2 + bool m_UpdatedOnce = false; + unsigned int m_BoxID; + glm::vec3 m_PrevPos; + glm::quat m_PrevOri; + + glm::vec3 worldSize = glm::vec3(50, 50, 50); + OctTree someOctTree; + }; #endif diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index dbdbcb45..ff7b56cc 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -5,9 +5,6 @@ #include "Core/World.h" #include "Core/Util/Any.h" -//octTree -//#include - #include //last! #define private public @@ -18,18 +15,26 @@ class HardcodedTestWorld : public World public: struct LinkOctTreeAndModel { EntityID entId; + OctTree* child; + glm::vec3 posxyz; + LinkOctTreeAndModel(EntityID eId, OctTree* ch, glm::vec3 pos) + { + entId = eId; + child = ch; + posxyz = pos; + } }; - EntityID OctTreeEntityIdSaved; - //std::vector allModels; - //ComponentWrapper moveModel; - //OctTree* someOctTreePointer; + EntityID anotherBoxTransformId; + std::vector linkOM; + OctTree someOctTree; //constructor HardcodedTestWorld() : World() + , someOctTree(AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)), 2) { registerTestComponents(); - createTestEntities(AABB(glm::vec3(-0.2f, 0.2f, 0.3f), glm::vec3(0.1f, 0.4f, 0.6f))); + //createTestEntities(); } private: @@ -61,58 +66,69 @@ private: RegisterComponent(f); } - void createTestEntities(AABB anotherBox) + void createTestEntities() { World& world = *this; - + EntityID tempId; //add octTree { + //copy of mainbox auto someAABB = AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)); - OctTree someOctTree(someAABB, 2); - //add a box - //auto anotherBox = AABB(glm::vec3(-0.2f, 0.2f, 0.3f), glm::vec3(0.1f, 0.4f, 0.6f)); + + //draw main box first + AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, &someOctTree, tempId); + + //add anotherbox in octTree + auto anotherBox = AABB(glm::vec3(0.1f, 0.1f, 0.1f), glm::vec3(0.2f, 0.2f, 0.2f)); //note: have to delete the box in the tree first, since were trying to move the box - someOctTree.ClearDynamicObjects(); someOctTree.AddDynamicObject(anotherBox); - //the main box first - AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, someOctTree.m_DynamicObjects.size()); - - //draw anotherbox - AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, 0); + //draw anotherbox and save it in anotherBoxTransformId + AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, &someOctTree, anotherBoxTransformId); //draw the octTree for (size_t j = 0; j < 8; j++) { AddBoxModel(someOctTree.m_Children[j]->m_Box.Center(), - someOctTree.m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Children[j]->m_DynamicObjects.size()); + someOctTree.m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Children[j], tempId); auto someChild = someOctTree.m_Children[j]; for (size_t i = 0; i < 8; i++) { AddBoxModel(someChild->m_Children[i]->m_Box.Center(), - someChild->m_Children[i]->m_Box.HalfSize().x, someChild->m_Children[i]->m_DynamicObjects.size()); + someChild->m_Children[i]->m_Box.HalfSize().x, someChild->m_Children[i], tempId); } } } }//end CreateEnt - void AddBoxModel(const glm::vec3 ¢er, const float &halfSize, const int &contBoxes) { + void AddBoxModel(const glm::vec3 ¢er, const float &halfSize, OctTree* child, EntityID &outEntityId) { World& world = *this; EntityID entityDummyScene = world.CreateEntity(); - + outEntityId = entityDummyScene; ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); transform["Position"] = center; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSize*2.0f; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSize*2.0f*0.97f; ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox2.obj"; + model["Resource"] = "Models/Core/UnitBox.obj"; model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - if (contBoxes != 0) + if (child->m_DynamicObjects.size() != 0) model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + linkOM.emplace_back(entityDummyScene, child, center); //extra //allModels.push_back(model); } + void createTestEntitiesTest2() + { + World& world = *this; + + 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"; + } }; \ No newline at end of file From c60e96883662d244d99b894f718cd9b6b6f6d9ab Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Thu, 10 Dec 2015 11:03:40 +0100 Subject: [PATCH 28/61] Removed a boxclear in the end of the test2 method --- include/Engine/Core/OctTree.h | 1 + src/Engine/Core/OctTree.cpp | 4 ++-- src/Tests/OctTreeTestGameClass.cpp | 24 ++++++++++++++++++----- src/Tests/OctTreeTestHardCodedTestWorld.h | 2 +- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index e5d5a83a..7379b202 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -58,6 +58,7 @@ private: glm::quat m_PrevOri; inline bool hasChildren() const; +public: int childIndexContainingPoint(const glm::vec3& point) const; std::vector childIndicesContainingBox(const AABB& box) const; }; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 82c1b554..582e3acd 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -109,8 +109,8 @@ 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)) { + if (BoxCollides(box, AABB())) { + //if (Collision::AABBVsAABB(box, aabb)) { cam->SetPosition(m_PrevPos); cam->SetOrientation(m_PrevOri); model["Color"] = greenCol; diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp index e79f645b..6b2e23bc 100644 --- a/src/Tests/OctTreeTestGameClass.cpp +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -58,7 +58,13 @@ void Game::Tick() m_EventBroker->Swap(); #define TEST2 + //this draws the octTree and you can set the cube inside it and see what boxes in the tree that it belongs to #ifdef TEST1 + if (!m_UpdatedOnce) { + m_UpdatedOnce = true; + m_World->createTestEntitiesTest1(); + } + //add/move the trigger box auto pos = m_Renderer->Camera()->Forward() + m_Renderer->Camera()->Position(); AABB boxi; @@ -103,25 +109,32 @@ void Game::Tick() //wireframe glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); #endif + //this tests AABB vs AABB collision and AABB vs OctTree with AABB in it #ifdef TEST2 //only add 1 for now... //grey box - AABB aabb; - aabb.CreateFromCenter(glm::vec3(0.f, 2.f, 0.f), glm::vec3(1.f, 1.f, 1.f)); 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); + AABB aabb; + aabb.CreateFromCenter(glm::vec3(0, 2.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)); + if (m_UpdatedOnce) { + auto test = someOctTree.childIndicesContainingBox(aabb); + std::vector test2; + someOctTree.BoxesInSameRegion(aabb, test2); + } if (!m_UpdatedOnce) { + m_UpdatedOnce = true; someOctTree.AddStaticObject(aabb); + //create the "small red box" m_BoxID = m_World->CreateEntity(); ComponentWrapper transform = m_World->AttachComponent(m_BoxID, "Transform"); transform["Scale"] = boxSize; ComponentWrapper model = m_World->AttachComponent(m_BoxID, "Model"); model["Resource"] = "Models/Core/UnitBox.obj"; - m_UpdatedOnce = true; m_World->createTestEntitiesTest2(); } @@ -132,8 +145,10 @@ void Game::Tick() ComponentWrapper transform = m_World->GetComponent(m_BoxID, "Transform"); transform["Position"] = boxPos; ComponentWrapper model = m_World->GetComponent(m_BoxID, "Model"); + //this checks AABB vs an AABB in the octTree if (someOctTree.BoxCollides(redBox, AABB())) { - //if (Collision::AABBVsAABB(redBox, aabb)) { + //this checks AABB vs AABB + //if (Collision::AABBVsAABB(redBox, aabb)) { m_Renderer->Camera()->SetPosition(m_PrevPos); m_Renderer->Camera()->SetOrientation(m_PrevOri); model["Color"] = greenCol; @@ -144,7 +159,6 @@ void Game::Tick() m_PrevPos = m_Renderer->Camera()->Position(); m_PrevOri = m_Renderer->Camera()->Orientation(); - someOctTree.ClearObjects(); m_RenderQueueFactory->Update(m_World); #endif diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index ff7b56cc..a0762cce 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -66,7 +66,7 @@ private: RegisterComponent(f); } - void createTestEntities() + void createTestEntitiesTest1() { World& world = *this; EntityID tempId; From 0d8f7acb7e229507e5c924c85eb59a8978232f6f Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 10 Dec 2015 16:04:55 +0100 Subject: [PATCH 29/61] OctTree doesn't check the same object multiple times if it exists in multiple places in the tree. --- include/Engine/Core/OctTree.h | 80 +++++++++--- include/Game/HardcodedTestWorld.h | 5 +- src/Engine/Core/OctTree.cpp | 201 +++++++++++++++++++++++------- 3 files changed, 222 insertions(+), 64 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 27fbcba7..2a3b87b2 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -13,7 +13,21 @@ public: struct Output { float CollideDistance; - }; + }; + struct ContainedObject + { + ContainedObject() + : Box(AABB()) + , Checked(false) + {} + ContainedObject(AABB box) + : Box(box) + , Checked(false) + {} + AABB Box; + bool Checked; + }; + OctTree(); ~OctTree(); //For the root OctTree, [octTreeBounds] should be a box containing the entire level. @@ -24,41 +38,71 @@ 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); - - void BoxesInSameRegion(const AABB& box, std::vector& outBoxes) const; - + //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); //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]. //Note: More efficient than calling BoxesInSameRegion from outside and testing there. - bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const; + 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. - std::vector m_StaticObjects; - std::vector m_DynamicObjects; - AABB m_Box; - + struct OctChild; + 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; - 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/Game/HardcodedTestWorld.h b/include/Game/HardcodedTestWorld.h index 5f7cc5f6..dd132971 100644 --- a/include/Game/HardcodedTestWorld.h +++ b/include/Game/HardcodedTestWorld.h @@ -108,12 +108,15 @@ private: ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); model["Resource"] = "Models/DummyScene.obj"; } + for (int i = 0; i < 16; ++i) { EntityID entityCollisionBox = world.CreateEntity(); ComponentWrapper transform = world.AttachComponent(entityCollisionBox, "Transform"); - transform["Position"] = glm::vec3(0.f, 2.f, 0.f); + transform["Position"] = glm::vec3(rand()%11-5, rand() % 11 - 5, rand() % 11 - 5); ComponentWrapper model = world.AttachComponent(entityCollisionBox, "Model"); model["Resource"] = "Models/Core/UnitBox.obj"; + float sc = 0.20f; + transform["Scale"] = glm::vec3(sc, sc, sc); ComponentWrapper collision = world.AttachComponent(entityCollisionBox, "Collision"); glm::vec3 pos = transform["Position"]; diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 5fab6f04..63eba63e 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -38,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 { @@ -78,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; @@ -95,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; @@ -119,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)) { @@ -141,25 +215,32 @@ bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const return true; } } else { - for (const auto& obj : m_StaticObjects) { - if (Collision::AABBVsAABB(boxToTest, obj)) { - outBoxIntersected = obj; - return true; + 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 (const auto& obj : m_DynamicObjects) { - //If there is a collision and it is not testing against itself. - if (!isSameBoxProbably(boxToTest, obj) && - Collision::AABBVsAABB(boxToTest, obj)) { - outBoxIntersected = obj; - return 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)) { @@ -182,19 +263,25 @@ bool OctTree::RayCollides(const Ray& ray, Output& data) const //Check against boxes in the node. float minDist = INFINITY; bool intersected = false; - for (const auto& obj : m_StaticObjects) { + for (int i : m_StaticObjIndices) { float dist; - if (Collision::RayVsAABB(ray, obj, 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 (const auto& obj : m_DynamicObjects) { + for (int i : m_DynamicObjIndices) { float dist; - if (Collision::RayVsAABB(ray, obj, 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; @@ -205,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(); } } @@ -274,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()); @@ -318,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 From 37f9ab232223a9aefa31c720d07f0dfd07740246 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 10 Dec 2015 17:53:33 +0100 Subject: [PATCH 30/61] Quick fix for unit test compilation. --- src/Tests/WorldTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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++; } From 4195baa5dca6a31eca4d83ce7df6a9cfc7eba022 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 10 Dec 2015 17:57:09 +0100 Subject: [PATCH 31/61] ContainedObject in OctTree made private. --- include/Engine/Core/OctTree.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index 2a3b87b2..a3715480 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -14,19 +14,6 @@ public: { float CollideDistance; }; - struct ContainedObject - { - ContainedObject() - : Box(AABB()) - , Checked(false) - {} - ContainedObject(AABB box) - : Box(box) - , Checked(false) - {} - AABB Box; - bool Checked; - }; OctTree(); ~OctTree(); @@ -59,7 +46,20 @@ public: bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected); private: - struct OctChild; + 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; From 350038057d5273b16f016fc4df7a684516a87124 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Fri, 11 Dec 2015 13:51:07 +0100 Subject: [PATCH 32/61] Misc OctTreeTest changes --- src/Engine/Core/OctTree.cpp | 2 +- src/Tests/OctTreeTest.cpp | 4 +- src/Tests/OctTreeTestGameClass.cpp | 10 ++--- src/Tests/OctTreeTestHardCodedTestWorld.h | 49 +++++++++++------------ 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 63eba63e..aae9762a 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -163,7 +163,7 @@ OctTree::OctChild::~OctChild() void OctTree::Update(float dt, World* world, Camera* cam) { - for (ComponentWrapper& c : world->GetComponents("Collision")) { + for (ComponentWrapper& c : *world->GetComponents("Collision")) { AABB aabb; aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]); AddDynamicObject(aabb); diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index 23a09fcd..71d41cad 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -30,8 +30,8 @@ BOOST_AUTO_TEST_CASE(octTreeTest) BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner)); //simple OctTree constructor check - OctTree someOctTree(someAABB, 5); - BOOST_CHECK(someOctTree.m_Children[0] != nullptr); + //OctTree someOctTree(someAABB, 5); + //BOOST_CHECK(someOctTree.m_Children[0] != nullptr); //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure } diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp index 6b2e23bc..735d3ee8 100644 --- a/src/Tests/OctTreeTestGameClass.cpp +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -57,7 +57,7 @@ void Game::Tick() m_Renderer->Update(dt); m_EventBroker->Swap(); -#define TEST2 +#define TEST1 //this draws the octTree and you can set the cube inside it and see what boxes in the tree that it belongs to #ifdef TEST1 if (!m_UpdatedOnce) { @@ -81,13 +81,13 @@ void Game::Tick() //check all children again in the tree if they have a box in them or not, and colormark them if they do //contentboxarna får man ut - inte childboxarna! std::vector boxIndex; - boxIndex = m_World->someOctTree.childIndicesContainingBox(boxi); + boxIndex = m_World->someOctTree.m_Root->childIndicesContainingBox(boxi); for (auto& oneLinkedObject : m_World->linkOM) { ComponentWrapper model = m_World->GetComponent(oneLinkedObject.entId, "Model"); model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - if (oneLinkedObject.child->m_DynamicObjects.size() != 0) { + if (oneLinkedObject.child->m_DynamicObjIndices.size() != 0) { model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); } @@ -95,7 +95,7 @@ void Game::Tick() //REQUIRED: childIndicesContainingBox must be public to test this! for each (auto someBoxIndex in boxIndex) { - glm::vec3 pos = m_World->someOctTree.m_Children[someBoxIndex]->m_Box.Center(); + glm::vec3 pos = m_World->someOctTree.m_Root->m_Children[someBoxIndex]->m_Box.Center(); if (abs(pos.x - oneLinkedObject.posxyz.x) < 0.005f && abs(pos.y - oneLinkedObject.posxyz.y) < 0.005f && abs(pos.z - oneLinkedObject.posxyz.z) < 0.005f) { @@ -122,7 +122,7 @@ void Game::Tick() aabb.CreateFromCenter(glm::vec3(0, 2.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)); if (m_UpdatedOnce) { - auto test = someOctTree.childIndicesContainingBox(aabb); + //auto test = someOctTree.childIndicesContainingBox(aabb); std::vector test2; someOctTree.BoxesInSameRegion(aabb, test2); } diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index a0762cce..46b1e00b 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -15,9 +15,9 @@ class HardcodedTestWorld : public World public: struct LinkOctTreeAndModel { EntityID entId; - OctTree* child; + OctTree::OctChild* child; glm::vec3 posxyz; - LinkOctTreeAndModel(EntityID eId, OctTree* ch, glm::vec3 pos) + LinkOctTreeAndModel(EntityID eId, OctTree::OctChild* ch, glm::vec3 pos) { entId = eId; child = ch; @@ -76,7 +76,7 @@ private: auto someAABB = AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)); //draw main box first - AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, &someOctTree, tempId); + AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, someOctTree.m_Root, tempId); //add anotherbox in octTree auto anotherBox = AABB(glm::vec3(0.1f, 0.1f, 0.1f), glm::vec3(0.2f, 0.2f, 0.2f)); @@ -84,15 +84,15 @@ private: someOctTree.AddDynamicObject(anotherBox); //draw anotherbox and save it in anotherBoxTransformId - AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, &someOctTree, anotherBoxTransformId); + AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, someOctTree.m_Root, anotherBoxTransformId); //draw the octTree for (size_t j = 0; j < 8; j++) { - AddBoxModel(someOctTree.m_Children[j]->m_Box.Center(), - someOctTree.m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Children[j], tempId); + AddBoxModel(someOctTree.m_Root->m_Children[j]->m_Box.Center(), + someOctTree.m_Root->m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Root->m_Children[j], tempId); - auto someChild = someOctTree.m_Children[j]; + auto someChild = someOctTree.m_Root->m_Children[j]; for (size_t i = 0; i < 8; i++) { @@ -103,24 +103,6 @@ private: } }//end CreateEnt - void AddBoxModel(const glm::vec3 ¢er, const float &halfSize, OctTree* child, EntityID &outEntityId) { - World& world = *this; - - EntityID entityDummyScene = world.CreateEntity(); - outEntityId = entityDummyScene; - ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); - transform["Position"] = center; - transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSize*2.0f*0.97f; - ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); - model["Resource"] = "Models/Core/UnitBox.obj"; - model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - if (child->m_DynamicObjects.size() != 0) - model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - - linkOM.emplace_back(entityDummyScene, child, center); - //extra - //allModels.push_back(model); - } void createTestEntitiesTest2() { World& world = *this; @@ -131,4 +113,21 @@ private: ComponentWrapper model = world.AttachComponent(entityCollisionBox, "Model"); model["Resource"] = "Models/Core/UnitBox.obj"; } + + void AddBoxModel(const glm::vec3 ¢er, const float &halfSize, OctTree::OctChild* child, EntityID &outEntityId) { + World& world = *this; + + EntityID entityDummyScene = world.CreateEntity(); + outEntityId = entityDummyScene; + ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform"); + transform["Position"] = center; + transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSize*2.0f*0.97f; + ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model"); + model["Resource"] = "Models/Core/UnitBox.obj"; + model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + if (child->m_DynamicObjIndices.size() != 0) + model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + + linkOM.emplace_back(entityDummyScene, child, center); + } }; \ No newline at end of file From 81034420a9cb9502bc24cc06f95f0ff2f69e940e Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 11 Dec 2015 16:04:30 +0100 Subject: [PATCH 33/61] Put back old version of OctTree for testing, made unit tests. Tests shows the old version checks for collisions etc. faster, new version adds objects into tree faster, and doesn't give duplicated results. --- src/Engine/Core/OctTree.cpp | 2 +- src/Tests/OctTreeTest.cpp | 135 ++++++++++++++- src/Tests/OldOctTree.cpp | 328 ++++++++++++++++++++++++++++++++++++ src/Tests/OldOctTree.h | 69 ++++++++ 4 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 src/Tests/OldOctTree.cpp create mode 100644 src/Tests/OldOctTree.h diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 63eba63e..aae9762a 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -163,7 +163,7 @@ OctTree::OctChild::~OctChild() void OctTree::Update(float dt, World* world, Camera* cam) { - for (ComponentWrapper& c : world->GetComponents("Collision")) { + for (ComponentWrapper& c : *world->GetComponents("Collision")) { AABB aabb; aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]); AddDynamicObject(aabb); diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index 47036485..d9ba2e86 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -2,7 +2,9 @@ using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include //srand -#include +#include "Engine/Core/OctTree.h" +#include "Engine/Core/Ray.h" +#include "OldOctTree.h" BOOST_AUTO_TEST_SUITE(octTreeTests) @@ -36,5 +38,136 @@ BOOST_AUTO_TEST_CASE(octSameRegionTest) BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().z, firstQuadrant.HalfSize().z, 0.00001f); } +const int LEVEL_BOUNDS = 500; +const int MAXSIZE = 50; +const int BOXES = 400; +const int NUM_DYNAMICS = 0; +const int NUM_STATICS = BOXES - NUM_DYNAMICS; +const int SEED = 6548; +const int TEST_FRAMES = 1; //300 +const int NUM_FUNCTION_LOOPS = 1; //25 +const int TESTS = 1; //10 + +template +void RegionTest(Tree& tree) +{ + AABB aabb; + aabb.CreateFromCenter(glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS), + glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE)); + std::vector outVec; + tree.BoxesInSameRegion(aabb, outVec); +} + +template +void RayTest(Tree& tree) +{ + Tree::Output data; + glm::vec3 rayStart = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS); + glm::vec3 rayEnd = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS); + tree.RayCollides({ rayStart , glm::normalize(rayEnd - rayStart) }, data); +} + +template +void BoxTest(Tree& tree) +{ + AABB outBox; + AABB aabb; + aabb.CreateFromCenter(glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS), + glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE)); + tree.BoxCollides(aabb, outBox); +} + +template +void NopTest(Tree& tree) +{ + +} + +template +void TestLoop(TestFunction xTest) +{ + srand(SEED); + glm::vec3 mini = glm::vec3(0, 0, 0); + glm::vec3 maxi = glm::vec3(LEVEL_BOUNDS, LEVEL_BOUNDS, LEVEL_BOUNDS); + Tree tree(AABB(mini, maxi), 3); + AABB aabb; + glm::vec3 center; + glm::vec3 size; + for (int t = 0; t < TESTS; ++t) { + for (int i = 0; i < NUM_STATICS; ++i) { + center = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS); + size = glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE); + aabb.CreateFromCenter(center, size); + tree.AddStaticObject(aabb); + } + for (int fr = 0; fr < TEST_FRAMES; ++fr) { + for (int i = 0; i < NUM_DYNAMICS; ++i) { + center = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS); + size = glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE); + aabb.CreateFromCenter(center, size); + tree.AddDynamicObject(aabb); + } + + for (int fl = 0; fl < NUM_FUNCTION_LOOPS; ++fl) { + xTest(tree); + } + + tree.ClearDynamicObjects(); + } + tree.ClearObjects(); + } + int asda = 0; + asda = 123; + BOOST_CHECK(asda == 123); +} + +BOOST_AUTO_TEST_CASE(octRegionPerfTestWithDuplicates) +{ + TestLoop(RegionTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octRegionPerfTestNoDuplicates) +{ + TestLoop(RegionTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octBoxPerfTestWithDuplicates) +{ + TestLoop(BoxTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octBoxPerfTestNoDuplicates) +{ + TestLoop(BoxTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octRayPerfTestWithDuplicates) +{ + TestLoop(RayTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octRayPerfTestNoDuplicates) +{ + TestLoop(RayTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octNopPerfTestWithDuplicates) +{ + TestLoop(NopTest); + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_CASE(octNopPerfTestNoDuplicates) +{ + TestLoop(NopTest); + BOOST_CHECK(true); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/Tests/OldOctTree.cpp b/src/Tests/OldOctTree.cpp new file mode 100644 index 00000000..72decd4d --- /dev/null +++ b/src/Tests/OldOctTree.cpp @@ -0,0 +1,328 @@ +#include +#include +#include + +#include "OldOctTree.h" +#include "Core/Collision.h" +#include "Core/World.h" +#include "Rendering/Camera.h" + +namespace Old +{ + +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; +} + +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() + : OctTree(AABB(), 0) +{} + +OctTree::OctTree(const AABB& octTreeBounds, int subDivisions) + : m_Box(octTreeBounds) + , m_UpdatedOnce(false) +{ + 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.Center(); + 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; + } + } +} + +void OctTree::Update(float dt, World* world, Camera* cam) +{ + AABB aabb; + for (ComponentWrapper& c : *world->GetComponents("Collision")) { + aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]); + AddStaticObject(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); + + 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; + } + + AABB box; + auto boxPos = cam->Position() + 1.2f*cam->Forward(); + box.CreateFromCenter(boxPos, boxSize); + 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)) { + cam->SetPosition(m_PrevPos); + cam->SetOrientation(m_PrevOri); + model["Color"] = greenCol; + } else { + model["Color"] = redCol; + } + + m_PrevPos = cam->Position(); + m_PrevOri = cam->Orientation(); + ClearObjects(); +} + +bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const +{ + if (hasChildren()) { + for (int i : childIndicesContainingBox(boxToTest)) { + if (m_Children[i]->BoxCollides(boxToTest, outBoxIntersected)) + return true; + } + } else { + for (const auto& obj : m_StaticObjects) { + if (Collision::AABBVsAABB(boxToTest, obj)) { + outBoxIntersected = obj; + return true; + } + } + for (const auto& obj : m_DynamicObjects) { + //If there is a collision and it is not testing against itself. + if (!isSameBoxProbably(boxToTest, obj) && + Collision::AABBVsAABB(boxToTest, obj)) { + outBoxIntersected = obj; + return true; + } + } + } + return false; +} + +bool OctTree::RayCollides(const Ray& ray, Output& data) const +{ + //If the node AABB is missed, everything it contains is missed. + if (Collision::RayAABBIntr(ray, m_Box)) { + //If the ray shoots the tree, and it is a parent to 8 children :o + if (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, 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 (m_Children[info.Index]->RayCollides(ray, data)) { + return true; + } + } + } else { + //Check against boxes in the node. + float minDist = INFINITY; + bool intersected = false; + for (const auto& obj : m_StaticObjects) { + float dist; + if (Collision::RayVsAABB(ray, obj, dist)) { + minDist = std::min(dist, minDist); + intersected = true; + } + } + for (const auto& obj : m_DynamicObjects) { + float dist; + if (Collision::RayVsAABB(ray, obj, dist)) { + minDist = std::min(dist, minDist); + intersected = true; + } + } + + data.CollideDistance = minDist; + return intersected; + } + } + return false; +} + + +void OctTree::AddDynamicObject(const AABB& box) +{ + if (hasChildren()) { + for (auto i : childIndicesContainingBox(box)) { + m_Children[i]->AddDynamicObject(box); + } + } else { + m_DynamicObjects.push_back(box); + } +} + +void OctTree::AddStaticObject(const AABB& box) +{ + if (hasChildren()) { + for (auto i : childIndicesContainingBox(box)) { + m_Children[i]->AddStaticObject(box); + } + } else { + m_StaticObjects.push_back(box); + } +} + +void OctTree::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()); + } +} + +void OctTree::ClearObjects() +{ + if (hasChildren()) { + for (OctTree*& c : m_Children) { + c->ClearObjects(); + } + } else { + m_DynamicObjects.clear(); + m_StaticObjects.clear(); + } +} + +void OctTree::ClearDynamicObjects() +{ + if (hasChildren()) { + for (OctTree*& c : m_Children) { + c->ClearObjects(); + } + } else { + m_DynamicObjects.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); +} + +std::vector OctTree::childIndicesContainingBox(const AABB& box) const +{ + int minInd = childIndexContainingPoint(box.MinCorner()); + int maxInd = childIndexContainingPoint(box.MaxCorner()); + //Because of the predictable ordering of the child indices, + //the number of bits set when xor:ing the indices will determine the number of children containing the box. + std::bitset<3> bits(minInd ^ maxInd); + switch (bits.count()) { + //Box contained completely in one child. + case 0: + return{ minInd }; + //Two children. + case 1: + return{ minInd, maxInd }; + //Four children. + case 2: + { + std::vector ret; + //Bit-hax to calculate the correct 4 children containing the box. + //This works because of the childrens index determine what part of + //the dimensions they are responsible for (which octant). + bits.flip(); + //At this point the bits necessarily have exactly one bit set. + for (int c = 0; c < 8; ++c) { + //If the child index have the same bit set as the bits, add box to it. + if (bits.to_ulong() & c) { + ret.push_back(c); + } + } + return ret; + } + case 3: //Eight children. + return{ 0,1,2,3,4,5,6,7 }; + default: + return std::vector(); + } +} + +inline bool OctTree::hasChildren() const +{ + return m_Children[0] != nullptr; +} +} \ No newline at end of file diff --git a/src/Tests/OldOctTree.h b/src/Tests/OldOctTree.h new file mode 100644 index 00000000..2e37b739 --- /dev/null +++ b/src/Tests/OldOctTree.h @@ -0,0 +1,69 @@ +#ifndef OldOctTree_h__ +#define OldOctTree_h__ + +#include "Core/AABB.h" + +struct Ray; +class World; +class Camera; + +namespace Old +{ + +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); + + //We should only ever need one OctTree in the game, and it should not need to be copied. + //Define these if the OctTree suddenly needs to be copied, think of the children OctTree* ptrs. + OctTree(const OctTree& other) = delete; + OctTree(const OctTree&& other) = delete; + OctTree& operator= (const OctTree& 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(); + + //Collision test function. WTODO: Probably remove or relocate elsewhere, Collision system? + void Update(float dt, World* world, Camera* cam); + //Returns true if the ray collides with something in the tree. Result is written to [data]. + bool RayCollides(const Ray& ray, Output& data) const; + //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. + bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const; + +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. + std::vector m_StaticObjects; + std::vector m_DynamicObjects; + AABB m_Box; + + bool m_UpdatedOnce; + unsigned int m_BoxID; + glm::vec3 m_PrevPos; + glm::quat m_PrevOri; + + 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 From 9a7d77f29d400a405c885ab1ce16183f97ee6a56 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 11 Dec 2015 16:44:49 +0100 Subject: [PATCH 34/61] Removed notes in OldOctTree. --- src/Tests/OldOctTree.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Tests/OldOctTree.h b/src/Tests/OldOctTree.h index 2e37b739..316b9545 100644 --- a/src/Tests/OldOctTree.h +++ b/src/Tests/OldOctTree.h @@ -36,7 +36,7 @@ public: void ClearObjects(); void ClearDynamicObjects(); - //Collision test function. WTODO: Probably remove or relocate elsewhere, Collision system? + //Collision test function. void Update(float dt, World* world, Camera* cam); //Returns true if the ray collides with something in the tree. Result is written to [data]. bool RayCollides(const Ray& ray, Output& data) const; @@ -47,9 +47,6 @@ public: 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. std::vector m_StaticObjects; std::vector m_DynamicObjects; AABB m_Box; From 55aad493ff5d009740e0d2013028b3b805ac4630 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 11 Dec 2015 16:45:38 +0100 Subject: [PATCH 35/61] Fixed warnings in OctTree. Cleaned unit tests. --- src/Engine/Core/OctTree.cpp | 6 +++--- src/Tests/OctTreeTest.cpp | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index aae9762a..e41a1692 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -300,7 +300,7 @@ void OctTree::OctChild::AddDynamicObject(const AABB& box) } } else { //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()); + m_DynamicObjIndices.push_back((int)m_DynamicObjectsRef.size()); } } @@ -312,7 +312,7 @@ void OctTree::OctChild::AddStaticObject(const AABB& box) } } else { //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()); + m_StaticObjIndices.push_back((int)m_StaticObjectsRef.size()); } } @@ -323,7 +323,7 @@ void OctTree::OctChild::BoxesInSameRegion(const AABB& box, std::vector& ou m_Children[i]->BoxesInSameRegion(box, outBoxes); } } else { - int startIndex = outBoxes.size(); + 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){ diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index d9ba2e86..efd2d109 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -44,9 +44,9 @@ const int BOXES = 400; const int NUM_DYNAMICS = 0; const int NUM_STATICS = BOXES - NUM_DYNAMICS; const int SEED = 6548; -const int TEST_FRAMES = 1; //300 -const int NUM_FUNCTION_LOOPS = 1; //25 -const int TESTS = 1; //10 +const int TEST_FRAMES = 300; +const int NUM_FUNCTION_LOOPS = 25; +const int TESTS = 0; //10 template void RegionTest(Tree& tree) @@ -116,9 +116,6 @@ void TestLoop(TestFunction xTest) } tree.ClearObjects(); } - int asda = 0; - asda = 123; - BOOST_CHECK(asda == 123); } BOOST_AUTO_TEST_CASE(octRegionPerfTestWithDuplicates) From 11e37ca8981258dd6ae4eee58f738f6ebe06f63a Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 14 Dec 2015 10:42:45 +0100 Subject: [PATCH 36/61] Added functions to test ray vs triangle- and model-intersections. --- include/Engine/Core/Collision.h | 28 +++++++++++- src/Engine/Core/Collision.cpp | 77 ++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/include/Engine/Core/Collision.h b/include/Engine/Core/Collision.h index b23b33cc..87f521bd 100644 --- a/include/Engine/Core/Collision.h +++ b/include/Engine/Core/Collision.h @@ -1,17 +1,43 @@ #ifndef Collision_h__ #define Collision_h__ +#include + #include "Core/Ray.h" #include "Core/AABB.h" +#include "Engine/Rendering/RawModel.h" namespace Collision { - +//Return true if the ray hits the box. bool RayAABBIntr(const Ray& ray, const AABB& box); bool RayVsAABB(const Ray& ray, const AABB& box); +//Return true if the ray hits the box, also outputs distance from ray origin to intersection point in [outDistance]. bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance); +//Return true if the ray hits any of the triangles in the model. Stops checking when a hit is detected. +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices); +//Return true if the ray hits any of the triangles in the model. +//Also returns the position of the intersection point. Will loop through all the whole model indices. +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + glm::vec3& outHitPosition); +//Return true if the ray hits any of the triangles in the model. +//Also returns the distance from the ray origin to the closest +//intersection point, and the barycentric u,v-coordinates. Will loop through all the whole model indices. +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + float& outDistance, + float& outUCoord, + float& outVCoord); + +//Return true if the boxes are intersecting. bool AABBVsAABB(const AABB& a, const AABB& b); + } #endif \ No newline at end of file diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index 07e37085..dee567e0 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -1,6 +1,7 @@ +#include + #include "Core/Collision.h" #include "Engine/GLM.h" -#include namespace Collision { @@ -74,4 +75,76 @@ bool AABBVsAABB(const AABB& a, const AABB& b) return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); } -} \ No newline at end of file +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices) +{ + for (int i = 0; i < modelIndices.size(); ++i) { + glm::vec3 v0 = modelVertices[modelIndices[i]].Position; + glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 + glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 + glm::vec3 m = ray.Origin - v0; + glm::vec3 MxE1 = glm::cross(m, e1); + glm::vec3 DxE2 = glm::cross(ray.Direction, e2); + float DetInv = 1.0f / glm::dot(e1, DxE2); + float u = glm::dot(m, DxE2) * DetInv; + float v = glm::dot(ray.Direction, MxE1) * DetInv; + if (u < 0 && v < 0 && 1 < u + v) { + continue; + } + //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. + if (0 <= glm::dot(e2, MxE1) * DetInv) { + return true; + } + } + return false; +} + +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + float& outDistance, + float& outUCoord, + float& outVCoord) +{ + outDistance = INFINITY; + bool hit = false; + for (int i = 0; i < modelIndices.size(); ++i) { + glm::vec3 v0 = modelVertices[modelIndices[i]].Position; + glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 + glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 + glm::vec3 m = ray.Origin - v0; + glm::vec3 MxE1 = glm::cross(m, e1); + glm::vec3 DxE2 = glm::cross(ray.Direction, e2); + float DetInv = 1.0f / glm::dot(e1, DxE2); + float dist = glm::dot(e2, MxE1) * DetInv; + if (dist >= outDistance) { + continue; + } + float u = glm::dot(m, DxE2) * DetInv; + float v = glm::dot(ray.Direction, MxE1) * DetInv; + //If u and v are positive, u+v <= 1, dist is positive, and less than closest. + if (0 <= u && 0 <= v && u + v <= 1 && 0 <= dist) { + outDistance = dist; + outUCoord = u; + outVCoord = v; + hit = true; + } + } + return hit; +} + +bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + glm::vec3& outHitPosition) +{ + float u; + float v; + float dist; + bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); + outHitPosition = ray.Origin + dist * ray.Direction; + return hit; +} + +} From 5fce7490a9d547e37d673d78f1c714ea5e6dc718 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Mon, 14 Dec 2015 11:57:18 +0100 Subject: [PATCH 37/61] MergeFix OctTree williambranch and andersbranch --- src/Tests/OctTreeTest.cpp | 45 ++----------------- src/Tests/OctTreeTestAnders.cpp | 53 +++++++++++++++++++++++ src/Tests/OctTreeTestGameClass.h | 1 + src/Tests/OctTreeTestHardCodedTestWorld.h | 1 + 4 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 src/Tests/OctTreeTestAnders.cpp diff --git a/src/Tests/OctTreeTest.cpp b/src/Tests/OctTreeTest.cpp index 68aa4c7a..0206ce50 100644 --- a/src/Tests/OctTreeTest.cpp +++ b/src/Tests/OctTreeTest.cpp @@ -2,51 +2,12 @@ using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; #include //srand -#include "OctTreeTestGameClass.h" -//HACK! Needed for white box testing -//else we would have to "open up" the octTree class more with get/sets, public methods, etc. which is not good encapsulation-wise -//#include "Engine/Core/OctTree.h" + +#include "Engine/Core/OctTree.h" #include "Engine/Core/Ray.h" #include "OldOctTree.h" -//friend class and refactoringIntoNewClass is some extra work and needs to be updated when the original class is updated, and can contain bugs that -//isnt in the original class -//Reflection-inspection seems to be only available for C# -//http://stackoverflow.com/questions/6778496/how-to-do-unit-testing-on-private-members-and-methods-of-c-classes -//http://stackoverflow.com/questions/3676664/unit-testing-of-private-methods -#define private public -#include -BOOST_AUTO_TEST_SUITE(octTreeTests) - -BOOST_AUTO_TEST_CASE(octTreeTest) -{ - //white box testing - //http://softwaretestingfundamentals.com/differences-between-black-box-testing-and-white-box-testing/ - //http://technologyconversations.com/2013/12/11/black-box-vs-white-box-testing/ - - //simple AABB constructor check - auto minCorner = glm::vec3(0.0f, 0.0f, 0.0f); - auto maxCorner = glm::vec3(1.0f, 1.0f, 1.0f); - auto someAABB = AABB(minCorner,maxCorner); - BOOST_CHECK(someAABB.MinCorner() == minCorner); - BOOST_CHECK(someAABB.MaxCorner() == maxCorner); - BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner)); - - //simple OctTree constructor check - //OctTree someOctTree(someAABB, 5); - //BOOST_CHECK(someOctTree.m_Children[0] != nullptr); - - //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure -} - -BOOST_AUTO_TEST_CASE(octTreeTest2) -{ - //octtree ritningen osv - Game game(0, nullptr); - while (game.Running()) { - game.Tick(); - } -} +BOOST_AUTO_TEST_SUITE(octTreeTestsW) BOOST_AUTO_TEST_CASE(octSameRegionTest) { diff --git a/src/Tests/OctTreeTestAnders.cpp b/src/Tests/OctTreeTestAnders.cpp new file mode 100644 index 00000000..12a122ea --- /dev/null +++ b/src/Tests/OctTreeTestAnders.cpp @@ -0,0 +1,53 @@ +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; +#include //srand + +//#define private public//HACK! Needed for white box testing +//#include "Engine/Core/OctTree.h" +//#include "OldOctTree.h" +//friend class and refactoringIntoNewClass is some extra work and needs to be updated when the original class is updated, and can contain bugs that +//isnt in the original class +//Reflection-inspection seems to be only available for C# +//http://stackoverflow.com/questions/6778496/how-to-do-unit-testing-on-private-members-and-methods-of-c-classes +//http://stackoverflow.com/questions/3676664/unit-testing-of-private-methods + +#include "OctTreeTestGameClass.h" + +#define private public//HACK! Needed for white box testing +#include +//else we would have to "open up" the octTree class more with get/sets, public methods, etc. which is not good encapsulation-wise + +BOOST_AUTO_TEST_SUITE(octTreeTestsA) + +BOOST_AUTO_TEST_CASE(octTreeTest) +{ + //white box testing + //http://softwaretestingfundamentals.com/differences-between-black-box-testing-and-white-box-testing/ + //http://technologyconversations.com/2013/12/11/black-box-vs-white-box-testing/ + + //simple AABB constructor check + auto minCorner = glm::vec3(0.0f, 0.0f, 0.0f); + auto maxCorner = glm::vec3(1.0f, 1.0f, 1.0f); + auto someAABB = AABB(minCorner, maxCorner); + BOOST_CHECK(someAABB.MinCorner() == minCorner); + BOOST_CHECK(someAABB.MaxCorner() == maxCorner); + BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner)); + + //simple OctTree constructor check + //OctTree someOctTree(someAABB, 5); + //BOOST_CHECK(someOctTree.m_Children[0] != nullptr); + + //simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure +} + +BOOST_AUTO_TEST_CASE(octTreeTest2) +{ + //octtree ritningen osv + Game game(0, nullptr); + while (game.Running()) { + game.Tick(); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/Tests/OctTreeTestGameClass.h b/src/Tests/OctTreeTestGameClass.h index 8ca34510..38aaab66 100644 --- a/src/Tests/OctTreeTestGameClass.h +++ b/src/Tests/OctTreeTestGameClass.h @@ -13,6 +13,7 @@ #include "OctTreeTestHardCodedTestWorld.h" #include "Core\Collision.h" + class Game { public: diff --git a/src/Tests/OctTreeTestHardCodedTestWorld.h b/src/Tests/OctTreeTestHardCodedTestWorld.h index 46b1e00b..512716df 100644 --- a/src/Tests/OctTreeTestHardCodedTestWorld.h +++ b/src/Tests/OctTreeTestHardCodedTestWorld.h @@ -7,6 +7,7 @@ #include //last! +//#include "OldOctTree.h" #define private public #include From 7f8e78c8cdeedcfb794b75b4cba3b00f583941ea Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Mon, 14 Dec 2015 17:04:35 +0100 Subject: [PATCH 38/61] RayVsModel tests in progress --- src/Engine/Core/Collision.cpp | 13 +++-- src/Tests/CMakeLists.txt | 1 + src/Tests/CollisionTest.cpp | 95 ++++++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index dee567e0..44875aa4 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -86,10 +86,14 @@ bool RayVsModel(const Ray& ray, glm::vec3 m = ray.Origin - v0; glm::vec3 MxE1 = glm::cross(m, e1); glm::vec3 DxE2 = glm::cross(ray.Direction, e2); - float DetInv = 1.0f / glm::dot(e1, DxE2); + float DetInv = glm::dot(e1, DxE2); + if (std::abs(DetInv) < FLT_EPSILON) { + continue; + } + DetInv = 1.0f / DetInv; float u = glm::dot(m, DxE2) * DetInv; float v = glm::dot(ray.Direction, MxE1) * DetInv; - if (u < 0 && v < 0 && 1 < u + v) { + if (u < 0 || v < 0 || 1 < u + v) { continue; } //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. @@ -116,7 +120,10 @@ bool RayVsModel(const Ray& ray, glm::vec3 m = ray.Origin - v0; glm::vec3 MxE1 = glm::cross(m, e1); glm::vec3 DxE2 = glm::cross(ray.Direction, e2); - float DetInv = 1.0f / glm::dot(e1, DxE2); + float DetInv = glm::dot(e1, DxE2); + if (std::abs(DetInv) < FLT_EPSILON) { + continue; + } float dist = glm::dot(e2, MxE1) * DetInv; if (dist >= outDistance) { continue; diff --git a/src/Tests/CMakeLists.txt b/src/Tests/CMakeLists.txt index 697dea29..a3f95a37 100644 --- a/src/Tests/CMakeLists.txt +++ b/src/Tests/CMakeLists.txt @@ -12,6 +12,7 @@ include_directories( ) file(GLOB SOURCE_FILES + "*.h" "*.cpp" ) diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index eef24e61..497812a3 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -3,11 +3,18 @@ #include using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; -#include +#include "Engine\Core\Collision.h" #include "Engine/Core/AABB.h" #include "Engine/Core/Ray.h" #include //srand #include "Engine/Core/OctTree.h" +//vs model +#include + +//ray vs model +#include "Engine\Core\ResourceManager.h" +#include "Engine\Rendering\Model.h" +#include "Engine\Core\Ray.h" //vs memleaks //#define _CRTDBG_MAP_ALLOC @@ -87,6 +94,92 @@ BOOST_AUTO_TEST_CASE(collisionTest2) BOOST_CHECK(test >= 0); } +BOOST_AUTO_TEST_CASE(rayVsModelTest) +{ + //simple test + + Ray ray; + ray.Origin = glm::vec3(-50, 0, 0); + //ray.Direction = glm::vec3(-1, 0, 0); + ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); + //inte model, det kräver renderar grejs tydligen + ResourceManager::RegisterType("RawModel"); + auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); + BOOST_CHECK(unitBox != nullptr); + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(hit); + ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(!hit); +} + +BOOST_AUTO_TEST_CASE(rayVsModelTest2) +{ + //advanced test, based on ray vs AABB + srand(7676762); + Ray ray; + AABB someAABB; + glm::vec3 minPos; + glm::vec3 maxPos; + bool z; + int test = 0; + minPos = glm::vec3(-0.5f, -0.5f, -0.5f); + maxPos = glm::vec3(0.5f, 0.5f, 0.5f); + someAABB = AABB(minPos, maxPos); + ResourceManager::RegisterType("RawModel"); + auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); + BOOST_CHECK(unitBox != nullptr); + + for (size_t i = 0; i < 1000000; i++) + { + ray.Origin.x = rand() % 100; + ray.Origin.y = rand() % 100; + ray.Origin.z = rand() % 100; + ray.Direction.x = rand() % 100; + ray.Direction.y = rand() % 100; + ray.Direction.z = rand() % 100; + ray.Origin /= 100; + ray.Origin = glm::vec3(-2, 0, 0); + ray.Direction /= 100; + ray.Direction = glm::normalize(ray.Direction); + + z = Collision::RayVsAABB(ray, someAABB); + if (z) { + //hit + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + if (!hit) { + hit = hit; + glm::vec3 outtttttttt; + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + } + else { + hit = hit; + } + BOOST_CHECK(hit); + } + // + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + if (hit) { + //hit + z = Collision::RayVsAABB(ray, someAABB); + if (!z) { + z = z; + z = Collision::RayVsAABB(ray, someAABB); + glm::vec3 outtttttttt; + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + } + else { + z = z; + } + BOOST_CHECK(hit); + } + + } +} + + BOOST_AUTO_TEST_CASE(octTest) { glm::vec3 mini = glm::vec3(-1, -1, -1); From 59095c2ce50b85f864d57eedb38d0a5da0224cde Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 14 Dec 2015 17:08:00 +0100 Subject: [PATCH 39/61] Started on CollisionSystem and TriggerSystem. Moved collision code to Collision folder. --- .../Engine/{Core => Collision}/Collision.h | 0 include/Engine/Collision/CollisionSystem.h | 30 ++++++++++++++ include/Engine/Collision/ETrigger.h | 39 +++++++++++++++++++ include/Engine/Collision/TriggerSystem.h | 23 +++++++++++ resources/Schema/Components.xsd | 2 + resources/Schema/Components/AABB.xml | 4 ++ resources/Schema/Components/AABB.xsd | 14 +++++++ resources/Schema/Components/Trigger.xml | 2 + resources/Schema/Components/Trigger.xsd | 8 ++++ src/Engine/{Core => Collision}/Collision.cpp | 0 src/Engine/Collision/CollisionSystem.cpp | 16 ++++++++ src/Engine/Collision/TriggerSystem.cpp | 22 +++++++++++ src/Game/Game.cpp | 4 +- 13 files changed, 162 insertions(+), 2 deletions(-) rename include/Engine/{Core => Collision}/Collision.h (100%) create mode 100644 include/Engine/Collision/CollisionSystem.h create mode 100644 include/Engine/Collision/ETrigger.h create mode 100644 include/Engine/Collision/TriggerSystem.h create mode 100644 resources/Schema/Components/AABB.xml create mode 100644 resources/Schema/Components/AABB.xsd create mode 100644 resources/Schema/Components/Trigger.xml create mode 100644 resources/Schema/Components/Trigger.xsd rename src/Engine/{Core => Collision}/Collision.cpp (100%) create mode 100644 src/Engine/Collision/CollisionSystem.cpp create mode 100644 src/Engine/Collision/TriggerSystem.cpp diff --git a/include/Engine/Core/Collision.h b/include/Engine/Collision/Collision.h similarity index 100% rename from include/Engine/Core/Collision.h rename to include/Engine/Collision/Collision.h diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h new file mode 100644 index 00000000..1aecfacc --- /dev/null +++ b/include/Engine/Collision/CollisionSystem.h @@ -0,0 +1,30 @@ +#ifndef CollisionSystem_h__ +#define CollisionSystem_h__ + +#include +#include + +#include "Common.h" +#include "Core/System.h" +#include "Core/EventBroker.h" +#include "Core/EKeyUp.h" + +class CollisionSystem : public System +{ +public: + CollisionSystem(EventBroker* eventBroker) + : System(eventBroker, "AABB") + { + //TODO: Debug stuff, remove later. + EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &CollisionSystem::OnKeyUp); + } + + virtual void Update(World* world, ComponentWrapper& collision, double dt) override; + +private: + + EventRelay m_EKeyUp; + bool OnKeyUp(const Events::KeyUp &event); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Collision/ETrigger.h b/include/Engine/Collision/ETrigger.h new file mode 100644 index 00000000..aaa5ef06 --- /dev/null +++ b/include/Engine/Collision/ETrigger.h @@ -0,0 +1,39 @@ +#ifndef Events_TriggerEnter_h__ +#define Events_TriggerEnter_h__ + +#include "../Core/EventBroker.h" +#include "../Core/Entity.h" + +namespace Events +{ + +/** Thrown once, when an entity is completely inside a trigger. */ +struct TriggerTouch : Event +{ + /** The id of the entity that touches the trigger. */ + EntityID Entity; + /** The id of the trigger entity. */ + EntityID Trigger; +}; + +/** Thrown once, when an entity has completely left a trigger. */ +struct TriggerLeave : Event +{ + /** The id of the entity that left the trigger. */ + EntityID Entity; + /** The id of the trigger entity. */ + EntityID Trigger; +}; + +/** Thrown when an entity is completely inside a trigger. */ +struct TriggerEnter : Event +{ + /** The id of the entity that entered the trigger. */ + EntityID Entity; + /** The id of the trigger entity. */ + EntityID Trigger; +}; + +} + +#endif diff --git a/include/Engine/Collision/TriggerSystem.h b/include/Engine/Collision/TriggerSystem.h new file mode 100644 index 00000000..94084ee3 --- /dev/null +++ b/include/Engine/Collision/TriggerSystem.h @@ -0,0 +1,23 @@ +#ifndef PlayerSystem_h__ +#define PlayerSystem_h__ + +#include + +#include "Core/System.h" +#include "Core/EventBroker.h" +#include "ETrigger.h" + +class TriggerSystem : public System +{ +public: + TriggerSystem(EventBroker* eventBroker) + : System(eventBroker, "Trigger") + {} + + virtual void Update(World* world, ComponentWrapper& collision, double dt) override; + +private: + +}; + +#endif \ No newline at end of file diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 12fb870e..fd04fd39 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -6,4 +6,6 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/AABB.xml b/resources/Schema/Components/AABB.xml new file mode 100644 index 00000000..341d1d0d --- /dev/null +++ b/resources/Schema/Components/AABB.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resources/Schema/Components/AABB.xsd b/resources/Schema/Components/AABB.xsd new file mode 100644 index 00000000..8fac860e --- /dev/null +++ b/resources/Schema/Components/AABB.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Trigger.xml b/resources/Schema/Components/Trigger.xml new file mode 100644 index 00000000..4c8aad58 --- /dev/null +++ b/resources/Schema/Components/Trigger.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/Trigger.xsd b/resources/Schema/Components/Trigger.xsd new file mode 100644 index 00000000..a8bc8865 --- /dev/null +++ b/resources/Schema/Components/Trigger.xsd @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Collision/Collision.cpp similarity index 100% rename from src/Engine/Core/Collision.cpp rename to src/Engine/Collision/Collision.cpp diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp new file mode 100644 index 00000000..d1657361 --- /dev/null +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -0,0 +1,16 @@ +#include "Collision/CollisionSystem.h" + + +void CollisionSystem::Update(World * world, ComponentWrapper & collision, double dt) +{ + m_EventBroker->Process(); + (glm::vec3&)collision["Center"] = (glm::vec3)world->GetComponent(collision.EntityID, "Transform")["Position"]; +} + +bool CollisionSystem::OnKeyUp(const Events::KeyUp & event) +{ + if (event.KeyCode == GLFW_KEY_Z) { + + } + return false; +} diff --git a/src/Engine/Collision/TriggerSystem.cpp b/src/Engine/Collision/TriggerSystem.cpp new file mode 100644 index 00000000..4b6c0de4 --- /dev/null +++ b/src/Engine/Collision/TriggerSystem.cpp @@ -0,0 +1,22 @@ +#include "Collision/TriggerSystem.h" +#include "Core/AABB.h" + +void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt) +{ + auto players = world->GetComponents("Player"); + if (players == nullptr) { + return; + } + //WTODO: Assumes box exists. + ComponentWrapper& cBox = world->GetComponent(trigger.EntityID, "AABB"); + AABB aabb; + aabb.CreateFromCenter(cBox["BoxCenter"], cBox["BoxSize"]); + for (auto& c : *players) { + + Events::TriggerEnter e; + e.Trigger = trigger.EntityID; + e.Entity = 41; + m_EventBroker->Publish(e); + } +} + diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index fa12bbc6..1070d2fa 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,4 +1,5 @@ #include "Game.h" +#include "Collision/TriggerSystem.h" Game::Game(int argc, char* argv[]) { @@ -46,8 +47,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline = new SystemPipeline(m_EventBroker); m_SystemPipeline->AddSystem(); m_SystemPipeline->AddSystem(); - - + m_SystemPipeline->AddSystem(); m_LastTime = glfwGetTime(); From ae7d4b90979bf901160da16bfce4cdea3fa0aa16 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 14 Dec 2015 17:35:39 +0100 Subject: [PATCH 40/61] Put Collision folder in CMakeLists.txt and altered paths. --- include/Engine/Collision/TriggerSystem.h | 7 ++++--- src/Engine/CMakeLists.txt | 7 +++++++ src/Engine/Collision/Collision.cpp | 2 +- src/Engine/Core/OctTree.cpp | 2 +- src/Tests/CollisionTest.cpp | 2 +- src/Tests/OctTreeTestGameClass.cpp | 2 +- src/Tests/OctTreeTestGameClass.h | 2 +- src/Tests/OctTreeTestGameMain.cpp | 2 +- src/Tests/OldOctTree.cpp | 2 +- 9 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/Engine/Collision/TriggerSystem.h b/include/Engine/Collision/TriggerSystem.h index 94084ee3..a7b2122f 100644 --- a/include/Engine/Collision/TriggerSystem.h +++ b/include/Engine/Collision/TriggerSystem.h @@ -1,7 +1,8 @@ -#ifndef PlayerSystem_h__ -#define PlayerSystem_h__ +#ifndef TriggerSystem_h__ +#define TriggerSystem_h__ #include +#include #include "Core/System.h" #include "Core/EventBroker.h" @@ -17,7 +18,7 @@ public: virtual void Update(World* world, ComponentWrapper& collision, double dt) override; private: - + std::unordered_map> m_EntitiesInTrigger; }; #endif \ No newline at end of file diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt index de24b9cf..915cdee2 100644 --- a/src/Engine/CMakeLists.txt +++ b/src/Engine/CMakeLists.txt @@ -70,6 +70,12 @@ file(GLOB SOURCE_FILES_GUI ) source_group(GUI FILES ${SOURCE_FILES_GUI}) +file(GLOB SOURCE_FILES_Collision + "${INCLUDE_PATH}/Collision/*.h" + "Collision/*.cpp" +) +source_group(Collision FILES ${SOURCE_FILES_Collision}) + set(SOURCE_FILES ${SOURCE_FILES_Core} ${SOURCE_FILES_Core_Util} @@ -78,6 +84,7 @@ set(SOURCE_FILES ${SOURCE_FILES_GUI} ${SOURCE_FILES_Rendering} ${SOURCE_FILES_Rendering_Util} + ${SOURCE_FILES_Collision} ) set(LIBRARIES diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 44875aa4..f8dfedcb 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -1,6 +1,6 @@ #include -#include "Core/Collision.h" +#include "Collision/Collision.h" #include "Engine/GLM.h" namespace Collision diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index e41a1692..b5fed86b 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -3,7 +3,7 @@ #include #include "Core/OctTree.h" -#include "Core/Collision.h" +#include "Collision/Collision.h" #include "Core/World.h" #include "Rendering/Camera.h" diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 497812a3..21b2ef20 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -3,7 +3,7 @@ #include using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; -#include "Engine\Core\Collision.h" +#include "Engine/Collision/Collision.h" #include "Engine/Core/AABB.h" #include "Engine/Core/Ray.h" #include //srand diff --git a/src/Tests/OctTreeTestGameClass.cpp b/src/Tests/OctTreeTestGameClass.cpp index 735d3ee8..1a7deb3f 100644 --- a/src/Tests/OctTreeTestGameClass.cpp +++ b/src/Tests/OctTreeTestGameClass.cpp @@ -15,7 +15,7 @@ Game::Game(int argc, char* argv[]) : someOctTree(AABB(-0.5f*worldSize, 0.5f*worl m_RenderQueueFactory = new RenderQueueFactory(); // Create the renderer - m_Renderer = new Renderer(); + m_Renderer = new Renderer(m_EventBroker); m_Renderer->SetFullscreen(m_Config->Get("Video.Fullscreen", false)); m_Renderer->SetVSYNC(m_Config->Get("Video.VSYNC", false)); m_Renderer->SetResolution(Rectangle( diff --git a/src/Tests/OctTreeTestGameClass.h b/src/Tests/OctTreeTestGameClass.h index 38aaab66..985d34d4 100644 --- a/src/Tests/OctTreeTestGameClass.h +++ b/src/Tests/OctTreeTestGameClass.h @@ -11,7 +11,7 @@ #include "Rendering/RenderQueueFactory.h" #include "OctTreeTestHardCodedTestWorld.h" -#include "Core\Collision.h" +#include "Collision/Collision.h" class Game diff --git a/src/Tests/OctTreeTestGameMain.cpp b/src/Tests/OctTreeTestGameMain.cpp index 5dd23cf2..43789cea 100644 --- a/src/Tests/OctTreeTestGameMain.cpp +++ b/src/Tests/OctTreeTestGameMain.cpp @@ -3,7 +3,7 @@ #include using boost::unit_test_framework::test_suite; using boost::unit_test_framework::test_case; -#include +#include "Engine/Collision/Collision.h" #include "Engine/Core/AABB.h" #include "Engine/Core/Ray.h" #include //srand diff --git a/src/Tests/OldOctTree.cpp b/src/Tests/OldOctTree.cpp index 72decd4d..4aabf9ed 100644 --- a/src/Tests/OldOctTree.cpp +++ b/src/Tests/OldOctTree.cpp @@ -3,7 +3,7 @@ #include #include "OldOctTree.h" -#include "Core/Collision.h" +#include "Collision/Collision.h" #include "Core/World.h" #include "Rendering/Camera.h" From 948f6d5de74d967f931fee04b737989930268b57 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Mon, 14 Dec 2015 17:53:11 +0100 Subject: [PATCH 41/61] RayVsModel & RayVsAABB comparison continued --- src/Engine/Core/Collision.cpp | 3 ++- src/Tests/CollisionTest.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index 44875aa4..f5381c08 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -119,11 +119,12 @@ bool RayVsModel(const Ray& ray, glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 glm::vec3 m = ray.Origin - v0; glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction, e2); + glm::vec3 DxE2 = glm::cross(ray.Direction, e2);//pVec float DetInv = glm::dot(e1, DxE2); if (std::abs(DetInv) < FLT_EPSILON) { continue; } + DetInv = 1.0f / DetInv; float dist = glm::dot(e2, MxE1) * DetInv; if (dist >= outDistance) { continue; diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 497812a3..3395d505 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -127,7 +127,8 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) maxPos = glm::vec3(0.5f, 0.5f, 0.5f); someAABB = AABB(minPos, maxPos); ResourceManager::RegisterType("RawModel"); - auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); + //auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); + auto unitBox = ResourceManager::Load("Models/Core/UnitCube.obj"); BOOST_CHECK(unitBox != nullptr); for (size_t i = 0; i < 1000000; i++) From 8e431c8edd2f0c04b45a1a53940d0201356b1ba6 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Tue, 15 Dec 2015 11:07:09 +0100 Subject: [PATCH 42/61] RayVsModel && RayVsAABB tests added and working. Also adjusted RayVsModel,RayVsAABB with deltas when values are very close to each other (floatingprecision problems) --- src/Engine/Core/Collision.cpp | 287 +++++++++++++++++----------------- src/Tests/CollisionTest.cpp | 86 +++++++++- 2 files changed, 225 insertions(+), 148 deletions(-) diff --git a/src/Engine/Core/Collision.cpp b/src/Engine/Core/Collision.cpp index f5381c08..151a7a28 100644 --- a/src/Engine/Core/Collision.cpp +++ b/src/Engine/Core/Collision.cpp @@ -6,153 +6,160 @@ namespace Collision { -bool RayAABBIntr(const Ray& ray, const AABB& box) -{ - glm::vec3 w = 75.0f * ray.Direction; - glm::vec3 v = glm::abs(w); - glm::vec3 c = ray.Origin - box.Center() + w; - glm::vec3 half = box.HalfSize(); - - if (abs(c.x) > v.x + half.x) { - return false; - } - if (abs(c.y) > v.y + half.y) { - return false; - } - if (abs(c.z) > v.z + half.z) { - return false; - } + //note: this one hasnt been delta adjusted like RayVsAABB has + bool RayAABBIntr(const Ray& ray, const AABB& box) + { + glm::vec3 w = 75.0f * ray.Direction; + glm::vec3 v = glm::abs(w); + glm::vec3 c = ray.Origin - box.Center() + w; + glm::vec3 half = box.HalfSize(); - if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) { - return false; - } - if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) { - return false; - } - return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); -} - -bool RayVsAABB(const Ray& ray, const AABB& box) -{ - float dummy; - return RayVsAABB(ray, box, dummy); -} - -bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) -{ - glm::vec3 invdir = 1.0f / ray.Direction; - - float t1 = (box.MinCorner().x - ray.Origin.x)*invdir.x; - float t2 = (box.MaxCorner().x - ray.Origin.x)*invdir.x; - float t3 = (box.MinCorner().y - ray.Origin.y)*invdir.y; - float t4 = (box.MaxCorner().y - ray.Origin.y)*invdir.y; - float t5 = (box.MinCorner().z - ray.Origin.z)*invdir.z; - float t6 = (box.MaxCorner().z - ray.Origin.z)*invdir.z; - - float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); - float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); - - if (tmax < 0 || tmin > tmax) - return false; - - outDistance = (tmin > 0) ? tmin : tmax; - return true; -} - -bool AABBVsAABB(const AABB& a, const AABB& b) -{ - const glm::vec3& aCenter = a.Center(); - const glm::vec3& bCenter = b.Center(); - const glm::vec3& aHSize = a.HalfSize(); - const glm::vec3& bHSize = b.HalfSize(); - //Test will probably exit because of the X and Z axes more often, so test them first. - if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) { - return false; - } - if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) { - return false; - } - return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); -} - -bool RayVsModel(const Ray& ray, - const std::vector& modelVertices, - const std::vector& modelIndices) -{ - for (int i = 0; i < modelIndices.size(); ++i) { - glm::vec3 v0 = modelVertices[modelIndices[i]].Position; - glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 - glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin - v0; - glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction, e2); - float DetInv = glm::dot(e1, DxE2); - if (std::abs(DetInv) < FLT_EPSILON) { - continue; + if (abs(c.x) > v.x + half.x) { + return false; } - DetInv = 1.0f / DetInv; - float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction, MxE1) * DetInv; - if (u < 0 || v < 0 || 1 < u + v) { - continue; + if (abs(c.y) > v.y + half.y) { + return false; } - //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. - if (0 <= glm::dot(e2, MxE1) * DetInv) { - return true; + if (abs(c.z) > v.z + half.z) { + return false; } + + if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) { + return false; + } + if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) { + return false; + } + return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x); } - return false; -} -bool RayVsModel(const Ray& ray, - const std::vector& modelVertices, - const std::vector& modelIndices, - float& outDistance, - float& outUCoord, - float& outVCoord) -{ - outDistance = INFINITY; - bool hit = false; - for (int i = 0; i < modelIndices.size(); ++i) { - glm::vec3 v0 = modelVertices[modelIndices[i]].Position; - glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 - glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin - v0; - glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction, e2);//pVec - float DetInv = glm::dot(e1, DxE2); - if (std::abs(DetInv) < FLT_EPSILON) { - continue; - } - DetInv = 1.0f / DetInv; - float dist = glm::dot(e2, MxE1) * DetInv; - if (dist >= outDistance) { - continue; - } - float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction, MxE1) * DetInv; - //If u and v are positive, u+v <= 1, dist is positive, and less than closest. - if (0 <= u && 0 <= v && u + v <= 1 && 0 <= dist) { - outDistance = dist; - outUCoord = u; - outVCoord = v; - hit = true; - } + bool RayVsAABB(const Ray& ray, const AABB& box) + { + float dummy; + return RayVsAABB(ray, box, dummy); } - return hit; -} -bool RayVsModel(const Ray& ray, - const std::vector& modelVertices, - const std::vector& modelIndices, - glm::vec3& outHitPosition) -{ - float u; - float v; - float dist; - bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); - outHitPosition = ray.Origin + dist * ray.Direction; - return hit; -} + bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) + { + glm::vec3 invdir = 1.0f / ray.Direction; + + float t1 = (box.MinCorner().x - ray.Origin.x)*invdir.x; + float t2 = (box.MaxCorner().x - ray.Origin.x)*invdir.x; + float t3 = (box.MinCorner().y - ray.Origin.y)*invdir.y; + float t4 = (box.MaxCorner().y - ray.Origin.y)*invdir.y; + float t5 = (box.MinCorner().z - ray.Origin.z)*invdir.z; + float t6 = (box.MaxCorner().z - ray.Origin.z)*invdir.z; + + float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); + float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); + + //if (tmax < 0 || tmin > tmax) + //if tmin,tmax are almost the same (i.e. hitting exactly in the corner) then tmin might be slightly + //greater than tmax becuase of floating-precision problems. fixed by adding a small delta to tmax + if (tmax < 0 || tmin>(tmax + 0.0001f)) + return false; + + outDistance = (tmin > 0) ? tmin : tmax; + return true; + } + + bool AABBVsAABB(const AABB& a, const AABB& b) + { + const glm::vec3& aCenter = a.Center(); + const glm::vec3& bCenter = b.Center(); + const glm::vec3& aHSize = a.HalfSize(); + const glm::vec3& bHSize = b.HalfSize(); + //Test will probably exit because of the X and Z axes more often, so test them first. + if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) { + return false; + } + if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) { + return false; + } + return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); + } + + bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices) + { + for (int i = 0; i < modelIndices.size(); ++i) { + glm::vec3 v0 = modelVertices[modelIndices[i]].Position; + glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 + glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 + glm::vec3 m = ray.Origin - v0; + glm::vec3 MxE1 = glm::cross(m, e1); + glm::vec3 DxE2 = glm::cross(ray.Direction, e2); + float DetInv = glm::dot(e1, DxE2); + if (std::abs(DetInv) < FLT_EPSILON) { + continue; + } + DetInv = 1.0f / DetInv; + float u = glm::dot(m, DxE2) * DetInv; + float v = glm::dot(ray.Direction, MxE1) * DetInv; + //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem + if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) { + continue; + } + //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. + if (0 <= glm::dot(e2, MxE1) * DetInv) { + return true; + } + } + return false; + } + + bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + float& outDistance, + float& outUCoord, + float& outVCoord) + { + outDistance = INFINITY; + bool hit = false; + for (int i = 0; i < modelIndices.size(); ++i) { + glm::vec3 v0 = modelVertices[modelIndices[i]].Position; + glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 + glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 + glm::vec3 m = ray.Origin - v0; + glm::vec3 MxE1 = glm::cross(m, e1); + glm::vec3 DxE2 = glm::cross(ray.Direction, e2);//pVec + float DetInv = glm::dot(e1, DxE2); + if (std::abs(DetInv) < FLT_EPSILON) { + continue; + } + DetInv = 1.0f / DetInv; + float dist = glm::dot(e2, MxE1) * DetInv; + if (dist >= outDistance) { + continue; + } + float u = glm::dot(m, DxE2) * DetInv; + float v = glm::dot(ray.Direction, MxE1) * DetInv; + + //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem + //If u and v are positive, u+v <= 1, dist is positive, and less than closest. + if (0 <= (u + 0.001f) && 0 <= (v + 0.001f) && u + v <= 1 && 0 <= dist) { + outDistance = dist; + outUCoord = u; + outVCoord = v; + hit = true; + } + } + return hit; + } + + bool RayVsModel(const Ray& ray, + const std::vector& modelVertices, + const std::vector& modelIndices, + glm::vec3& outHitPosition) + { + float u; + float v; + float dist; + bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); + outHitPosition = ray.Origin + dist * ray.Direction; + return hit; + } } diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 3395d505..d5f7e0ed 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -96,13 +96,12 @@ BOOST_AUTO_TEST_CASE(collisionTest2) BOOST_AUTO_TEST_CASE(rayVsModelTest) { - //simple test + //simple box test Ray ray; ray.Origin = glm::vec3(-50, 0, 0); - //ray.Direction = glm::vec3(-1, 0, 0); ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); - //inte model, det kräver renderar grejs tydligen + //using a rawmodel here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); BOOST_CHECK(unitBox != nullptr); @@ -115,19 +114,24 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest) BOOST_AUTO_TEST_CASE(rayVsModelTest2) { - //advanced test, based on ray vs AABB - srand(7676762); + //advanced test, this will check so rayVSAABB and rayVsModel(with boxmodel) gives the same result (hit/miss) + //testing with different seeds +// srand(7676762); +// srand(7676462); +// srand(7462); + srand(72); Ray ray; AABB someAABB; glm::vec3 minPos; glm::vec3 maxPos; bool z; int test = 0; + //min/max is the same as the rawmodels boundaries ofcourse minPos = glm::vec3(-0.5f, -0.5f, -0.5f); maxPos = glm::vec3(0.5f, 0.5f, 0.5f); someAABB = AABB(minPos, maxPos); + //using a rawmodel here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); - //auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); auto unitBox = ResourceManager::Load("Models/Core/UnitCube.obj"); BOOST_CHECK(unitBox != nullptr); @@ -142,6 +146,9 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) ray.Origin /= 100; ray.Origin = glm::vec3(-2, 0, 0); ray.Direction /= 100; + //if we normalize the ray.direction when its 0,0,0 then we get nan,nan,nan - thus we have this check to prevent that + if (ray.Direction.x < 0.0001f && ray.Direction.y < 0.0001f && ray.Direction.z < 0.0001f) + continue; ray.Direction = glm::normalize(ray.Direction); z = Collision::RayVsAABB(ray, someAABB); @@ -149,7 +156,7 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) //hit bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); if (!hit) { - hit = hit; + //if rayvsaabb hit but rayvvmodel didnt hit, we get to here glm::vec3 outtttttttt; hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt); hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); @@ -159,13 +166,21 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) } BOOST_CHECK(hit); } + ////breakpoint test + //if (!z) { + // z = z; + //} // bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + ////breakpoint test + //if (!hit) { + // hit = hit; + //} if (hit) { //hit z = Collision::RayVsAABB(ray, someAABB); if (!z) { - z = z; + //if rayvsmodel hit but rayvsaabb didnt hit then we get to here z = Collision::RayVsAABB(ray, someAABB); glm::vec3 outtttttttt; hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt); @@ -180,7 +195,62 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) } } +BOOST_AUTO_TEST_CASE(rayVsModelTest3) +{ + //simple test + Ray ray; + ray.Origin = glm::vec3(-50, 0, 0); + //ray.Direction = glm::vec3(-1, 0, 0); + ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); + //using a rawmodel here, else we have to init the renderingsystem + ResourceManager::RegisterType("RawModel"); + auto unitBox = ResourceManager::Load("Models/Core/UnitSphere.obj"); + BOOST_CHECK(unitBox != nullptr); + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(hit); + ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(!hit); +} + +BOOST_AUTO_TEST_CASE(rayVsModelTest4) +{ + //simple test + + Ray ray; + ray.Origin = glm::vec3(-50, 0, 0); + //ray.Direction = glm::vec3(-1, 0, 0); + ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); + //using a rawmodel here, else we have to init the renderingsystem + ResourceManager::RegisterType("RawModel"); + auto unitBox = ResourceManager::Load("Models/Core/UnitCylinder.obj"); + BOOST_CHECK(unitBox != nullptr); + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(hit); + ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(!hit); +} + +BOOST_AUTO_TEST_CASE(rayVsModelTest5) +{ + //simple test + + Ray ray; + ray.Origin = glm::vec3(-50, 0, 0); + //ray.Direction = glm::vec3(-1, 0, 0); + ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); + //using a rawmodel here, else we have to init the renderingsystem + ResourceManager::RegisterType("RawModel"); + auto unitBox = ResourceManager::Load("Models/Core/UnitRaptor.obj"); + BOOST_CHECK(unitBox != nullptr); + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(hit); + ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(!hit); +} BOOST_AUTO_TEST_CASE(octTest) { glm::vec3 mini = glm::vec3(-1, -1, -1); From 454ed0032b45e391fd3803ec3b6dee682db77e36 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Tue, 15 Dec 2015 14:01:49 +0100 Subject: [PATCH 43/61] Refactored some RayVsModel tests --- src/Tests/CollisionTest.cpp | 77 ++++++++++--------------------------- 1 file changed, 21 insertions(+), 56 deletions(-) diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index eb048a79..82e2e800 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -10,6 +10,7 @@ using boost::unit_test_framework::test_case; #include "Engine/Core/OctTree.h" //vs model #include +#include //ray vs model #include "Engine\Core\ResourceManager.h" @@ -23,6 +24,22 @@ using boost::unit_test_framework::test_case; //#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) //#define new DEBUG_CLIENTBLOCK +void RayTest(std::string fileName) { + //simple box test + Ray ray; + ray.Origin = glm::vec3(-50, 0, 0); + ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); + //using a rawmodel here, else we have to init the renderingsystem + ResourceManager::RegisterType("RawModel"); + auto unitBox = ResourceManager::Load(fileName); + BOOST_CHECK(unitBox != nullptr); + bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(hit); + ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); + hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + BOOST_CHECK(!hit); +} + BOOST_AUTO_TEST_SUITE(collisionTests) BOOST_AUTO_TEST_CASE(collisionTest) @@ -97,19 +114,7 @@ BOOST_AUTO_TEST_CASE(collisionTest2) BOOST_AUTO_TEST_CASE(rayVsModelTest) { //simple box test - - Ray ray; - ray.Origin = glm::vec3(-50, 0, 0); - ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); - //using a rawmodel here, else we have to init the renderingsystem - ResourceManager::RegisterType("RawModel"); - auto unitBox = ResourceManager::Load("Models/Core/UnitBox.obj"); - BOOST_CHECK(unitBox != nullptr); - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(hit); - ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(!hit); + RayTest("Models/Core/UnitBox.obj"); } BOOST_AUTO_TEST_CASE(rayVsModelTest2) @@ -194,62 +199,22 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) } } - BOOST_AUTO_TEST_CASE(rayVsModelTest3) { //simple test - - Ray ray; - ray.Origin = glm::vec3(-50, 0, 0); - //ray.Direction = glm::vec3(-1, 0, 0); - ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); - //using a rawmodel here, else we have to init the renderingsystem - ResourceManager::RegisterType("RawModel"); - auto unitBox = ResourceManager::Load("Models/Core/UnitSphere.obj"); - BOOST_CHECK(unitBox != nullptr); - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(hit); - ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(!hit); + RayTest("Models/Core/UnitSphere.obj"); } BOOST_AUTO_TEST_CASE(rayVsModelTest4) { //simple test - - Ray ray; - ray.Origin = glm::vec3(-50, 0, 0); - //ray.Direction = glm::vec3(-1, 0, 0); - ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); - //using a rawmodel here, else we have to init the renderingsystem - ResourceManager::RegisterType("RawModel"); - auto unitBox = ResourceManager::Load("Models/Core/UnitCylinder.obj"); - BOOST_CHECK(unitBox != nullptr); - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(hit); - ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(!hit); + RayTest("Models/Core/UnitCylinder.obj"); } BOOST_AUTO_TEST_CASE(rayVsModelTest5) { //simple test - - Ray ray; - ray.Origin = glm::vec3(-50, 0, 0); - //ray.Direction = glm::vec3(-1, 0, 0); - ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); - //using a rawmodel here, else we have to init the renderingsystem - ResourceManager::RegisterType("RawModel"); - auto unitBox = ResourceManager::Load("Models/Core/UnitRaptor.obj"); - BOOST_CHECK(unitBox != nullptr); - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(hit); - ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); - BOOST_CHECK(!hit); + RayTest("Models/Core/UnitRaptor.obj"); } BOOST_AUTO_TEST_CASE(octTest) { From 170610bcb1c1b24b031beca0935087135a436b88 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 15 Dec 2015 14:49:52 +0100 Subject: [PATCH 44/61] Added a TriggerSystem, seems to work as intended. --- include/Engine/Collision/Collision.h | 1 + include/Engine/Collision/ETrigger.h | 2 +- include/Engine/Collision/TriggerSystem.h | 18 +++- include/Game/PlayerSystem.h | 10 ++ resources/Schema/Entities/Test.xml | 6 +- src/Engine/Collision/Collision.cpp | 14 +++ src/Engine/Collision/TriggerSystem.cpp | 121 +++++++++++++++++++++-- src/Engine/Core/OctTree.cpp | 12 +-- src/Game/PlayerSystem.cpp | 18 ++++ 9 files changed, 178 insertions(+), 24 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 87f521bd..8b1f96b2 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -37,6 +37,7 @@ bool RayVsModel(const Ray& ray, //Return true if the boxes are intersecting. bool AABBVsAABB(const AABB& a, const AABB& b); +bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon = 0.0001f); } diff --git a/include/Engine/Collision/ETrigger.h b/include/Engine/Collision/ETrigger.h index aaa5ef06..8852ee8c 100644 --- a/include/Engine/Collision/ETrigger.h +++ b/include/Engine/Collision/ETrigger.h @@ -25,7 +25,7 @@ struct TriggerLeave : Event EntityID Trigger; }; -/** Thrown when an entity is completely inside a trigger. */ +/** Thrown once, when an entity is completely inside a trigger. */ struct TriggerEnter : Event { /** The id of the entity that entered the trigger. */ diff --git a/include/Engine/Collision/TriggerSystem.h b/include/Engine/Collision/TriggerSystem.h index a7b2122f..59718018 100644 --- a/include/Engine/Collision/TriggerSystem.h +++ b/include/Engine/Collision/TriggerSystem.h @@ -8,6 +8,8 @@ #include "Core/EventBroker.h" #include "ETrigger.h" +class AABB; + class TriggerSystem : public System { public: @@ -18,7 +20,21 @@ public: virtual void Update(World* world, ComponentWrapper& collision, double dt) override; private: - std::unordered_map> m_EntitiesInTrigger; + std::unordered_map> m_EntitiesTouchingTrigger; + std::unordered_map> m_EntitiesCompletelyInTrigger; + + bool getEntityBox(World* world, EntityID id, AABB& outBox); + //True if leave event was thrown. + bool throwLeaveIfWasInTrigger(std::unordered_set& triggerSet, EntityID pId, EntityID tId); + void attachAABBComponentFromModel(World* world, EntityID id); + template + void publish(EntityID pId, EntityID tId) + { + Event e; + e.Trigger = tId; + e.Entity = pId; + m_EventBroker->Publish(e); + } }; #endif \ No newline at end of file diff --git a/include/Game/PlayerSystem.h b/include/Game/PlayerSystem.h index 18752ac6..de2a27a6 100644 --- a/include/Game/PlayerSystem.h +++ b/include/Game/PlayerSystem.h @@ -9,6 +9,7 @@ #include "Core/EventBroker.h" #include "Core/EKeyDown.h" #include "Core/EKeyUp.h" +#include "Collision/ETrigger.h" struct KeyInput { @@ -26,6 +27,9 @@ public: { EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &PlayerSystem::OnKeyDown); EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &PlayerSystem::OnKeyUp); + EVENT_SUBSCRIBE_MEMBER(m_ETouch, &PlayerSystem::OnTouch); + EVENT_SUBSCRIBE_MEMBER(m_EEnter, &PlayerSystem::OnEnter); + EVENT_SUBSCRIBE_MEMBER(m_ELeave, &PlayerSystem::OnLeave); } virtual void Update(World* world, ComponentWrapper& player, double dt) override; @@ -39,6 +43,12 @@ private: bool OnKeyDown(const Events::KeyDown &event); EventRelay m_EKeyUp; bool OnKeyUp(const Events::KeyUp &event); + EventRelay m_EEnter; + bool OnEnter(const Events::TriggerEnter &event); + EventRelay m_ETouch; + bool PlayerSystem::OnTouch(const Events::TriggerTouch &event); + EventRelay m_ELeave; + bool PlayerSystem::OnLeave(const Events::TriggerLeave &event); }; #endif \ No newline at end of file diff --git a/resources/Schema/Entities/Test.xml b/resources/Schema/Entities/Test.xml index 78494ce1..ef194ad0 100644 --- a/resources/Schema/Entities/Test.xml +++ b/resources/Schema/Entities/Test.xml @@ -14,11 +14,13 @@ - + Models/ScaleWidget.obj + + @@ -42,6 +44,8 @@ Models/Core/UnitCube.obj + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index f8dfedcb..baf599c0 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -154,4 +154,18 @@ bool RayVsModel(const Ray& ray, return hit; } +bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon) +{ + const glm::vec3& ma1 = first.MaxCorner(); + const glm::vec3& ma2 = first.MaxCorner(); + const glm::vec3& mi1 = second.MinCorner(); + const glm::vec3& mi2 = second.MinCorner(); + return (std::abs(ma1.x - ma2.x) < epsilon) && + (std::abs(mi1.x - mi2.x) < epsilon) && + (std::abs(ma1.z - ma2.z) < epsilon) && + (std::abs(mi1.z - mi2.z) < epsilon) && + (std::abs(ma1.y - ma2.y) < epsilon) && + (std::abs(mi1.y - mi2.y) < epsilon); +} + } diff --git a/src/Engine/Collision/TriggerSystem.cpp b/src/Engine/Collision/TriggerSystem.cpp index 4b6c0de4..38a8fac4 100644 --- a/src/Engine/Collision/TriggerSystem.cpp +++ b/src/Engine/Collision/TriggerSystem.cpp @@ -1,22 +1,123 @@ #include "Collision/TriggerSystem.h" +#include "Collision/Collision.h" #include "Core/AABB.h" +#include "Rendering/Model.h" void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt) { + //Currently only players can trigger things. auto players = world->GetComponents("Player"); if (players == nullptr) { return; } - //WTODO: Assumes box exists. - ComponentWrapper& cBox = world->GetComponent(trigger.EntityID, "AABB"); - AABB aabb; - aabb.CreateFromCenter(cBox["BoxCenter"], cBox["BoxSize"]); - for (auto& c : *players) { - - Events::TriggerEnter e; - e.Trigger = trigger.EntityID; - e.Entity = 41; - m_EventBroker->Publish(e); + EntityID tId = trigger.EntityID; + AABB triggerBox; + //The trigger *should* have a bounding box, or something, to test against so it can be triggered. + if (!getEntityBox(world, tId, triggerBox)) { + return; + } + for (auto& pc : *players) { + EntityID pId = pc.EntityID; + AABB playerBox; + //The player can't trigger anything without an AABB. + if (!getEntityBox(world, pId, playerBox)) { + continue; + } + if (!Collision::AABBVsAABB(triggerBox, playerBox)) { + //Entity is not touching the trigger, + //Throw event if it was previously. + if (throwLeaveIfWasInTrigger(m_EntitiesTouchingTrigger[tId], pId, tId)) { + continue; + } + //This only occurs if the entity was completely inside the trigger one frame, + //then completely outside the trigger, e.g. when dying and respawning. + throwLeaveIfWasInTrigger(m_EntitiesCompletelyInTrigger[tId], pId, tId); + } else { + //Entity is at least touching the trigger. + AABB completelyInsideBox; + completelyInsideBox.CreateFromCenter(triggerBox.Center(), triggerBox.Size() - playerBox.Size()); + if (Collision::AABBVsAABB(completelyInsideBox, playerBox)) { + //Entity is completely inside the trigger. + //If it was only touching before, it is erased. + m_EntitiesTouchingTrigger[tId].erase(pId); + std::unordered_set& completeSet = m_EntitiesCompletelyInTrigger[tId]; + if (completeSet.count(pId) == 0) { + //If it wasn't completely in the trigger, throw Enter and add to the set. + completeSet.insert(pId); + publish(pId, tId); + } + } else { + //Entity is only touching the trigger. + std::unordered_set& touchSet = m_EntitiesTouchingTrigger[tId]; + std::unordered_set& completeSet = m_EntitiesCompletelyInTrigger[tId]; + const auto& it = completeSet.find(pId); + //If it was completely inside before. + if (it != completeSet.end()) { + completeSet.erase(it); + touchSet.insert(pId); + //If it was completely outside before. + } else if (touchSet.count(pId) == 0) { + publish(pId, tId); + touchSet.insert(pId); + } + //Else, it was touching the trigger last frame too and nothing is done. + } + } } } +bool TriggerSystem::getEntityBox(World* world, EntityID id, AABB& outBox) +{ + //TODO: Improve checking if component exists. Remove try + bool retry; + do { + retry = false; + try { + ComponentWrapper& cBox = world->GetComponent(id, "AABB"); + outBox.CreateFromCenter(cBox["BoxCenter"], cBox["BoxSize"]); + } catch (std::out_of_range e) { + retry = true; + attachAABBComponentFromModel(world, id); + } + } while (retry); + return true; +} + +bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set& triggerSet, EntityID pId, EntityID tId) +{ + const auto& it = triggerSet.find(pId); + if (it != triggerSet.end()) { + //If it was in the trigger, but not anymore, throw leaveEvent and erase from the set. + triggerSet.erase(it); + publish(pId, tId); + return true; + } + return false; +} + +void TriggerSystem::attachAABBComponentFromModel(World* world, EntityID id) +{ + ComponentWrapper model = world->GetComponent(id, "Model"); + ComponentWrapper transform = world->GetComponent(id, "Transform"); + ComponentWrapper collision = world->AttachComponent(id, "AABB"); + Model* modelRes = ResourceManager::Load(model["Resource"]); + + glm::mat4 modelMatrix = modelRes->m_Matrix * + glm::translate(glm::mat4(), (glm::vec3)transform["Position"]) * + glm::toMat4((glm::quat)transform["Orientation"]) * + glm::scale((glm::vec3)transform["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; +} diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index b5fed86b..8c262faf 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -21,16 +21,6 @@ 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() @@ -228,7 +218,7 @@ bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersect for (int i : m_DynamicObjIndices) { if (!m_DynamicObjectsRef[i].Checked) { const AABB& objBox = m_DynamicObjectsRef[i].Box; - if (!isSameBoxProbably(boxToTest, objBox) && + if (!Collision::IsSameBoxProbably(boxToTest, objBox) && Collision::AABBVsAABB(boxToTest, objBox)) { outBoxIntersected = objBox; return true; diff --git a/src/Game/PlayerSystem.cpp b/src/Game/PlayerSystem.cpp index 736ea105..4f86a2de 100644 --- a/src/Game/PlayerSystem.cpp +++ b/src/Game/PlayerSystem.cpp @@ -56,3 +56,21 @@ bool PlayerSystem::OnKeyUp(const Events::KeyUp & event) } return false; } + +bool PlayerSystem::OnTouch(const Events::TriggerTouch &event) +{ + LOG_INFO("Player %i touched widget (entity %i).", event.Entity, event.Trigger); + return false; +} + +bool PlayerSystem::OnEnter(const Events::TriggerEnter &event) +{ + LOG_INFO("Player %i entered widget (entity %i).", event.Entity, event.Trigger); + return false; +} + +bool PlayerSystem::OnLeave(const Events::TriggerLeave &event) +{ + LOG_INFO("Player %i left widget (entity %i).", event.Entity, event.Trigger); + return false; +} \ No newline at end of file From e2417be8c726043f94542d17552d9770af77226d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 15 Dec 2015 15:36:28 +0100 Subject: [PATCH 45/61] Very simple CollisionSystem made, 'Z','Y' to toggle on/off. --- include/Engine/Collision/CollisionSystem.h | 5 +-- resources/Schema/Entities/Test.xml | 7 ++-- src/Engine/Collision/CollisionSystem.cpp | 38 +++++++++++++++++++--- src/Game/Game.cpp | 2 ++ src/Game/PlayerSystem.cpp | 6 ++-- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h index 1aecfacc..e7a862f7 100644 --- a/include/Engine/Collision/CollisionSystem.h +++ b/include/Engine/Collision/CollisionSystem.h @@ -14,15 +14,16 @@ class CollisionSystem : public System public: CollisionSystem(EventBroker* eventBroker) : System(eventBroker, "AABB") + , zPress(false) { //TODO: Debug stuff, remove later. EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &CollisionSystem::OnKeyUp); } - virtual void Update(World* world, ComponentWrapper& collision, double dt) override; + virtual void Update(World* world, ComponentWrapper& cAABB, double dt) override; private: - + bool zPress; EventRelay m_EKeyUp; bool OnKeyUp(const Events::KeyUp &event); }; diff --git a/resources/Schema/Entities/Test.xml b/resources/Schema/Entities/Test.xml index ef194ad0..0233ff95 100644 --- a/resources/Schema/Entities/Test.xml +++ b/resources/Schema/Entities/Test.xml @@ -14,23 +14,24 @@ - + Models/ScaleWidget.obj - - + Models/RotationWidget.obj + + diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index d1657361..e1fcb0a2 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -1,16 +1,46 @@ +#include "Collision/Collision.h" #include "Collision/CollisionSystem.h" +#include "Core/AABB.h" - -void CollisionSystem::Update(World * world, ComponentWrapper & collision, double dt) +void CollisionSystem::Update(World * world, ComponentWrapper & cAABB, double dt) { + //cAABB is any entity that should be collideable. m_EventBroker->Process(); - (glm::vec3&)collision["Center"] = (glm::vec3)world->GetComponent(collision.EntityID, "Transform")["Position"]; + //Currently the box is translated according to the models Transform matrix, + //but not scaled (or rotated since it's AABB). This should be fine, if we don't + //want to shrink or scale up an collideable object after creation. + cAABB["BoxCenter"] = (glm::vec3)world->GetComponent(cAABB.EntityID, "Transform")["Position"]; + AABB thisBox; + thisBox.CreateFromCenter(cAABB["BoxCenter"], cAABB["BoxSize"]); + //Here c should be an object that moves, currently only players. + if (zPress) { + return; + } + for (auto& mover : *world->GetComponents("Player")) { + if (cAABB.EntityID == mover.EntityID) { + continue; + } + AABB otherBox; + ComponentWrapper& aabbComp = world->GetComponent(mover.EntityID, "AABB"); + otherBox.CreateFromCenter(aabbComp["BoxCenter"], aabbComp["BoxSize"]); + if (Collision::AABBVsAABB(thisBox, otherBox)) { + ComponentWrapper& trans = world->GetComponent(mover.EntityID, "Transform"); + //TODO: Move entity to correct position on collision instead of this. Special treatment if both are movers. + glm::vec3 newPos = trans["Position"]; + float moveSpeed = 0.12f; + newPos += moveSpeed * glm::normalize(newPos - thisBox.Center()); + trans["Position"] = newPos; + aabbComp["BoxCenter"] = newPos; + } + } } bool CollisionSystem::OnKeyUp(const Events::KeyUp & event) { if (event.KeyCode == GLFW_KEY_Z) { - + zPress = true; + } else if (event.KeyCode == GLFW_KEY_X) { + zPress = false; } return false; } diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 1070d2fa..ee5a29b0 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,5 +1,6 @@ #include "Game.h" #include "Collision/TriggerSystem.h" +#include "Collision/CollisionSystem.h" Game::Game(int argc, char* argv[]) { @@ -47,6 +48,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline = new SystemPipeline(m_EventBroker); m_SystemPipeline->AddSystem(); m_SystemPipeline->AddSystem(); + m_SystemPipeline->AddSystem(); m_SystemPipeline->AddSystem(); m_LastTime = glfwGetTime(); diff --git a/src/Game/PlayerSystem.cpp b/src/Game/PlayerSystem.cpp index 4f86a2de..727ddea3 100644 --- a/src/Game/PlayerSystem.cpp +++ b/src/Game/PlayerSystem.cpp @@ -59,18 +59,18 @@ bool PlayerSystem::OnKeyUp(const Events::KeyUp & event) bool PlayerSystem::OnTouch(const Events::TriggerTouch &event) { - LOG_INFO("Player %i touched widget (entity %i).", event.Entity, event.Trigger); + LOG_INFO("Player entity %i touched widget (entity %i).", event.Entity, event.Trigger); return false; } bool PlayerSystem::OnEnter(const Events::TriggerEnter &event) { - LOG_INFO("Player %i entered widget (entity %i).", event.Entity, event.Trigger); + LOG_INFO("Player entity %i entered widget (entity %i).", event.Entity, event.Trigger); return false; } bool PlayerSystem::OnLeave(const Events::TriggerLeave &event) { - LOG_INFO("Player %i left widget (entity %i).", event.Entity, event.Trigger); + LOG_INFO("Player entity %i left widget (entity %i).", event.Entity, event.Trigger); return false; } \ No newline at end of file From 99b76d7ae1780fdc3022ae097581e10fdfff1ec7 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 16 Dec 2015 12:19:43 +0100 Subject: [PATCH 46/61] Fixed returning address to temporary in functions. AABB also constructible from glm::vec4s. --- include/Engine/Core/AABB.h | 3 ++- include/Engine/Core/ComponentPool.h | 2 +- include/Engine/Core/MemoryPool.h | 2 +- src/Engine/Core/AABB.cpp | 4 ++++ src/Engine/Core/ComponentPool.cpp | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/Engine/Core/AABB.h b/include/Engine/Core/AABB.h index 0a0adafa..c8e05248 100644 --- a/include/Engine/Core/AABB.h +++ b/include/Engine/Core/AABB.h @@ -9,6 +9,7 @@ public: AABB() = default; //No checks are made. Values in minPos must be less than values in maxPos, i.e. min.x < max.x, etc. AABB(const glm::vec3& minPos, const glm::vec3& maxPos); + AABB(const glm::vec4& minPos, const glm::vec4& maxPos); //No checks are made. Size must consist of non-negative numbers. virtual void CreateFromCenter(const glm::vec3& center, const glm::vec3& size); virtual ~AABB(); @@ -16,7 +17,7 @@ public: 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& Size() const { return 2.0f * m_HalfSize; } + const glm::vec3 Size() const { return 2.0f * m_HalfSize; } const glm::vec3& HalfSize() const { return m_HalfSize; } private: glm::vec3 m_MinCorner; diff --git a/include/Engine/Core/ComponentPool.h b/include/Engine/Core/ComponentPool.h index 8dd8dc29..619aade8 100644 --- a/include/Engine/Core/ComponentPool.h +++ b/include/Engine/Core/ComponentPool.h @@ -20,7 +20,7 @@ public: ~ComponentPoolForwardIterator() = default; ComponentPoolForwardIterator& operator=(const ComponentPoolForwardIterator& other) = default; ComponentPoolForwardIterator& operator++(); - ComponentPoolForwardIterator& operator++(int); + ComponentPoolForwardIterator operator++(int); bool operator!=(const ComponentPoolForwardIterator& other) const; bool operator==(const ComponentPoolForwardIterator& other) const; ComponentWrapper operator*() const; diff --git a/include/Engine/Core/MemoryPool.h b/include/Engine/Core/MemoryPool.h index 034e6dc4..ff24ac80 100644 --- a/include/Engine/Core/MemoryPool.h +++ b/include/Engine/Core/MemoryPool.h @@ -253,7 +253,7 @@ public: } //Postfix increment i.e. iter++. Prefer pre-increment (++iter) for efficiency. - MemoryPoolForwardIterator& operator++(int) + MemoryPoolForwardIterator operator++(int) { MemoryPoolForwardIterator copyIter(*this); operator++(); diff --git a/src/Engine/Core/AABB.cpp b/src/Engine/Core/AABB.cpp index 9da2bdb0..05403022 100644 --- a/src/Engine/Core/AABB.cpp +++ b/src/Engine/Core/AABB.cpp @@ -7,6 +7,10 @@ AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos) , m_HalfSize(0.5f * (maxPos - minPos)) {} +AABB::AABB(const glm::vec4& minPos, const glm::vec4& maxPos) + : AABB(glm::vec3(minPos), glm::vec3(maxPos)) +{} + void AABB::CreateFromCenter(const glm::vec3& center, const glm::vec3& size) { m_Center = center; diff --git a/src/Engine/Core/ComponentPool.cpp b/src/Engine/Core/ComponentPool.cpp index ca9cc801..ce24c1f7 100644 --- a/src/Engine/Core/ComponentPool.cpp +++ b/src/Engine/Core/ComponentPool.cpp @@ -19,7 +19,7 @@ bool ComponentPoolForwardIterator::operator!=(const ComponentPoolForwardIterator return m_MemoryPoolIterator != other.m_MemoryPoolIterator; } -ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++(int) +ComponentPoolForwardIterator ComponentPoolForwardIterator::operator++(int) { ComponentPoolForwardIterator copyIter(*this); operator++(); From d10df6ae095b3589b2dc3b7d29b06d7320c04f2e Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 16 Dec 2015 12:22:15 +0100 Subject: [PATCH 47/61] TriggerSystem implemented and working. --- include/Engine/Collision/Collision.h | 8 ++++ include/Engine/Collision/TriggerSystem.h | 8 ++-- src/Engine/Collision/Collision.cpp | 57 ++++++++++++++++++++++++ src/Engine/Collision/TriggerSystem.cpp | 54 +++------------------- src/Game/PlayerSystem.cpp | 2 +- 5 files changed, 75 insertions(+), 54 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 8b1f96b2..1fcb910e 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -6,6 +6,10 @@ #include "Core/Ray.h" #include "Core/AABB.h" #include "Engine/Rendering/RawModel.h" +#include "Core/Entity.h" + +class World; +struct ComponentWrapper; namespace Collision { @@ -39,6 +43,10 @@ bool RayVsModel(const Ray& ray, bool AABBVsAABB(const AABB& a, const AABB& b); bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon = 0.0001f); +//Returns true if the entity has a boundingbox. Outputs the aabb in [outBox]. +bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFromModel = false); +bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox); + } #endif \ No newline at end of file diff --git a/include/Engine/Collision/TriggerSystem.h b/include/Engine/Collision/TriggerSystem.h index 59718018..7e6ef008 100644 --- a/include/Engine/Collision/TriggerSystem.h +++ b/include/Engine/Collision/TriggerSystem.h @@ -10,23 +10,21 @@ class AABB; -class TriggerSystem : public System +class TriggerSystem : public PureSystem { public: TriggerSystem(EventBroker* eventBroker) - : System(eventBroker, "Trigger") + : PureSystem(eventBroker, "Trigger") {} - virtual void Update(World* world, ComponentWrapper& collision, double dt) override; + virtual void UpdateComponent(World* world, ComponentWrapper& collision, double dt) override; private: std::unordered_map> m_EntitiesTouchingTrigger; std::unordered_map> m_EntitiesCompletelyInTrigger; - bool getEntityBox(World* world, EntityID id, AABB& outBox); //True if leave event was thrown. bool throwLeaveIfWasInTrigger(std::unordered_set& triggerSet, EntityID pId, EntityID tId); - void attachAABBComponentFromModel(World* world, EntityID id); template void publish(EntityID pId, EntityID tId) { diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 69bffa1a..500d59bc 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -2,6 +2,8 @@ #include "Collision/Collision.h" #include "Engine/GLM.h" +#include "Core/World.h" +#include "Rendering/Model.h" namespace Collision { @@ -176,4 +178,59 @@ bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilo (std::abs(mi1.y - mi2.y) < epsilon); } +void attachAABBComponentFromModel(World* world, EntityID id) +{ + ComponentWrapper model = world->GetComponent(id, "Model"); + ComponentWrapper collision = world->AttachComponent(id, "AABB"); + Model* modelRes = ResourceManager::Load(model["Resource"]); + + glm::mat4 modelMatrix = modelRes->m_Matrix; + + 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; +} + +bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox) +{ + ComponentWrapper& cTrans = world->GetComponent(AABBComponent.EntityID, "Transform"); + ComponentWrapper model = world->GetComponent(AABBComponent.EntityID, "Model"); + Model* modelRes = ResourceManager::Load(model["Resource"]); + outBox.CreateFromCenter(AABBComponent["BoxCenter"], AABBComponent["BoxSize"]); + glm::vec3 mini = outBox.MinCorner(); + glm::vec3 maxi = outBox.MaxCorner(); + + glm::mat4 modelMatrix = modelRes->m_Matrix * + glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) * + glm::scale((glm::vec3)cTrans["Scale"]); + + outBox = AABB(modelMatrix * glm::vec4(mini.x, mini.y, mini.z, 1), + modelMatrix * glm::vec4(maxi.x, maxi.y, maxi.z, 1)); + return true; +} + +bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFromModel) +{ + if (!world->HasComponent(entity, "AABB")) { + if (forceBoxFromModel) { + attachAABBComponentFromModel(world, entity); + } else { + return false; + } + } + + ComponentWrapper& cBox = world->GetComponent(entity, "AABB"); + return GetEntityBox(world, cBox, outBox); +} + } diff --git a/src/Engine/Collision/TriggerSystem.cpp b/src/Engine/Collision/TriggerSystem.cpp index 38a8fac4..09092461 100644 --- a/src/Engine/Collision/TriggerSystem.cpp +++ b/src/Engine/Collision/TriggerSystem.cpp @@ -3,7 +3,7 @@ #include "Core/AABB.h" #include "Rendering/Model.h" -void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt) +void TriggerSystem::UpdateComponent(World* world, ComponentWrapper& trigger, double dt) { //Currently only players can trigger things. auto players = world->GetComponents("Player"); @@ -13,14 +13,14 @@ void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt) EntityID tId = trigger.EntityID; AABB triggerBox; //The trigger *should* have a bounding box, or something, to test against so it can be triggered. - if (!getEntityBox(world, tId, triggerBox)) { + if (!Collision::GetEntityBox(world, tId, triggerBox, true)) { return; } for (auto& pc : *players) { EntityID pId = pc.EntityID; AABB playerBox; //The player can't trigger anything without an AABB. - if (!getEntityBox(world, pId, playerBox)) { + if (!Collision::GetEntityBox(world, pId, playerBox, true)) { continue; } if (!Collision::AABBVsAABB(triggerBox, playerBox)) { @@ -35,8 +35,9 @@ void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt) } else { //Entity is at least touching the trigger. AABB completelyInsideBox; - completelyInsideBox.CreateFromCenter(triggerBox.Center(), triggerBox.Size() - playerBox.Size()); - if (Collision::AABBVsAABB(completelyInsideBox, playerBox)) { + completelyInsideBox.CreateFromCenter(triggerBox.Center(), triggerBox.Size() - 2.0f * playerBox.Size()); + if (Collision::AABBVsAABB(completelyInsideBox, playerBox) && + glm::all(glm::greaterThan(triggerBox.Size(), playerBox.Size()))) { //Entity is completely inside the trigger. //If it was only touching before, it is erased. m_EntitiesTouchingTrigger[tId].erase(pId); @@ -66,23 +67,6 @@ void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt) } } -bool TriggerSystem::getEntityBox(World* world, EntityID id, AABB& outBox) -{ - //TODO: Improve checking if component exists. Remove try - bool retry; - do { - retry = false; - try { - ComponentWrapper& cBox = world->GetComponent(id, "AABB"); - outBox.CreateFromCenter(cBox["BoxCenter"], cBox["BoxSize"]); - } catch (std::out_of_range e) { - retry = true; - attachAABBComponentFromModel(world, id); - } - } while (retry); - return true; -} - bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set& triggerSet, EntityID pId, EntityID tId) { const auto& it = triggerSet.find(pId); @@ -95,29 +79,3 @@ bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set& trigg return false; } -void TriggerSystem::attachAABBComponentFromModel(World* world, EntityID id) -{ - ComponentWrapper model = world->GetComponent(id, "Model"); - ComponentWrapper transform = world->GetComponent(id, "Transform"); - ComponentWrapper collision = world->AttachComponent(id, "AABB"); - Model* modelRes = ResourceManager::Load(model["Resource"]); - - glm::mat4 modelMatrix = modelRes->m_Matrix * - glm::translate(glm::mat4(), (glm::vec3)transform["Position"]) * - glm::toMat4((glm::quat)transform["Orientation"]) * - glm::scale((glm::vec3)transform["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; -} diff --git a/src/Game/PlayerSystem.cpp b/src/Game/PlayerSystem.cpp index 41d5602c..9f7a14cf 100644 --- a/src/Game/PlayerSystem.cpp +++ b/src/Game/PlayerSystem.cpp @@ -16,7 +16,7 @@ void PlayerSystem::UpdateComponent(World * world, ComponentWrapper & player, dou } else { m_Direction.x = 0; } - m_EventBroker->Process(); + ComponentWrapper& transform = world->GetComponent(player.EntityID, "Transform"); (glm::vec3&)player["Velocity"] = m_Speed * float(dt) * m_Direction; (glm::vec3&)transform["Position"] += (glm::vec3)player["Velocity"]; From cf0ff9bfe54e0971f0de9981a1aef920db411474 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 16 Dec 2015 12:23:08 +0100 Subject: [PATCH 48/61] Simple quickly made CollisionSystem. --- include/Engine/Collision/CollisionSystem.h | 6 +++--- src/Engine/Collision/CollisionSystem.cpp | 21 ++++++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h index e7a862f7..254a2461 100644 --- a/include/Engine/Collision/CollisionSystem.h +++ b/include/Engine/Collision/CollisionSystem.h @@ -9,18 +9,18 @@ #include "Core/EventBroker.h" #include "Core/EKeyUp.h" -class CollisionSystem : public System +class CollisionSystem : public PureSystem { public: CollisionSystem(EventBroker* eventBroker) - : System(eventBroker, "AABB") + : PureSystem(eventBroker, "AABB") , zPress(false) { //TODO: Debug stuff, remove later. EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &CollisionSystem::OnKeyUp); } - virtual void Update(World* world, ComponentWrapper& cAABB, double dt) override; + virtual void UpdateComponent(World* world, ComponentWrapper& cAABB, double dt) override; private: bool zPress; diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index e1fcb0a2..ee5d4d29 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -2,16 +2,13 @@ #include "Collision/CollisionSystem.h" #include "Core/AABB.h" -void CollisionSystem::Update(World * world, ComponentWrapper & cAABB, double dt) +void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, double dt) { //cAABB is any entity that should be collideable. - m_EventBroker->Process(); - //Currently the box is translated according to the models Transform matrix, - //but not scaled (or rotated since it's AABB). This should be fine, if we don't - //want to shrink or scale up an collideable object after creation. - cAABB["BoxCenter"] = (glm::vec3)world->GetComponent(cAABB.EntityID, "Transform")["Position"]; AABB thisBox; - thisBox.CreateFromCenter(cAABB["BoxCenter"], cAABB["BoxSize"]); + if (!Collision::GetEntityBox(world, cAABB, thisBox)) { + return; + } //Here c should be an object that moves, currently only players. if (zPress) { return; @@ -21,8 +18,9 @@ void CollisionSystem::Update(World * world, ComponentWrapper & cAABB, double dt) continue; } AABB otherBox; - ComponentWrapper& aabbComp = world->GetComponent(mover.EntityID, "AABB"); - otherBox.CreateFromCenter(aabbComp["BoxCenter"], aabbComp["BoxSize"]); + if (!Collision::GetEntityBox(world, mover.EntityID, otherBox)) { + continue; + } if (Collision::AABBVsAABB(thisBox, otherBox)) { ComponentWrapper& trans = world->GetComponent(mover.EntityID, "Transform"); //TODO: Move entity to correct position on collision instead of this. Special treatment if both are movers. @@ -30,7 +28,6 @@ void CollisionSystem::Update(World * world, ComponentWrapper & cAABB, double dt) float moveSpeed = 0.12f; newPos += moveSpeed * glm::normalize(newPos - thisBox.Center()); trans["Position"] = newPos; - aabbComp["BoxCenter"] = newPos; } } } @@ -38,9 +35,7 @@ void CollisionSystem::Update(World * world, ComponentWrapper & cAABB, double dt) bool CollisionSystem::OnKeyUp(const Events::KeyUp & event) { if (event.KeyCode == GLFW_KEY_Z) { - zPress = true; - } else if (event.KeyCode == GLFW_KEY_X) { - zPress = false; + zPress = !zPress; } return false; } From 6d1cbd928ed2f84445d9390efdfcb992e0beb493 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 16 Dec 2015 16:34:27 +0100 Subject: [PATCH 49/61] Improved collision logic, still not perfect since it updates before PlayerSystem. --- include/Engine/Collision/Collision.h | 3 +++ src/Engine/Collision/Collision.cpp | 34 ++++++++++++++++++++++++ src/Engine/Collision/CollisionSystem.cpp | 17 ++++++------ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 1fcb910e..64bf2d2c 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -41,6 +41,9 @@ bool RayVsModel(const Ray& ray, //Return true if the boxes are intersecting. bool AABBVsAABB(const AABB& a, const AABB& b); +//Return true if the boxes are intersecting. +//Also outputs the minimum translation that box [a] would need in order to resolve collision. +bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation); bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon = 0.0001f); //Returns true if the entity has a boundingbox. Outputs the aabb in [outBox]. diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 500d59bc..d9cf8a0e 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -81,6 +81,40 @@ namespace Collision return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1])); } + bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation) + { + minimumTranslation = glm::vec3(0, 0, 0); + const glm::vec3& aMax = a.MaxCorner(); + const glm::vec3& bMax = b.MaxCorner(); + const glm::vec3& aMin = a.MinCorner(); + const glm::vec3& bMin = b.MinCorner(); + const glm::vec3& bSize = b.Size(); + const glm::vec3& aSize = a.Size(); + float minOffset = INFINITY; + float off; + auto axisesIntersecting = glm::tvec3(false, false, false); + for (int i = 0; i < 3; ++i) { + off = bMax[i] - aMin[i]; + if (off > 0 && off < bSize[i] + aSize[i]) { + if (off < minOffset) { + minimumTranslation = glm::vec3(); + minimumTranslation[i] = minOffset = off; + } + axisesIntersecting[i] = true; + } + off = aMax[i] - bMin[i]; + if (off > 0 && off < bSize[i] + aSize[i]) { + if (off < minOffset) { + minOffset = off; + minimumTranslation = glm::vec3(); + minimumTranslation[i] = -off; + } + axisesIntersecting[i] = true; + } + } + return glm::all(axisesIntersecting); + } + bool RayVsModel(const Ray& ray, const std::vector& modelVertices, const std::vector& modelIndices) diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index ee5d4d29..dec33a20 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -4,15 +4,18 @@ void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, double dt) { - //cAABB is any entity that should be collideable. + //TODO: Update CollisionSystem system after PlayerSystem. + + //Right now, cAABB is a component attached to any entity that should be collideable. AABB thisBox; if (!Collision::GetEntityBox(world, cAABB, thisBox)) { return; } - //Here c should be an object that moves, currently only players. + //Press 'Z' to enable/disable collision. if (zPress) { return; } + //Here, mover should be an object that moves, currently only players. for (auto& mover : *world->GetComponents("Player")) { if (cAABB.EntityID == mover.EntityID) { continue; @@ -21,13 +24,11 @@ void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, d if (!Collision::GetEntityBox(world, mover.EntityID, otherBox)) { continue; } - if (Collision::AABBVsAABB(thisBox, otherBox)) { + glm::vec3 resolveTranslation; + if (Collision::AABBVsAABB(otherBox, thisBox, resolveTranslation)) { ComponentWrapper& trans = world->GetComponent(mover.EntityID, "Transform"); - //TODO: Move entity to correct position on collision instead of this. Special treatment if both are movers. - glm::vec3 newPos = trans["Position"]; - float moveSpeed = 0.12f; - newPos += moveSpeed * glm::normalize(newPos - thisBox.Center()); - trans["Position"] = newPos; + //TODO: Special treatment if both are movers. + trans["Position"] = (glm::vec3)trans["Position"] + resolveTranslation; } } } From a23d69af9a884ef0839e09fc5827e882aeb4dee1 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 16 Dec 2015 17:08:55 +0100 Subject: [PATCH 50/61] Removed old relics from OctTree testing in OctTree. --- include/Engine/Core/OctTree.h | 8 ++---- src/Engine/Core/OctTree.cpp | 48 ----------------------------------- 2 files changed, 2 insertions(+), 54 deletions(-) diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index a3715480..c09ab63e 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -4,8 +4,6 @@ #include "Core/AABB.h" struct Ray; -class World; -class Camera; class OctTree { @@ -20,8 +18,8 @@ public: //For the root OctTree, [octTreeBounds] should be a box containing the entire level. OctTree(const AABB& octTreeBounds, int subDivisions); - //We should only ever need one OctTree in the game, and it should not need to be copied. - //Define these if the OctTree suddenly needs to be copied, think of the children OctTree* ptrs. + //We cannot copy the OctTree as of now, because of the recursive dynamic allocation. + //Define these if the OctTree suddenly needs to be copied, think of the children OctChild* ptrs. OctTree(const OctTree& other) = delete; OctTree(const OctTree&& other) = delete; OctTree& operator= (const OctTree& other) = delete; @@ -36,8 +34,6 @@ public: //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); //Returns true if the ray collides with something in the tree. Result is written to [data]. bool RayCollides(const Ray& ray, Output& data); //Returns true if the box collides with something in the tree. diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 8c262faf..31b32cec 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -4,8 +4,6 @@ #include "Core/OctTree.h" #include "Collision/Collision.h" -#include "Core/World.h" -#include "Rendering/Camera.h" namespace { @@ -151,52 +149,6 @@ OctTree::OctChild::~OctChild() } } -void OctTree::Update(float dt, World* world, Camera* cam) -{ - for (ComponentWrapper& c : *world->GetComponents("Collision")) { - AABB aabb; - aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]); - 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::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; - } - - AABB box; - auto boxPos = cam->Position() + 1.2f*cam->Forward(); - box.CreateFromCenter(boxPos, boxSize); - ComponentWrapper transform = world->GetComponent(m_BoxID, "Transform"); - transform["Position"] = boxPos; - ComponentWrapper model = world->GetComponent(m_BoxID, "Model"); - bool collBox = BoxCollides(box, AABB()); - if (collBox) { - cam->SetPosition(m_PrevPos); - cam->SetOrientation(m_PrevOri); - 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(); - ClearDynamicObjects(); -} - bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const { if (hasChildren()) { From 03d5f0dd674e486dfdbcbabced4f48875731285d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 17 Dec 2015 13:31:54 +0100 Subject: [PATCH 51/61] Added an macro IF_DEBUG_IS(x) that executes if run in debug mode, AABB has a check so max and min corners are set properly in debug mode. --- include/Engine/Common.h | 3 ++- include/Engine/Core/Util/IfDebug.h | 17 +++++++++++++++++ src/Engine/Core/AABB.cpp | 15 ++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 include/Engine/Core/Util/IfDebug.h diff --git a/include/Engine/Common.h b/include/Engine/Common.h index 2469a11d..ebdc90d0 100644 --- a/include/Engine/Common.h +++ b/include/Engine/Common.h @@ -4,4 +4,5 @@ #include #include -#include "Core/Util/Logging.h" \ No newline at end of file +#include "Core/Util/Logging.h" +#include "Core/Util/IfDebug.h" \ No newline at end of file diff --git a/include/Engine/Core/Util/IfDebug.h b/include/Engine/Core/Util/IfDebug.h new file mode 100644 index 00000000..159162c5 --- /dev/null +++ b/include/Engine/Core/Util/IfDebug.h @@ -0,0 +1,17 @@ +//Example: +//IF_DEBUG_IS(true) { +// //Stuff done only in debug mode. +//} +// +//IF_DEBUG_IS(false) { +// //Stuff done only in release mode. +//} else { +// //Stuff only in debug. +//} +#ifndef IF_DEBUG_IS +#ifndef DEBUG +#define IF_DEBUG_IS(c) if(c) +#else +#define IF_DEBUG_IS(c) if(!c) +#endif +#endif \ No newline at end of file diff --git a/src/Engine/Core/AABB.cpp b/src/Engine/Core/AABB.cpp index 05403022..b1da6b26 100644 --- a/src/Engine/Core/AABB.cpp +++ b/src/Engine/Core/AABB.cpp @@ -1,11 +1,24 @@ #include "Core/AABB.h" +#include "Common.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)) -{} +{ + IF_DEBUG_IS(true) { + if (glm::any(glm::lessThan(m_MaxCorner, m_MinCorner))) { + LOG_WARNING("AABB maxCorner coordinates are not greater than minCorner"); + m_MaxCorner.x = glm::max(m_MaxCorner.x, m_MinCorner.x); + m_MinCorner.x = glm::min(m_MaxCorner.x, m_MinCorner.x); + m_MaxCorner.y = glm::max(m_MaxCorner.y, m_MinCorner.y); + m_MinCorner.y = glm::min(m_MaxCorner.y, m_MinCorner.y); + m_MaxCorner.z = glm::max(m_MaxCorner.z, m_MinCorner.z); + m_MinCorner.z = glm::min(m_MaxCorner.z, m_MinCorner.z); + } + } +} AABB::AABB(const glm::vec4& minPos, const glm::vec4& maxPos) : AABB(glm::vec3(minPos), glm::vec3(maxPos)) From 5160a45b446a9b30a85e35161e0442010b27fcfd Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 17 Dec 2015 13:41:34 +0100 Subject: [PATCH 52/61] Made Ray into a class with checks so the direction will always be normalized. --- include/Engine/Collision/Collision.h | 4 +++ include/Engine/Core/OctTree.h | 2 +- include/Engine/Core/Ray.h | 25 +++++++++++-- src/Engine/Collision/Collision.cpp | 33 ++++++++--------- src/Engine/Core/OctTree.cpp | 2 +- src/Tests/CollisionTest.cpp | 53 ++++++++++------------------ src/Tests/OldOctTree.cpp | 2 +- src/Tests/OldOctTree.h | 2 +- 8 files changed, 66 insertions(+), 57 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 64bf2d2c..714cee3f 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -1,6 +1,10 @@ #ifndef Collision_h__ #define Collision_h__ +//NOTE: Collision.h needs to be #included before , +//because Collision #includes "RawModel.h", which has "Texture.h", which has "OpenGL.h" which must be #included first +//or you will get "fatal error C1189: #error: gl.h included before glew.h" + #include #include "Core/Ray.h" diff --git a/include/Engine/Core/OctTree.h b/include/Engine/Core/OctTree.h index c09ab63e..cdb21fbb 100644 --- a/include/Engine/Core/OctTree.h +++ b/include/Engine/Core/OctTree.h @@ -3,7 +3,7 @@ #include "Core/AABB.h" -struct Ray; +class Ray; class OctTree { diff --git a/include/Engine/Core/Ray.h b/include/Engine/Core/Ray.h index 64392cbc..2ed192a2 100644 --- a/include/Engine/Core/Ray.h +++ b/include/Engine/Core/Ray.h @@ -2,11 +2,30 @@ #define Ray_h__ #include "../GLM.h" +#include "Common.h" -struct Ray +class Ray { - glm::vec3 Origin; - glm::vec3 Direction; +public: + Ray(const glm::vec3& origin, const glm::vec3& dir) + : m_Origin(origin) + , m_Direction(glm::normalize(dir)) + { + IF_DEBUG_IS(true) { + if (glm::any(glm::isnan(m_Direction))) { + LOG_WARNING("Ray Direction was set to the zero-vector, expect unknown side effects and/or crashes."); + } + } + } + const glm::vec3& Origin() const { return m_Origin; } + const glm::vec3& Direction() const { return m_Direction; } + //Sets the ray origin at parameter. + void SetOrigin(const glm::vec3& origin) { m_Origin = origin; } + //Normalizes the parameter and sets direction to it. + void SetDirection(const glm::vec3& direction) { m_Direction = glm::normalize(direction); } +private: + glm::vec3 m_Origin; + glm::vec3 m_Direction; }; #endif // Ray_h__ diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index d9cf8a0e..ec6c471c 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -11,9 +11,9 @@ namespace Collision //note: this one hasnt been delta adjusted like RayVsAABB has bool RayAABBIntr(const Ray& ray, const AABB& box) { - glm::vec3 w = 75.0f * ray.Direction; + glm::vec3 w = 75.0f * ray.Direction(); glm::vec3 v = glm::abs(w); - glm::vec3 c = ray.Origin - box.Center() + w; + glm::vec3 c = ray.Origin() - box.Center() + w; glm::vec3 half = box.HalfSize(); if (abs(c.x) > v.x + half.x) { @@ -43,14 +43,15 @@ namespace Collision bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance) { - glm::vec3 invdir = 1.0f / ray.Direction; + glm::vec3 invdir = 1.0f / ray.Direction(); + glm::vec3 origin = ray.Origin(); - float t1 = (box.MinCorner().x - ray.Origin.x)*invdir.x; - float t2 = (box.MaxCorner().x - ray.Origin.x)*invdir.x; - float t3 = (box.MinCorner().y - ray.Origin.y)*invdir.y; - float t4 = (box.MaxCorner().y - ray.Origin.y)*invdir.y; - float t5 = (box.MinCorner().z - ray.Origin.z)*invdir.z; - float t6 = (box.MaxCorner().z - ray.Origin.z)*invdir.z; + float t1 = (box.MinCorner().x - origin.x)*invdir.x; + float t2 = (box.MaxCorner().x - origin.x)*invdir.x; + float t3 = (box.MinCorner().y - origin.y)*invdir.y; + float t4 = (box.MaxCorner().y - origin.y)*invdir.y; + float t5 = (box.MinCorner().z - origin.z)*invdir.z; + float t6 = (box.MaxCorner().z - origin.z)*invdir.z; float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6)); float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6)); @@ -123,16 +124,16 @@ namespace Collision glm::vec3 v0 = modelVertices[modelIndices[i]].Position; glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin - v0; + glm::vec3 m = ray.Origin() - v0; glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction, e2); + glm::vec3 DxE2 = glm::cross(ray.Direction(), e2); float DetInv = glm::dot(e1, DxE2); if (std::abs(DetInv) < FLT_EPSILON) { continue; } DetInv = 1.0f / DetInv; float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction, MxE1) * DetInv; + float v = glm::dot(ray.Direction(), MxE1) * DetInv; //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) { continue; @@ -158,9 +159,9 @@ namespace Collision glm::vec3 v0 = modelVertices[modelIndices[i]].Position; glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin - v0; + glm::vec3 m = ray.Origin() - v0; glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction, e2);//pVec + glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);//pVec float DetInv = glm::dot(e1, DxE2); if (std::abs(DetInv) < FLT_EPSILON) { continue; @@ -171,7 +172,7 @@ namespace Collision continue; } float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction, MxE1) * DetInv; + float v = glm::dot(ray.Direction(), MxE1) * DetInv; //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem //If u and v are positive, u+v <= 1, dist is positive, and less than closest. @@ -194,7 +195,7 @@ namespace Collision float v; float dist; bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v); - outHitPosition = ray.Origin + dist * ray.Direction; + outHitPosition = ray.Origin() + dist * ray.Direction(); return hit; } diff --git a/src/Engine/Core/OctTree.cpp b/src/Engine/Core/OctTree.cpp index 31b32cec..c1d8c64e 100644 --- a/src/Engine/Core/OctTree.cpp +++ b/src/Engine/Core/OctTree.cpp @@ -192,7 +192,7 @@ bool OctTree::OctChild::RayCollides(const Ray& ray, Output& data) const std::vector childInfos; childInfos.reserve(8); for (int i = 0; i < 8; ++i) { - childInfos.push_back({ i, glm::distance(ray.Origin, m_Children[i]->m_Box.Center()) }); + childInfos.push_back({ i, glm::distance(ray.Origin(), 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. diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 82e2e800..e6a29298 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -26,16 +26,14 @@ using boost::unit_test_framework::test_case; void RayTest(std::string fileName) { //simple box test - Ray ray; - ray.Origin = glm::vec3(-50, 0, 0); - ray.Direction = glm::normalize(glm::vec3(1, 0, 0)); + Ray ray(glm::vec3(-50, 0, 0), glm::vec3(1, 0, 0)); //using a rawmodel here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); auto unitBox = ResourceManager::Load(fileName); - BOOST_CHECK(unitBox != nullptr); + BOOST_REQUIRE(unitBox != nullptr); bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); BOOST_CHECK(hit); - ray.Direction = glm::normalize(glm::vec3(-1, 0, 0)); + ray.SetDirection(glm::vec3(-1, 0, 0)); hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); BOOST_CHECK(!hit); } @@ -49,7 +47,6 @@ BOOST_AUTO_TEST_CASE(collisionTest) //fixed seed srand(2); - Ray ray; AABB someAABB; glm::vec3 minPos; glm::vec3 maxPos; @@ -57,12 +54,10 @@ BOOST_AUTO_TEST_CASE(collisionTest) int test = 0; for (size_t i = 0; i < 10; i++) { - ray.Origin.x = rand() % 100; - ray.Origin.y = rand() % 100; - ray.Origin.z = rand() % 100; - ray.Direction.x = rand() % 100; - ray.Direction.y = rand() % 100; - ray.Direction.z = rand() % 100; + Ray ray( + glm::vec3(rand() % 100, rand() % 100, rand() % 100), + glm::vec3(rand() % 100, rand() % 100, rand() % 100) + ); minPos.x = rand() % 100; minPos.y = rand() % 100; minPos.z = rand() % 100; @@ -83,7 +78,6 @@ BOOST_AUTO_TEST_CASE(collisionTest2) { //fixed seed srand(2); - Ray ray; AABB someAABB; glm::vec3 minPos; glm::vec3 maxPos; @@ -91,12 +85,10 @@ BOOST_AUTO_TEST_CASE(collisionTest2) int test = 0; for (size_t i = 0; i < 1000000; i++) { - ray.Origin.x = rand() % 100; - ray.Origin.y = rand() % 100; - ray.Origin.z = rand() % 100; - ray.Direction.x = rand() % 100; - ray.Direction.y = rand() % 100; - ray.Direction.z = rand() % 100; + Ray ray( + glm::vec3(rand() % 100, rand() % 100, rand() % 100), + glm::vec3(rand() % 100, rand() % 100, rand() % 100) + ); minPos.x = rand() % 100; minPos.y = rand() % 100; minPos.z = rand() % 100; @@ -114,7 +106,7 @@ BOOST_AUTO_TEST_CASE(collisionTest2) BOOST_AUTO_TEST_CASE(rayVsModelTest) { //simple box test - RayTest("Models/Core/UnitBox.obj"); + RayTest("Models/Core/UnitCube.obj"); } BOOST_AUTO_TEST_CASE(rayVsModelTest2) @@ -125,7 +117,6 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) // srand(7676462); // srand(7462); srand(72); - Ray ray; AABB someAABB; glm::vec3 minPos; glm::vec3 maxPos; @@ -142,19 +133,13 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) for (size_t i = 0; i < 1000000; i++) { - ray.Origin.x = rand() % 100; - ray.Origin.y = rand() % 100; - ray.Origin.z = rand() % 100; - ray.Direction.x = rand() % 100; - ray.Direction.y = rand() % 100; - ray.Direction.z = rand() % 100; - ray.Origin /= 100; - ray.Origin = glm::vec3(-2, 0, 0); - ray.Direction /= 100; + Ray ray( + glm::vec3(-2, 0, 0), + glm::vec3(rand() % 100, rand() % 100, rand() % 100) + ); //if we normalize the ray.direction when its 0,0,0 then we get nan,nan,nan - thus we have this check to prevent that - if (ray.Direction.x < 0.0001f && ray.Direction.y < 0.0001f && ray.Direction.z < 0.0001f) + if (glm::any(glm::isnan(ray.Direction()))) continue; - ray.Direction = glm::normalize(ray.Direction); z = Collision::RayVsAABB(ray, someAABB); if (z) { @@ -224,10 +209,10 @@ BOOST_AUTO_TEST_CASE(octTest) tree.AddDynamicObject(AABB(mini, -0.9f*maxi)); OctTree::Output data; glm::vec3 origin = 3.0f * mini; - bool rayIntersected = tree.RayCollides({ origin , glm::normalize(mini - origin) }, data); + bool rayIntersected = tree.RayCollides(Ray(origin , mini - origin), data); BOOST_CHECK(rayIntersected); tree.ClearDynamicObjects(); - rayIntersected = tree.RayCollides({ origin , glm::normalize(mini - origin) }, data); + rayIntersected = tree.RayCollides(Ray(origin, mini - origin), data); BOOST_CHECK(!rayIntersected); } diff --git a/src/Tests/OldOctTree.cpp b/src/Tests/OldOctTree.cpp index 4aabf9ed..d3738ee6 100644 --- a/src/Tests/OldOctTree.cpp +++ b/src/Tests/OldOctTree.cpp @@ -172,7 +172,7 @@ bool OctTree::RayCollides(const Ray& ray, Output& data) const std::vector childInfos; childInfos.reserve(8); for (int i = 0; i < 8; ++i) { - childInfos.push_back({ i, glm::distance(ray.Origin, m_Children[i]->m_Box.Center()) }); + childInfos.push_back({ i, glm::distance(ray.Origin(), 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. diff --git a/src/Tests/OldOctTree.h b/src/Tests/OldOctTree.h index 316b9545..3b0eefdc 100644 --- a/src/Tests/OldOctTree.h +++ b/src/Tests/OldOctTree.h @@ -3,7 +3,7 @@ #include "Core/AABB.h" -struct Ray; +class Ray; class World; class Camera; From 3e2ffdb85bae2742f75abc9ce857d195b408cc8b Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 17 Dec 2015 13:48:35 +0100 Subject: [PATCH 53/61] Fixed comment on Trigger events. --- include/Engine/Collision/ETrigger.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Engine/Collision/ETrigger.h b/include/Engine/Collision/ETrigger.h index 8852ee8c..687c73fa 100644 --- a/include/Engine/Collision/ETrigger.h +++ b/include/Engine/Collision/ETrigger.h @@ -7,7 +7,7 @@ namespace Events { -/** Thrown once, when an entity is completely inside a trigger. */ +/** Thrown once, when an entity is only touching a trigger. */ struct TriggerTouch : Event { /** The id of the entity that touches the trigger. */ @@ -25,7 +25,7 @@ struct TriggerLeave : Event EntityID Trigger; }; -/** Thrown once, when an entity is completely inside a trigger. */ +/** Thrown once, when an entity is completely contained inside a trigger. */ struct TriggerEnter : Event { /** The id of the entity that entered the trigger. */ From 800065400541c4650dfbd0719d555d69f5bb2008 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 15:28:05 +0100 Subject: [PATCH 54/61] Model loading now fetches material opacity value properly --- src/Engine/Rendering/RawModel.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Engine/Rendering/RawModel.cpp b/src/Engine/Rendering/RawModel.cpp index 5aec7f22..95a75a15 100644 --- a/src/Engine/Rendering/RawModel.cpp +++ b/src/Engine/Rendering/RawModel.cpp @@ -76,13 +76,15 @@ RawModel::RawModel(std::string fileName) } // Material diffuse color - aiColor4D diffuse; + aiColor3D diffuse; material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); - desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a); + float opacity; + material->Get(AI_MATKEY_OPACITY, opacity); + desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity); // Material specular color - aiColor4D specular; + aiColor3D specular; material->Get(AI_MATKEY_COLOR_SPECULAR, specular); - desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, specular.a); + desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, 1.f); m_Vertices.push_back(desc); } From c1b9404e524138c47edf83b2965221b44642bc66 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 13:26:12 +0100 Subject: [PATCH 55/61] Created a FileDropped event for when a file is dropped onto the client by the OS --- include/Engine/Core/EFileDropped.h | 16 ++++++++++++++++ include/Engine/Core/InputManager.h | 3 +++ src/Engine/Core/InputManager.cpp | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 include/Engine/Core/EFileDropped.h diff --git a/include/Engine/Core/EFileDropped.h b/include/Engine/Core/EFileDropped.h new file mode 100644 index 00000000..1ad22a2f --- /dev/null +++ b/include/Engine/Core/EFileDropped.h @@ -0,0 +1,16 @@ +#ifndef EFileDropped_h__ +#define EFileDropped_h__ + +#include "EventBroker.h" + +namespace Events +{ + +struct FileDropped : Event +{ + std::string Path; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/InputManager.h b/include/Engine/Core/InputManager.h index 0d53169e..3b035dbe 100644 --- a/include/Engine/Core/InputManager.h +++ b/include/Engine/Core/InputManager.h @@ -16,6 +16,7 @@ #include "ELockMouse.h" #include "EGamepadAxis.h" #include "EGamepadButton.h" +#include "EFileDropped.h" class InputManager { @@ -71,6 +72,8 @@ private: static void GLFWCharCallback(GLFWwindow* window, unsigned int c); static std::vector> GLFWScrollCallbackQueue; static void GLFWScrollCallback(GLFWwindow* window, double xoffset, double yoffset); + static std::vector GLFWDropCallbackQueue; + static void GLFWDropCallback(GLFWwindow* window, int count, const char* paths[]); }; #endif diff --git a/src/Engine/Core/InputManager.cpp b/src/Engine/Core/InputManager.cpp index cb901cb8..50dcac7c 100644 --- a/src/Engine/Core/InputManager.cpp +++ b/src/Engine/Core/InputManager.cpp @@ -2,6 +2,7 @@ std::vector InputManager::GLFWCharCallbackQueue; std::vector> InputManager::GLFWScrollCallbackQueue; +std::vector InputManager::GLFWDropCallbackQueue; void InputManager::Initialize() { @@ -10,6 +11,7 @@ void InputManager::Initialize() //m_LastGamepadButtonState = std::array(); glfwSetCharCallback(m_GLFWWindow, &InputManager::GLFWCharCallback); glfwSetScrollCallback(m_GLFWWindow, &InputManager::GLFWScrollCallback); + glfwSetDropCallback(m_GLFWWindow, &InputManager::GLFWDropCallback); EVENT_SUBSCRIBE_MEMBER(m_ELockMouse, &InputManager::OnLockMouse); EVENT_SUBSCRIBE_MEMBER(m_EUnlockMouse, &InputManager::OnUnlockMouse); @@ -95,6 +97,14 @@ void InputManager::Update(double dt) } GLFWScrollCallbackQueue.clear(); + // File drop + for (auto& path : GLFWDropCallbackQueue) { + Events::FileDropped e; + e.Path = path; + m_EventBroker->Publish(e); + } + GLFWDropCallbackQueue.clear(); + // // Lock mouse while holding LMB // if (m_CurrentMouseState[GLFW_MOUSE_BUTTON_LEFT]) // { @@ -229,6 +239,14 @@ void InputManager::GLFWScrollCallback(GLFWwindow* window, double xoffset, double GLFWScrollCallbackQueue.push_back(std::make_pair(xoffset, yoffset)); } + +void InputManager::GLFWDropCallback(GLFWwindow* window, int count, const char* paths[]) +{ + for (int i = 0; i < count; i++) { + GLFWDropCallbackQueue.push_back(std::string(paths[i])); + } +} + bool InputManager::OnLockMouse(const Events::LockMouse &event) { m_MouseLocked = true; From cf6dde6c092955e135111486304716d22f9619d9 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 13:26:55 +0100 Subject: [PATCH 56/61] Boost dependency updated to version 1.60! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b04f8ed..7c5f2268 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,4 @@ Libraries that are too big to be bundled with the project. | Project | Version | License | Root folder environment variable (Windows) | | ---------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- | ------------------------------------------ | -| **[Boost](http://www.boost.org)** | 1.59.0+ | [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt) | BOOST_ROOT | +| **[Boost](http://www.boost.org)** | 1.60.0+ | [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt) | BOOST_ROOT | From a655f0bae5f428361f41ca5c8e4da689c537baba Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 13:28:31 +0100 Subject: [PATCH 57/61] Drag-and-drop asset picking and base work for showing component field tooltips in the editor --- assets | 2 +- include/Engine/Editor/EditorSystem.h | 5 ++++ src/Engine/Editor/EditorSystem.cpp | 42 +++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/assets b/assets index c5f67434..673d4a4e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c5f674349a915ab1a2b4da632d87a9832d1f6fab +Subproject commit 673d4a4e4c5a3f5bc9fedf82234e8f8751f63a44 diff --git a/include/Engine/Editor/EditorSystem.h b/include/Engine/Editor/EditorSystem.h index 021a14ca..6709775d 100644 --- a/include/Engine/Editor/EditorSystem.h +++ b/include/Engine/Editor/EditorSystem.h @@ -1,5 +1,6 @@ #include #include +#include #include "../Core/System.h" #include "../Core/EMousePress.h" #include "../Core/EMouseRelease.h" @@ -8,6 +9,7 @@ #include "../Input/EInputCommand.h" #include "../Rendering/IRenderer.h" #include "../Rendering/EPicking.h" +#include "../Core/EFileDropped.h" #include "../Rendering/RenderQueueFactory.h" class EditorSystem : public ImpureSystem @@ -50,6 +52,7 @@ private: EntityID m_Selection = 0; EntityID m_LastSelection = 0; glm::vec3 m_Position; + std::string m_LastDroppedFile; EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); @@ -61,6 +64,8 @@ private: bool OnMouseMove(const Events::MouseMove& e); EventRelay m_EPicking; bool OnPicking(const Events::Picking& e); + EventRelay m_EFileDropped; + bool OnFileDropped(const Events::FileDropped& e); void updateWidget(); void setWidgetMode(WidgetMode newMode); diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 2463aec6..32f5b162 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -19,6 +19,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer) EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &EditorSystem::OnMouseRelease); EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &EditorSystem::OnMouseMove); EVENT_SUBSCRIBE_MEMBER(m_EPicking, &EditorSystem::OnPicking); + EVENT_SUBSCRIBE_MEMBER(m_EFileDropped, &EditorSystem::OnFileDropped); } void EditorSystem::Update(World* world, double dt) @@ -36,6 +37,11 @@ void EditorSystem::Update(World* world, double dt) updateWidget(); drawUI(world, dt); + + // Clear drop queue if it wasn't handled by any UI element + if (!m_LastDroppedFile.empty()) { + m_LastDroppedFile = ""; + } } bool EditorSystem::OnInputCommand(const Events::InputCommand& e) @@ -212,6 +218,12 @@ bool EditorSystem::OnPicking(const Events::Picking& e) return true; }; +bool EditorSystem::OnFileDropped(const Events::FileDropped& e) +{ + m_LastDroppedFile = boost::filesystem::path(e.Path).lexically_relative(boost::filesystem::current_path()).string(); + std::replace(m_LastDroppedFile.begin(), m_LastDroppedFile.end(), '\\', '/'); + return true; +} void EditorSystem::updateWidget() { @@ -389,37 +401,53 @@ void EditorSystem::drawUI(World* world, double dt) const std::string& field = pair.first; const std::string& type = pair.second; + ImGui::PushID(field.c_str()); if (type == "Vector") { auto& val = component.Property(field); if (field == "Scale") { - ImGui::DragFloat3(field.c_str(), glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits::max()); + ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits::max()); } else if (field == "Orientation") { glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi())); - if (ImGui::SliderFloat3(field.c_str(), glm::value_ptr(tempVal), 0.f, glm::two_pi())) { + if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi())) { val = tempVal; } } else { - ImGui::DragFloat3(field.c_str(), glm::value_ptr(val), 0.1f, std::numeric_limits::lowest(), std::numeric_limits::max()); + ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits::lowest(), std::numeric_limits::max()); } } else if (type == "Color") { auto& val = component.Property(field); - ImGui::ColorEdit4(field.c_str(), glm::value_ptr(val), true); + ImGui::ColorEdit4("", glm::value_ptr(val), true); } else if (type == "string") { std::string& val = component.Property(field); char tempString[1024]; memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString))); - if (ImGui::InputText(field.c_str(), tempString, sizeof(tempString))) { + if (ImGui::InputText("", tempString, sizeof(tempString))) { val = std::string(tempString); LOG_DEBUG("%s::%s changed!", componentType.c_str(), field.c_str()); } + // DROP STUFF + if (ImGui::IsItemHovered() && !m_LastDroppedFile.empty()) { + val = m_LastDroppedFile; + m_LastDroppedFile = ""; + } + } else if (type == "double") { float tempVal = static_cast(component.Property(field)); - if (ImGui::InputFloat(field.c_str(), &tempVal, 0.01f, 1.f)) { + if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) { component.SetProperty(field, static_cast(tempVal)); } } else if (type == "bool") { auto& val = component.Property(field); - ImGui::Checkbox(field.c_str(), &val); + ImGui::Checkbox("", &val); + } else { + ImGui::TextDisabled(type.c_str()); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::Text(field.c_str()); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("field annotation goes here"); } } } From 00f6056f7c28631d1fdab1166e1d3850909464b6 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 14:16:08 +0100 Subject: [PATCH 58/61] Uniform scaling when dragging scaling widget origin --- src/Engine/Editor/EditorSystem.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 32f5b162..ed615a50 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -153,6 +153,13 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e) glm::vec3& scaleX = m_World->GetComponent(m_WidgetX, "Transform")["Scale"]; glm::vec3& scaleY = m_World->GetComponent(m_WidgetY, "Transform")["Scale"]; glm::vec3& scaleZ = m_World->GetComponent(m_WidgetZ, "Transform")["Scale"]; + + if (m_WidgetCurrentAxis.x > 0 && m_WidgetCurrentAxis.y > 0 && m_WidgetCurrentAxis.z > 0) { + float movementLength = glm::length(movement); + float dot = glm::dot((glm::vec3)widgetOrientation, movement); + movement = glm::vec3(movementLength) * glm::sign(dot); + (glm::vec3&)m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] += movement; + } if (m_WidgetCurrentAxis.x > 0) { scaleX.x += movement.x; } @@ -162,14 +169,19 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e) if (m_WidgetCurrentAxis.z > 0) { scaleZ.z += movement.z; } - if (m_WidgetCurrentAxis.x > 0 && m_WidgetCurrentAxis.y > 0 && m_WidgetCurrentAxis.z > 0) { - float max = glm::max(scaleX.x, glm::max(scaleY.y, scaleZ.z)); - (glm::vec3&)m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] = glm::vec3(max); - } (glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Scale"] += movement; } } + + /*LOG_DEBUG("DELTA %f", e.DeltaX); + if (e.X < 0) { + glfwSetCursorPos(m_Renderer->Window(), width - 1, e.Y); + } + if (e.X >= width) { + glfwSetCursorPos(m_Renderer->Window(), 0, e.Y); + }*/ + return true; } @@ -287,7 +299,7 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) m_World->GetComponent(m_WidgetOrigin, "Model")["Resource"] = "Models/ScaleWidgetOrigin.obj"; if (m_Selection != 0) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); - widgetTransform["Orientation"] = (glm::vec3)selectionTransform["Orientation"]; + widgetTransform["Orientation"] = glm::eulerAngles(RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection)); } } else if (newMode == WidgetMode::Rotate) { m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/RotationWidgetX.obj"; From e9128efd136b928d6c20f17a04bfbdb77437d0f2 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Thu, 17 Dec 2015 14:26:20 +0100 Subject: [PATCH 59/61] Translation widget planes --- include/Engine/Editor/EditorSystem.h | 3 +++ src/Engine/Editor/EditorSystem.cpp | 33 +++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/include/Engine/Editor/EditorSystem.h b/include/Engine/Editor/EditorSystem.h index 6709775d..779b7f97 100644 --- a/include/Engine/Editor/EditorSystem.h +++ b/include/Engine/Editor/EditorSystem.h @@ -43,8 +43,11 @@ private: EntityID m_Widget = 0; EntityID m_WidgetX = 0; + EntityID m_WidgetPlaneX = 0; EntityID m_WidgetY = 0; + EntityID m_WidgetPlaneY = 0; EntityID m_WidgetZ = 0; + EntityID m_WidgetPlaneZ = 0; EntityID m_WidgetOrigin = 0; glm::vec3 m_WidgetCurrentAxis; float m_WidgetPickingDepth = 0.f; diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index ed615a50..a43ca900 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -85,6 +85,9 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e) return false; } + if (m_Selection == 0) { + return false; + } auto widgetTransform = m_World->GetComponent(m_Widget, "Transform"); glm::vec3 widgetOrientation = widgetTransform["Orientation"]; @@ -207,9 +210,9 @@ bool EditorSystem::OnPicking(const Events::Picking& e) EntityID parent = m_World->GetParent(entity); if (parent == m_Widget) { m_WidgetCurrentAxis = glm::vec3( - (entity == m_WidgetX) || (entity == m_WidgetOrigin), - (entity == m_WidgetY) || (entity == m_WidgetOrigin), - (entity == m_WidgetZ) || (entity == m_WidgetOrigin) + (entity == m_WidgetX) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneY || entity == m_WidgetPlaneZ), + (entity == m_WidgetY) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneZ), + (entity == m_WidgetZ) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneY) ); m_WidgetPickingDepth = result.Depth; @@ -245,12 +248,24 @@ void EditorSystem::updateWidget() m_WidgetX = m_World->CreateEntity(m_Widget); m_World->AttachComponent(m_WidgetX, "Transform"); m_World->AttachComponent(m_WidgetX, "Model"); + m_WidgetPlaneX = m_World->CreateEntity(m_Widget); + m_World->AttachComponent(m_WidgetPlaneX, "Transform"); + m_World->AttachComponent(m_WidgetPlaneX, "Model"); + m_World->GetComponent(m_WidgetPlaneX, "Model")["Resource"] = "Models/WidgetPlaneX.obj"; m_WidgetY = m_World->CreateEntity(m_Widget); m_World->AttachComponent(m_WidgetY, "Transform"); m_World->AttachComponent(m_WidgetY, "Model"); + m_WidgetPlaneY = m_World->CreateEntity(m_Widget); + m_World->AttachComponent(m_WidgetPlaneY, "Transform"); + m_World->AttachComponent(m_WidgetPlaneY, "Model"); + m_World->GetComponent(m_WidgetPlaneY, "Model")["Resource"] = "Models/WidgetPlaneY.obj"; m_WidgetZ = m_World->CreateEntity(m_Widget); m_World->AttachComponent(m_WidgetZ, "Transform"); m_World->AttachComponent(m_WidgetZ, "Model"); + m_WidgetPlaneZ = m_World->CreateEntity(m_Widget); + m_World->AttachComponent(m_WidgetPlaneZ, "Transform"); + m_World->AttachComponent(m_WidgetPlaneZ, "Model"); + m_World->GetComponent(m_WidgetPlaneZ, "Model")["Resource"] = "Models/WidgetPlaneZ.obj"; m_WidgetOrigin = m_World->CreateEntity(m_Widget); m_World->AttachComponent(m_WidgetOrigin, "Transform"); m_World->AttachComponent(m_WidgetOrigin, "Model"); @@ -276,15 +291,24 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) auto widgetTransform = m_World->GetComponent(m_Widget, "Transform"); widgetTransform["Orientation"] = glm::vec3(0.f); m_World->GetComponent(m_WidgetX, "Transform")["Scale"] = glm::vec3(1.f); + m_World->GetComponent(m_WidgetPlaneX, "Model")["Visible"] = false; m_World->GetComponent(m_WidgetY, "Transform")["Scale"] = glm::vec3(1.f); + m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = false; m_World->GetComponent(m_WidgetZ, "Transform")["Scale"] = glm::vec3(1.f); + m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = false; m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] = glm::vec3(1.f); + m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false; if (newMode == WidgetMode::Translate) { m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/TranslationWidgetX.obj"; m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/TranslationWidgetY.obj"; m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/TranslationWidgetZ.obj"; - m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false; + // Temporarily disabled for local space until I can figure out what's wrong with the math + if (m_WidgetSpace != WidgetSpace::Local) { + m_World->GetComponent(m_WidgetPlaneX, "Model")["Visible"] = true; + m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = true; + m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = true; + } if (m_Selection != 0) { if (m_WidgetSpace == WidgetSpace::Local) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); @@ -305,7 +329,6 @@ void EditorSystem::setWidgetMode(WidgetMode newMode) m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/RotationWidgetX.obj"; m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/RotationWidgetY.obj"; m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/RotationWidgetZ.obj"; - m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false; if (m_Selection != 0) { auto selectionTransform = m_World->GetComponent(m_Selection, "Transform"); if (m_WidgetSpace == WidgetSpace::Local) { From 0f59b863eedb9f154c01de26c5df8aecc1cb123d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 17 Dec 2015 17:08:47 +0100 Subject: [PATCH 60/61] Renamed IF_DEBUG_IS to DEBUG_IF, slightly different behavior and added some safety checking for auto creating AABBs from models. --- include/Engine/Core/Ray.h | 2 +- include/Engine/Core/Util/IfDebug.h | 21 ++- .../Schema/Entities/CollisionTestLevel.xml | 122 ++++++++++++++++++ src/Engine/Collision/Collision.cpp | 9 +- src/Engine/Core/AABB.cpp | 18 ++- 5 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 resources/Schema/Entities/CollisionTestLevel.xml diff --git a/include/Engine/Core/Ray.h b/include/Engine/Core/Ray.h index 2ed192a2..0fcef01e 100644 --- a/include/Engine/Core/Ray.h +++ b/include/Engine/Core/Ray.h @@ -11,7 +11,7 @@ public: : m_Origin(origin) , m_Direction(glm::normalize(dir)) { - IF_DEBUG_IS(true) { + DEBUG_IF(true) { if (glm::any(glm::isnan(m_Direction))) { LOG_WARNING("Ray Direction was set to the zero-vector, expect unknown side effects and/or crashes."); } diff --git a/include/Engine/Core/Util/IfDebug.h b/include/Engine/Core/Util/IfDebug.h index 159162c5..79cb3a4c 100644 --- a/include/Engine/Core/Util/IfDebug.h +++ b/include/Engine/Core/Util/IfDebug.h @@ -1,17 +1,12 @@ -//Example: -//IF_DEBUG_IS(true) { -// //Stuff done only in debug mode. -//} -// -//IF_DEBUG_IS(false) { -// //Stuff done only in release mode. -//} else { -// //Stuff only in debug. -//} -#ifndef IF_DEBUG_IS +// Example: +// DEBUG_IF(condition) { +// // This code is executed only in debug mode and if condition is true. +// } +// NOTE: condition statement is not executed at all in release mode. +#ifndef DEBUG_IF #ifndef DEBUG -#define IF_DEBUG_IS(c) if(c) +#define DEBUG_IF(c) if(c) #else -#define IF_DEBUG_IS(c) if(!c) +#define DEBUG_IF(c) if(false) #endif #endif \ No newline at end of file diff --git a/resources/Schema/Entities/CollisionTestLevel.xml b/resources/Schema/Entities/CollisionTestLevel.xml new file mode 100644 index 00000000..99842b88 --- /dev/null +++ b/resources/Schema/Entities/CollisionTestLevel.xml @@ -0,0 +1,122 @@ + + + + + + + + + Models/DummyScene.obj + + + + + + + + + + + Models/ScaleWidget.obj + + + + + + + + + + + Models/RotationWidgetX.obj + + + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitRaptor.obj + + + + + + + + + + + + 20 + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + + + + + + Models/Core/UnitCube.obj + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index ec6c471c..dd2adec8 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -213,8 +213,11 @@ bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilo (std::abs(mi1.y - mi2.y) < epsilon); } -void attachAABBComponentFromModel(World* world, EntityID id) +bool attachAABBComponentFromModel(World* world, EntityID id) { + if (!world->HasComponent(id, "Model")) { + return false; + } ComponentWrapper model = world->GetComponent(id, "Model"); ComponentWrapper collision = world->AttachComponent(id, "AABB"); Model* modelRes = ResourceManager::Load(model["Resource"]); @@ -234,6 +237,7 @@ void attachAABBComponentFromModel(World* world, EntityID id) } collision["BoxCenter"] = 0.5f * (maxi + mini); collision["BoxSize"] = maxi - mini; + return true; } bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox) @@ -258,7 +262,8 @@ bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFrom { if (!world->HasComponent(entity, "AABB")) { if (forceBoxFromModel) { - attachAABBComponentFromModel(world, entity); + if (!attachAABBComponentFromModel(world, entity)) + return false; } else { return false; } diff --git a/src/Engine/Core/AABB.cpp b/src/Engine/Core/AABB.cpp index b1da6b26..0df56229 100644 --- a/src/Engine/Core/AABB.cpp +++ b/src/Engine/Core/AABB.cpp @@ -7,16 +7,14 @@ AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos) , m_Center(0.5f * (maxPos + minPos)) , m_HalfSize(0.5f * (maxPos - minPos)) { - IF_DEBUG_IS(true) { - if (glm::any(glm::lessThan(m_MaxCorner, m_MinCorner))) { - LOG_WARNING("AABB maxCorner coordinates are not greater than minCorner"); - m_MaxCorner.x = glm::max(m_MaxCorner.x, m_MinCorner.x); - m_MinCorner.x = glm::min(m_MaxCorner.x, m_MinCorner.x); - m_MaxCorner.y = glm::max(m_MaxCorner.y, m_MinCorner.y); - m_MinCorner.y = glm::min(m_MaxCorner.y, m_MinCorner.y); - m_MaxCorner.z = glm::max(m_MaxCorner.z, m_MinCorner.z); - m_MinCorner.z = glm::min(m_MaxCorner.z, m_MinCorner.z); - } + DEBUG_IF(glm::any(glm::lessThan(m_MaxCorner, m_MinCorner))) { + LOG_WARNING("AABB maxCorner coordinates are not greater than minCorner"); + m_MaxCorner.x = glm::max(m_MaxCorner.x, m_MinCorner.x); + m_MinCorner.x = glm::min(m_MaxCorner.x, m_MinCorner.x); + m_MaxCorner.y = glm::max(m_MaxCorner.y, m_MinCorner.y); + m_MinCorner.y = glm::min(m_MaxCorner.y, m_MinCorner.y); + m_MaxCorner.z = glm::max(m_MaxCorner.z, m_MinCorner.z); + m_MinCorner.z = glm::min(m_MaxCorner.z, m_MinCorner.z); } } From 5e0fe975f36f62b253b1f935f70cd092aba42d6d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 17 Dec 2015 17:48:00 +0100 Subject: [PATCH 61/61] Small error handling check. --- src/Engine/Collision/Collision.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index dd2adec8..d076d1c2 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -221,6 +221,9 @@ bool attachAABBComponentFromModel(World* world, EntityID id) ComponentWrapper model = world->GetComponent(id, "Model"); ComponentWrapper collision = world->AttachComponent(id, "AABB"); Model* modelRes = ResourceManager::Load(model["Resource"]); + if (modelRes == nullptr) { + return false; + } glm::mat4 modelMatrix = modelRes->m_Matrix;