From b748deb1e1e91c3177af65e1c778454f6afd1585 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 9 Feb 2016 10:05:49 +0100 Subject: [PATCH] WIP FrustumCulling, most code added but not working now. --- include/Engine/Core/Octree.h | 117 ++++++++++++++++++++++++ include/Engine/Rendering/RenderSystem.h | 7 +- src/Engine/Rendering/RenderSystem.cpp | 53 ++++++++++- src/Game/Game.cpp | 10 +- 4 files changed, 179 insertions(+), 8 deletions(-) diff --git a/include/Engine/Core/Octree.h b/include/Engine/Core/Octree.h index 907d5736..d97d644b 100644 --- a/include/Engine/Core/Octree.h +++ b/include/Engine/Core/Octree.h @@ -2,6 +2,7 @@ #define Octree_h__ #include +#include #include "../Common.h" #include "AABB.h" @@ -40,6 +41,8 @@ public: //The type Box must be AABB, or inherit from AABB. template void ObjectsInSameRegion(const Box& box, std::vector& outObjects); + //Get the objects that are inside the frustum defined by the viewProjection matrix, the objects are put in outObjects. + void ObjectsInFrustum(const glm::mat4x4& viewProj, std::vector& outObjects); //Empty the tree of all objects, static and dynamic. void ClearObjects(); //Empty the tree of all dynamic objects. Static objects remain in the tree. @@ -68,6 +71,56 @@ struct Output float CollideDistance; }; +//Contains points P in: dot(normal, P) + d = 0 +struct Plane +{ + glm::vec3 normal; + float distance; +}; + +//A frustum defined by 6 planes. +struct Frustum +{ + enum Output + { + Inside, + Outside, + Intersects + }; + Plane planes[6]; + + Output VsAABB(const AABB& box) const + { + const glm::vec3& maxCorner = box.MaxCorner(); + const glm::vec3& minCorner = box.MinCorner(); + bool completelyInside = true; + for (const Plane& p : planes) { + bool anyWasInside = false; + bool anyWasOutside = false; + //If points are on both sides of the plane, we can stop. + for (int i = 0; i < 8 && (!anyWasInside || !anyWasOutside); ++i) { + std::bitset<3> bits(i); + glm::vec3 corner; + corner.x = bits.test(0) ? maxCorner.x : minCorner.x; + corner.y = bits.test(1) ? maxCorner.y : minCorner.y; + corner.z = bits.test(2) ? maxCorner.z : minCorner.z; + if (glm::dot(p.normal, corner) > p.distance) { + anyWasInside = true; + } else { + anyWasOutside = true; + } + } + if (!anyWasInside) { + return Outside; + } + if (anyWasOutside) { + completelyInside = false; + } + } + return completelyInside ? Inside : Intersects; + } +}; + struct ContainedObject { ContainedObject() @@ -97,6 +150,8 @@ struct Child void AddStaticObject(const AABB& box); template void ObjectsInSameRegion(const Box& box, std::vector& outObjects) const; + template + void ObjectsInFrustum(const Frustum& frustum, std::vector& outObjects, bool takeAllDontTest) const; void ClearObjects(); void ClearDynamicObjects(); bool RayCollides(const Ray& ray, Output& data) const; @@ -154,6 +209,26 @@ void Octree::ObjectsInSameRegion(const Box& box, std::vector& outObjects) m_Root->ObjectsInSameRegion(box, outObjects); } +template +void Octree::ObjectsInFrustum(const glm::mat4x4& viewProj, std::vector& outObjects) +{ + falsifyObjectChecks(); + OctSpace::Frustum frustum; + for (int i = 0; i < 6; ++i) { + int sign = 2 * (i % 2) - 1; + int index = i / 2; + OctSpace::Plane& plane = frustum.planes[i]; + plane.normal.x = viewProj[0].w + sign * viewProj[0][index]; + plane.normal.y = viewProj[1].w + sign * viewProj[1][index]; + plane.normal.z = viewProj[2].w + sign * viewProj[2][index]; + plane.distance = viewProj[3].w + sign * viewProj[3][index]; + float divByNormalLength = 1.0f / glm::length(plane.normal); + plane.normal *= divByNormalLength; + plane.distance *= divByNormalLength; + } + m_Root->ObjectsInFrustum(frustum, outObjects, false); +} + template void Octree::ClearObjects() { @@ -230,4 +305,46 @@ void OctSpace::Child::ObjectsInSameRegion(const Box& box, std::vector& outObj } } +template +void OctSpace::Child::ObjectsInFrustum(const Frustum& frustum, std::vector& outObjects, bool takeAllDontTest) const +{ + if (hasChildren()) { + for (const Child* c : m_Children) { + Frustum::Output out = Frustum::Inside; + if (!takeAllDontTest) { + out = frustum.VsAABB(c->m_Box); + if (out == Frustum::Outside) { + continue; + } + } + c->ObjectsInFrustum(frustum, outObjects, out == Frustum::Inside); + } + } else { + size_t startIndex = outObjects.size(); + int numDuplicates = 0; + outObjects.resize(outObjects.size() + m_StaticObjIndices.size() + m_DynamicObjIndices.size()); + for (size_t i = 0; i < m_StaticObjIndices.size(); ++i) { + ContainedObject& obj = m_StaticObjectsRef[m_StaticObjIndices[i]]; + if (obj.Checked || !frustum.VsAABB(obj.Box)) { + ++numDuplicates; + } else { + obj.Checked = true; + outObjects[startIndex + i - numDuplicates] = *static_cast(obj.Box.get()); + } + } + for (size_t i = 0; i < m_DynamicObjIndices.size(); ++i) { + ContainedObject& obj = m_DynamicObjectsRef[m_DynamicObjIndices[i]]; + if (obj.Checked || !frustum.VsAABB(obj.Box)) { + ++numDuplicates; + } else { + obj.Checked = true; + outObjects[startIndex + i - numDuplicates] = *static_cast(obj.Box.get()); + } + } + for (size_t i = 0; i < numDuplicates; ++i) { + outObjects.pop_back(); + } + } +} + #endif \ No newline at end of file diff --git a/include/Engine/Rendering/RenderSystem.h b/include/Engine/Rendering/RenderSystem.h index d64147b9..d404c56a 100644 --- a/include/Engine/Rendering/RenderSystem.h +++ b/include/Engine/Rendering/RenderSystem.h @@ -16,11 +16,13 @@ #include "PointLightJob.h" #include "../Core/Transform.h" #include "../Core/EPlayerSpawned.h" +#include "../Core/Octree.h" +#include "../Collision/EntityAABB.h" class RenderSystem : public ImpureSystem { public: - RenderSystem(World* world, EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame); + RenderSystem(World* world, EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame, Octree* frustumCullOctree); ~RenderSystem(); virtual void Update(double dt) override; @@ -29,9 +31,12 @@ private: const IRenderer* m_Renderer; RenderFrame* m_RenderFrame; Camera* m_Camera; + Camera* m_LastCullCamera; + Camera** m_FrustumCamPtr; World* m_World; EntityWrapper m_CurrentCamera = EntityWrapper::Invalid; EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; + Octree* m_Octree; EventRelay m_ESetCamera; bool OnSetCamera(Events::SetCamera &event); diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp index eaecf99e..d57e263a 100644 --- a/src/Engine/Rendering/RenderSystem.cpp +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -1,21 +1,26 @@ #include "Rendering/RenderSystem.h" +#include "Collision/Collision.h" -RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRenderer* renderer, RenderFrame* renderFrame) +RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRenderer* renderer, RenderFrame* renderFrame, Octree* frustumCullOctree) : System(world, eventBroker) , m_Renderer(renderer) , m_RenderFrame(renderFrame) , m_World(world) + , m_Octree(frustumCullOctree) { EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &RenderSystem::OnPlayerSpawned); m_Camera = new Camera((float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height, glm::radians(45.f), 0.01f, 5000.f); + m_LastCullCamera = new Camera(*m_Camera); + m_FrustumCamPtr = &m_Camera; } RenderSystem::~RenderSystem() { delete m_Camera; + delete m_LastCullCamera; } bool RenderSystem::OnSetCamera(Events::SetCamera& e) @@ -28,6 +33,17 @@ bool RenderSystem::OnSetCamera(Events::SetCamera& e) m_Camera->SetPosition(cTransform["Position"]); m_Camera->SetOrientation(glm::quat((const glm::vec3&)cTransform["Orientation"])); m_CurrentCamera = e.CameraEntity; + //Right now, lets set the camera to cull away stuff if it is connected to anything. + //TODO: This won't work with spectators, or death anim. + if (m_CurrentCamera.Parent().Valid()) { + //Copy the camera into the last frustum camera, without allocating new memory. + new ((void*)m_LastCullCamera) Camera(*m_Camera); + m_FrustumCamPtr = &m_Camera; + } else { + //If the camera has no parents, i.e. a free camera, + //then we cull from the last camera, so we can see if the culling works. + m_FrustumCamPtr = &m_LastCullCamera; + } return true; } @@ -42,11 +58,33 @@ bool RenderSystem::isChildOfCurrentCamera(EntityWrapper entity) void RenderSystem::fillModels(std::list>& opaqueJobs, std::list>& transparentJobs) { + + std::vector seenEntities; + //m_Octree->ObjectsInFrustum((*m_FrustumCamPtr)->ProjectionMatrix() * (*m_FrustumCamPtr)->ViewMatrix(), seenEntities); + //m_Octree->ObjectsInFrustum((*m_FrustumCamPtr)->ViewMatrix() * (*m_FrustumCamPtr)->ProjectionMatrix(), seenEntities); + + glm::mat4x4 viewProj = (*m_FrustumCamPtr)->ViewMatrix() * (*m_FrustumCamPtr)->ProjectionMatrix(); + OctSpace::Frustum frustum; + for (int i = 0; i < 6; ++i) { + int sign = 2 * (i % 2) - 1; + int index = i / 2; + OctSpace::Plane& plane = frustum.planes[i]; + plane.normal.x = viewProj[0].w + sign * viewProj[0][index]; + plane.normal.y = viewProj[1].w + sign * viewProj[1][index]; + plane.normal.z = viewProj[2].w + sign * viewProj[2][index]; + plane.distance = viewProj[3].w + sign * viewProj[3][index]; + float divByNormalLength = 1.0f / glm::length(plane.normal); + plane.normal *= divByNormalLength; + plane.distance *= divByNormalLength; + } + + //for (auto& seenEntity : seenEntities) { + // EntityWrapper entity = seenEntity.Entity; + // ComponentWrapper cModel = entity["Model"]; auto models = m_World->GetComponents("Model"); if (models == nullptr) { return; } - for (auto& cModel : *models) { bool visible = cModel["Visible"]; if (!visible) { @@ -57,7 +95,7 @@ void RenderSystem::fillModels(std::list>& opaqueJobs, continue; } - EntityWrapper entity(m_World, cModel.EntityID); + EntityWrapper entity = EntityWrapper(m_World, cModel.EntityID); // Only render children of a camera if that camera is currently active if (isChildOfACamera(entity) && !isChildOfCurrentCamera(entity)) { @@ -69,6 +107,15 @@ void RenderSystem::fillModels(std::list>& opaqueJobs, continue; } + if (entity.HasComponent("AABB")) { + OctSpace::Frustum::Output o = frustum.VsAABB(*Collision::EntityAbsoluteAABB(entity)); + if (o == OctSpace::Frustum::Outside) { + continue; + } + } else { + continue; + } + Model* model; try { model = ResourceManager::Load<::Model, true>(resource); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 0c92271d..d3c2eb57 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -75,9 +75,10 @@ Game::Game(int argc, char* argv[]) // Create Octrees - m_OctreeCollision = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); - m_OctreeTrigger = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); - m_OctreeFrustrumCulling = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); + AABB boxContainingTheWorld(glm::vec3(-300), glm::vec3(300)); + m_OctreeCollision = new Octree(boxContainingTheWorld, 4); + m_OctreeTrigger = new Octree(boxContainingTheWorld, 4); + m_OctreeFrustrumCulling = new Octree(boxContainingTheWorld, 4); // Create system pipeline m_SystemPipeline = new SystemPipeline(m_World, m_EventBroker); @@ -99,6 +100,7 @@ Game::Game(int argc, char* argv[]) ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision, "Collidable"); m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeTrigger, "Player"); + m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeFrustrumCulling, "Model"); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); @@ -107,7 +109,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeTrigger); ++updateOrderLevel; - m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame); + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame, m_OctreeFrustrumCulling); ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame);