From 8268513786be9f57568751e566b6511bb3e1b3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20=E2=80=98Touky=E2=80=99=20Huet?= Date: Fri, 7 Mar 2014 02:07:36 +0000 Subject: [PATCH] Added Portal system to main code base --- src/Makefile.am | 2 +- src/lol/algorithm/aabb_tree.h | 255 +++++++++++----------- src/lol/algorithm/all.h | 1 + src/lol/algorithm/portal.h | 391 ++++++++++++++++++++++++++++++++++ src/lol/debug/lines.h | 12 -- 5 files changed, 525 insertions(+), 136 deletions(-) create mode 100644 src/lol/algorithm/portal.h diff --git a/src/Makefile.am b/src/Makefile.am index 61140b11..427e1947 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,7 +42,7 @@ liblolcore_headers = \ lol/math/constants.h \ \ lol/algorithm/all.h \ - lol/algorithm/sort.h \ + lol/algorithm/sort.h lol/algorithm/portal.h lol/algorithm/aabb_tree.h \ \ lol/sys/all.h \ lol/sys/init.h lol/sys/file.h lol/sys/thread.h lol/sys/atomic.h \ diff --git a/src/lol/algorithm/aabb_tree.h b/src/lol/algorithm/aabb_tree.h index 99d9a180..1b05f37e 100644 --- a/src/lol/algorithm/aabb_tree.h +++ b/src/lol/algorithm/aabb_tree.h @@ -9,8 +9,8 @@ // http://www.wtfpl.net/ for more details. // -#if !defined __LOL_SPACE_PARTITIONING_H__ -#define __LOL_SPACE_PARTITIONING_H__ +#if !defined __LOL_AABB_TREE_H__ +#define __LOL_AABB_TREE_H__ #include #include @@ -19,6 +19,114 @@ namespace lol { //------ AABB TREE SYSTEM ----- +template class AABBTree; +template class QuadTree; +template class OcTree; + +//-- +namespace Debug { +//-- +#define GET_DRAW_DATA(DEF_TREE, DEF_BOXES, DEF_ELEM, CHILD_NB, TBB) \ +boxes.Push(tree->GetAABB(), vec4::one); \ +leaves.Push(0, boxes.Last().m1); \ +while (leaves.Count() > 0) \ +{ \ + for (int j = 0; j < tree->m_tree[leaves[0].m1].m_elements.Count(); j++) \ + { \ + bool done = false; \ + for (int k = 0; k < elements.Count(); k++) \ + { \ + if (elements[k].m1 == tree->m_elements[tree->m_tree[leaves[0].m1].m_elements[j]].m_element) \ + { \ + elements[k].m2++; \ + done = true; \ + break; \ + } \ + } \ + if (!done) \ + elements.Push(tree->m_elements[tree->m_tree[leaves[0].m1].m_elements[j]].m_element, 1, vec4::v1001); \ + } \ + \ + for (int i = 0; i < CHILD_NB; i++) \ + { \ + if (tree->m_tree[leaves[0].m1].m_children[i] != 0) \ + { \ + TBB bbox = tree->GetSubAABB(leaves[0].m2, i); \ + leaves.Push(tree->m_tree[leaves[0].m1].m_children[i], bbox); \ + boxes.Push(bbox, color); \ + } \ + } \ + leaves.Remove(0); \ +} + +//-- +template +void Draw(QuadTree* tree, vec4 color) +{ + Array boxes; + Array elements; + Array leaves; + + GET_DRAW_DATA(tree, boxes, elements, 4, box2); + + vec3 off = vec3::v010 * .1f; + vec3 add = vec3::v010 * .1f; + while (boxes.Count() > 0) + { + Debug::DrawBox(vec3(boxes[0].m1.A.x, tree->m_debug_y_offset, boxes[0].m1.A.y), + vec3(boxes[0].m1.B.x, tree->m_debug_y_offset, boxes[0].m1.B.y), + boxes[0].m2); + boxes.Remove(0); + } + while (elements.Count() > 0) + { + while (elements[0].m2 > 0) + { + Debug::DrawBox(vec3(elements[0].m1->GetAABB().A.x, tree->m_debug_y_offset, elements[0].m1->GetAABB().A.y) + off * (float)elements[0].m2, + vec3(elements[0].m1->GetAABB().B.x, tree->m_debug_y_offset, elements[0].m1->GetAABB().B.y) + off * (float)elements[0].m2, + elements[0].m3); + elements[0].m2--; + } + elements.Remove(0); + } +} +//-- +template +void Draw(OcTree* tree, vec4 color) +{ + Array boxes; + Array elements; + Array leaves; + + GET_DRAW_DATA(tree, boxes, elements, 8, box3); + + vec3 off = vec3::v010 * .1f; + vec3 add = vec3::v010 * .1f; + while (boxes.Count() > 0) + { + float size = boxes[0].m1.B.x - boxes[0].m1.A.x; + Debug::DrawBox(vec3(boxes[0].m1.A.x, boxes[0].m1.A.y, boxes[0].m1.A.z) /* + off * (m_size.x / size) */, + vec3(boxes[0].m1.B.x, boxes[0].m1.B.y, boxes[0].m1.B.z) /* + off * (m_size.x / size) */, + boxes[0].m2); + //off += add; + boxes.Remove(0); + } + while (elements.Count() > 0) + { + while (elements[0].m2 > 0) + { + Debug::DrawBox(vec3(elements[0].m1->GetAABB().A.x, elements[0].m1->GetAABB().A.y, elements[0].m1->GetAABB().A.z) + off * (float)elements[0].m2, + vec3(elements[0].m1->GetAABB().B.x, elements[0].m1->GetAABB().B.y, elements[0].m1->GetAABB().B.z) + off * (float)elements[0].m2, + elements[0].m3); + elements[0].m2--; + } + elements.Remove(0); + } +} +//-- +} + +//-- template class AABBTree { @@ -53,9 +161,7 @@ public: m_max_element = 1; AddLeaf(0); } - ~AABBTree() - { - } + ~AABBTree() { } private: //-- @@ -127,16 +233,6 @@ private: return idx; } - //-- - virtual TV GetSubOffset(int sub) = 0; - virtual TB GetAABB() { return TB(-m_size * .5f, m_size * .5f); } - virtual TB GetSubAABB(const TB& bbox, int sub) - { - TV v(GetSubOffset(sub)); - TV half_vec = (bbox.B - bbox.A) * .5f; - return TB(bbox.A + half_vec * v, - bbox.A + half_vec * (v + TV::one)); - } //-- bool TestLeaf(int leaf, const TB& leaf_bb, const TB& test_bb, Array& elements) { @@ -218,53 +314,20 @@ public: m_elements.Empty(); } - void SetSize(TV size) { m_size = size; } - void SetMaxDepth(int max_depth) { m_max_depth = max_depth; } - -#if LOL_BUILD_DEBUG - //DEBUG DRAW - virtual void DebugDraw(vec4 color) + //-- + virtual TV GetSubOffset(int sub) = 0; + virtual TB GetAABB() { return TB(-m_size * .5f, m_size * .5f); } + virtual TB GetSubAABB(const TB& bbox, int sub) { - Array leaves; - Array boxes; - Array elements; - boxes.Push(GetAABB(), vec4::one); - leaves.Push(0, boxes.Last().m1); - while (leaves.Count() > 0) - { - for (int j = 0; j < m_tree[leaves[0].m1].m_elements.Count(); j++) - { - bool done = false; - for (int k = 0; k < elements.Count(); k++) - { - if (elements[k].m1 == m_elements[m_tree[leaves[0].m1].m_elements[j]].m_element) - { - elements[k].m2++; - done = true; - break; - } - } - if (!done) - elements.Push(m_elements[m_tree[leaves[0].m1].m_elements[j]].m_element, 1, vec4::v1001); - } - - for (int i = 0; i < child_nb; i++) - { - if (m_tree[leaves[0].m1].m_children[i] != 0) - { - TB bbox = GetSubAABB(leaves[0].m2, i); - leaves.Push(m_tree[leaves[0].m1].m_children[i], bbox); - boxes.Push(bbox, color); - } - } - leaves.Remove(0); - } - DebugDrawBoxes(boxes, elements); + TV v(GetSubOffset(sub)); + TV half_vec = (bbox.B - bbox.A) * .5f; + return TB(bbox.A + half_vec * v, + bbox.A + half_vec * (v + TV::one)); } -protected: - //DEBUG DRAW - virtual void DebugDrawBoxes(Array boxes, Array elements) = 0; -#endif //LOL_BUILD_DEBUG + + //-- + void SetSize(TV size) { m_size = size; } + void SetMaxDepth(int max_depth) { m_max_depth = max_depth; } protected: Array m_tree; //actual tree @@ -275,86 +338,32 @@ protected: int m_max_element; //Maximum element per leaf }; +//-- template class QuadTree : public AABBTree { + template friend void Debug::Draw(QuadTree* tree, vec4 color); public: - QuadTree() - { -#if LOL_BUILD_DEBUG - m_debug_y_offset = 0; -#endif //LOL_BUILD_DEBUG - } -#if LOL_BUILD_DEBUG -public: + QuadTree() { m_debug_y_offset = 0.f; } + virtual ~QuadTree() { } float m_debug_y_offset; -protected: - virtual void DebugDrawBoxes(Array boxes, Array elements) - { - vec3 off = vec3::v010 * .1f; - vec3 add = vec3::v010 * .1f; - while (boxes.Count() > 0) - { - Debug::DrawBox(vec3(boxes[0].m1.A.x, m_debug_y_offset, boxes[0].m1.A.y), - vec3(boxes[0].m1.B.x, m_debug_y_offset, boxes[0].m1.B.y), - boxes[0].m2); - boxes.Remove(0); - } - while (elements.Count() > 0) - { - while (elements[0].m2 > 0) - { - Debug::DrawBox(vec3(elements[0].m1->GetAABB().A.x, m_debug_y_offset, elements[0].m1->GetAABB().A.y) + off * (float)elements[0].m2, - vec3(elements[0].m1->GetAABB().B.x, m_debug_y_offset, elements[0].m1->GetAABB().B.y) + off * (float)elements[0].m2, - elements[0].m3); - elements[0].m2--; - } - elements.Remove(0); - } - } -#endif //LOL_BUILD_DEBUG protected: virtual vec2 GetSubOffset(int sub) { return vec2(ivec2(sub % 2, sub / 2)); } }; +//-- template class OcTree : public AABBTree { + template friend void Debug::Draw(OcTree* tree, vec4 color); public: - virtual ~OcTree() {} - + OcTree() { } + virtual ~OcTree() { } protected: -#if LOL_BUILD_DEBUG - virtual void DebugDrawBoxes(Array boxes, Array elements) - { - vec3 off = vec3::v010 * .1f; - vec3 add = vec3::v010 * .1f; - while (boxes.Count() > 0) - { - float size = boxes[0].m1.B.x - boxes[0].m1.A.x; - Debug::DrawBox(vec3(boxes[0].m1.A.x, boxes[0].m1.A.y, boxes[0].m1.A.z) /* + off * (m_size.x / size) */, - vec3(boxes[0].m1.B.x, boxes[0].m1.B.y, boxes[0].m1.B.z) /* + off * (m_size.x / size) */, - boxes[0].m2); - //off += add; - boxes.Remove(0); - } - while (elements.Count() > 0) - { - while (elements[0].m2 > 0) - { - Debug::DrawBox(vec3(elements[0].m1->GetAABB().A.x, elements[0].m1->GetAABB().A.y, elements[0].m1->GetAABB().A.z) + off * (float)elements[0].m2, - vec3(elements[0].m1->GetAABB().B.x, elements[0].m1->GetAABB().B.y, elements[0].m1->GetAABB().B.z) + off * (float)elements[0].m2, - elements[0].m3); - elements[0].m2--; - } - elements.Remove(0); - } - } -#endif //LOL_BUILD_DEBUG virtual vec3 GetSubOffset(int sub) { return vec3(ivec3(sub % 2, sub / 4, (sub % 4) / 2)); } }; } /* namespace lol */ -#endif // __LOL_SPACE_PARTITIONING_H__ +#endif // __LOL_AABB_TREE_H__ diff --git a/src/lol/algorithm/all.h b/src/lol/algorithm/all.h index 5b9ab9f3..45a4880c 100644 --- a/src/lol/algorithm/all.h +++ b/src/lol/algorithm/all.h @@ -13,6 +13,7 @@ #include #include +#include #endif // __LOL_ALGORITHM_ALL_H__ diff --git a/src/lol/algorithm/portal.h b/src/lol/algorithm/portal.h new file mode 100644 index 00000000..45ad8a0b --- /dev/null +++ b/src/lol/algorithm/portal.h @@ -0,0 +1,391 @@ +// +// Lol Engine +// +// Copyright: (c) 2010-2014 Sam Hocevar +// (c) 2013-2014 Benjamin "Touky" Huet +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if !defined __LOL_PORTAL_H__ +#define __LOL_PORTAL_H__ + +#include +#include + +namespace lol +{ + +//------ PORTAL SYSTEM -------- +template class PortalRoom; +template class PortalDoor; +template class PortalSet; + +//-- +namespace Debug { +template +void Draw(PortalDoor& port, vec4 color) +{ + vec3 points[4]; port.GetPoints(points); + + //Draw normal + vec3 p = port.m_center + port.m_up * port.m_size.y * .5f; + Debug::DrawLine(p, p + port.m_normal, vec4::v1001); + Debug::DrawLine(p, p + port.m_up, vec4::v0101); + //draw door + for (int l = 0; l < 4; l++) + Debug::DrawLine(points[l], points[(l + 1) % 4], color); + Debug::DrawLine(points[0], points[2], color); + Debug::DrawLine(points[1], points[3], color); +} +} + +//PortalDoor base class +template +class PortalDoor +{ + template friend class PortalSet; + template friend class PortalRoom; + template friend void Debug::Draw(PortalDoor& port, vec4 color); + +private: + void Init() + { + m_center = vec3::zero; + m_normal = vec3::zero; + m_up = vec3::zero; + m_size = vec2::zero; + + m_view = mat4::identity; + m_proj = mat4::identity; + + m_rooms[0] = nullptr; + m_rooms[1] = nullptr; + } + +public: + //Normal portal + PortalDoor(vec3 center, vec3 normal, vec3 up, vec2 size) + { + Init(); + + m_center = center; + m_normal = normal; + m_up = up; + m_size = size; + } + //Camera portal + PortalDoor(mat4 view, mat4 proj) + { + Init(); + + m_view = view; + m_proj = proj; + } + //D.Tor + ~PortalDoor() + { + ConnectRooms(nullptr, nullptr); + } + + //Connect door to room + void ConnectRooms(class PortalRoom* front_room, class PortalRoom* back_room) + { + for (int i = 0; i < 2; i++) + if (m_rooms[i] != nullptr) + *m_rooms[i] >> this; + + m_rooms[0] = back_room; + m_rooms[1] = front_room; + + for (int i = 0; i < 2; i++) + if (m_rooms[i] != nullptr) + *m_rooms[i] << this; + } + + //-- + void DisconnectRoom(class PortalRoom* room) + { + for (int i = 0; i < 2; i++) + if (m_rooms[i] != nullptr && m_rooms[i] == room) + m_rooms[i] = nullptr; + } + + //-- + PortalRoom* GetRoom(bool front) { return m_rooms[(int)front]; } + PortalRoom* GetRoom(PortalRoom* room) { return (m_rooms[0] == room) ? m_rooms[1] : m_rooms[0]; } + + //Get Four portal point + void GetPoints(vec3 *points) const + { + vec3 right = cross(m_normal, m_up); + points[0] = m_center + right * m_size.x * .5f + m_up * m_size.y; + points[1] = m_center + right * m_size.x * .5f; + points[2] = m_center + right * m_size.x * -.5f; + points[3] = m_center + right * m_size.x * -.5f + m_up * m_size.y; + } + //Builds the portal view proj. + //Returns false if portal is out of the view or points are on each others. + bool BuildViewProj(mat4 view, mat4 proj) + { + mat4 cam_mx = proj * view; + mat4 inv_proj_mx = inverse(proj); + + //First: Check normal dot + if (lol::abs(dot((inverse(view) * vec4::v0010).xyz, m_normal)) < .00001f) + return false; + + //Second: convert in screen + vec3 port_2d[2] = { vec3(FLT_MAX), vec3(-FLT_MAX) }; + vec3 door_points[4]; + vec4 proj_p[4]; + + GetPoints(door_points); + + for (int i = 0; i < 4; i++) + { + //W to S calculations + proj_p[i] = cam_mx * vec4(door_points[i], 1.f); + proj_p[i] /= proj_p[i].w; + + //Clamp points within screen + port_2d[0] = lol::min(proj_p[i].xyz, port_2d[0]); + port_2d[1] = lol::max(proj_p[i].xyz, port_2d[1]); + port_2d[0] = vec3(lol::clamp(port_2d[0].xy, vec2::onen, vec2::one), port_2d[0].z); + port_2d[1] = vec3(lol::clamp(port_2d[1].xy, vec2::onen, vec2::one), port_2d[1].z); + } + + //Quit if door not within the screen + for (int i = 0; i < 3; i++) + if (port_2d[0][i] == port_2d[1][i]) + return false; + + //Third: Convert back to view + ivec2 j[4] = { ivec2::v00, ivec2::v01, ivec2::v11, ivec2::v10 }; + vec3 frust[2] = { vec3::one * FLT_MAX, vec3::one * -FLT_MAX }; + for (int i = 0; i < 5; i++) + { + int k = i % 4; + //world calculations + proj_p[k] = inv_proj_mx * vec4(port_2d[j[k].x].x, port_2d[j[k].y].y, (i<4)?(port_2d[0].z):(1.f), 1.f); + proj_p[k] /= proj_p[k].w; + proj_p[k].z = lol::abs(proj_p[k].z); + + for (int h = 0; h < 3; h++) + { + if (i < 4 || h > 1) + { + frust[0][h] = lol::min(frust[0][h], proj_p[k][h]); + frust[1][h] = lol::max(frust[1][h], proj_p[k][h]); + } + } + } + + //Fourth: Create frustum + m_proj = mat4::frustum(frust[0].x, frust[1].x, frust[0].y, frust[1].y, frust[0].z, frust[1].z); + m_view = view; + + return true; + } + + //View proj getter (doesn't check matrix validity) + mat4 GetViewProj() { return m_proj * m_view; } + + //-- + bool TestCollide(const vec3& point) + { + return TestPointVsFrustum(point, GetViewProj()); + } + + //-- + bool TestCollide(const PortalDoor& door) + { + vec3 door_points[4]; + vec3 res_points[4]; + ivec3 pos_test = ivec3::zero; + bool is_in = false; + + //Get points and test them on frustum + door.GetPoints(door_points); + for (int i = 0; i < 4; i++) + { + is_in = is_in || TestPointVsFrustum(door_points[i], GetViewProj(), &res_points[i]); + + if (is_in) + return true; + + //Add points on test stuff + pos_test += ivec3(lol::clamp(res_points[i], vec3::onen * 1.1f, vec3::one * 1.1f)); + } + + return false; + + //Check if at least one point is not on the same side as the others + for (int i = 0; i < 3; i++) + if (lol::abs(pos_test[i]) == 4) + return false; + + return true; + } + +private: + mat4 m_view; + mat4 m_proj; + vec3 m_center; + vec3 m_normal; + vec3 m_up; + vec2 m_size; + PortalRoom* m_rooms[2]; //0: Back, 1: Front +}; + +//-- +template +class PortalRoom +{ + template friend class PortalSet; + template friend class PortalDoor; + +public: + PortalRoom(TE* element=nullptr) + { + m_element = element; + } + ~PortalRoom() + { + for (int i = 0; i < m_doors.Count(); i++) + m_doors[i]->DisconnectRoom(this); + m_doors.Empty(); + } + + PortalRoom& operator<<(class PortalDoor* door) + { + m_doors.PushUnique(door); + return *this; + } + PortalRoom& operator>>(class PortalDoor* door) + { + m_doors.RemoveSwapItem(door); + return *this; + } + + int GetDoorCount() { return m_doors.Count(); } + PortalDoor* GetDoor(int i) { return m_doors[i]; } + +private: + //Portals associated with this room. + Array*> m_doors; + TE* m_element; +}; + +//-- +template +class PortalSet +{ +public: + ~PortalSet() + { + for (int i = 0; i < m_doors.Count(); i++) + delete m_doors[i]; + for (int i = 0; i < m_rooms.Count(); i++) + delete m_rooms[i]; + m_doors.Empty(); + m_rooms.Empty(); + } + + //Visible room getter + void GetVisibleRooms(PortalDoor* see_through, PortalRoom* start_room, Array*>& visible_rooms) + { + Array*> ignore_doors; + GetVisibleRooms(see_through, start_room, visible_rooms, ignore_doors); + #if LOL_BUILD_DEBUG + for (int j = 0; j < visible_rooms.Count(); j++) + { + vec4 tmp = vec4::zero; + for (int i = 0; i < visible_rooms[j]->m_doors.Count(); i++) + { + PortalDoor* port = visible_rooms[j]->m_doors[i]; + Debug::Draw(*port, vec4::v0111); + tmp += vec4(port->m_center, 1.f); + } + tmp /= tmp.w; + Debug::DrawBox(tmp.xyz-vec3::one,tmp.xyz+vec3::one,vec4::v1101); + } + for (int i = 0; i < ignore_doors.Count(); i++) + { + PortalDoor* port = ignore_doors[i]; + Debug::Draw(*port, vec4::v1011); + Debug::DrawViewProj(port->m_view, port->m_proj, vec4::v1011); + } + #endif //LOL_BUILD_DEBUG + } +private: + void GetVisibleRooms(PortalDoor* see_through, PortalRoom* start_room, Array*>& visible_rooms, Array*>& ignore_doors) + { + for (int i = 0; i < start_room->m_doors.Count(); i++) + { + PortalDoor* door = start_room->m_doors[i]; + if (door == see_through || ignore_doors.Find(door) != INDEX_NONE) + continue; + + if (see_through->TestCollide(*door)) + { + PortalRoom* other_room = door->GetRoom(start_room); + if (visible_rooms.Find(other_room) != INDEX_NONE) + continue; + + ignore_doors.PushUnique(door); + visible_rooms.PushUnique(other_room); + door->BuildViewProj(see_through->m_view, see_through->m_proj); + GetVisibleRooms(door, other_room, visible_rooms, ignore_doors); + } + } + } +public: + + //Operator + PortalSet& operator<<(class PortalRoom* room) + { + m_rooms.PushUnique(room); + for (int i = 0; i < room->m_doors.Count(); i++) + m_doors.PushUnique(room->m_doors[i]); + return *this; + } + //-- + PortalSet& operator>>(class PortalRoom* room) + { + for (int i = 0; i < room->m_doors.Count(); i++) + *this >> room->m_doors[i]; + m_rooms.RemoveItem(room); + return *this; + } + //-- + PortalSet& operator<<(class PortalDoor* door) + { + m_doors.PushUnique(door); + return *this; + } + //-- + PortalSet& operator>>(class PortalDoor* door) + { + m_doors.RemoveItem(door); + return *this; + } + + //-- + int GetDoorCount() { return m_doors.Count(); } + PortalDoor* GetDoor(int i) { return m_doors[i]; } + int GetRoomCount() { return m_rooms.Count(); } + PortalRoom* GetRoom(int i) { return m_rooms[i]; } + +private: + //Portals associated with this room. + Array*> m_rooms; + Array*> m_doors; +}; + +} /* namespace lol */ + +#endif // __LOL_PORTAL_H__ + diff --git a/src/lol/debug/lines.h b/src/lol/debug/lines.h index fb71df21..2df1db66 100644 --- a/src/lol/debug/lines.h +++ b/src/lol/debug/lines.h @@ -24,18 +24,6 @@ namespace lol namespace Debug { -//This funcs MUST be specialized -template -void Draw(T& x, vec4 color) -{ - ASSERT(0); -} -template