diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index b792cfd69..3dac592c4 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -8,6 +8,7 @@ #ifndef NDK_COMPONENTS_GRAPHICSCOMPONENT_HPP #define NDK_COMPONENTS_GRAPHICSCOMPONENT_HPP +#include #include #include #include @@ -16,6 +17,7 @@ namespace Ndk { class GraphicsComponent; + using GraphicsComponentCullingList = Nz::CullingList; using GraphicsComponentHandle = Nz::ObjectHandle; class NDK_API GraphicsComponent : public Component, public Nz::HandledObject @@ -29,10 +31,11 @@ namespace Ndk inline GraphicsComponent(const GraphicsComponent& graphicsComponent); ~GraphicsComponent() = default; - inline void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const; + inline void AddToCullingList(GraphicsComponentCullingList* cullingList) const; + void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const; - inline void Attach(Nz::InstancedRenderableRef renderable, int renderOrder = 0); - inline void Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder = 0); + void Attach(Nz::InstancedRenderableRef renderable, int renderOrder = 0); + void Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder = 0); inline void Clear(); @@ -46,10 +49,12 @@ namespace Ndk inline const Nz::BoundingVolumef& GetBoundingVolume() const; + inline void RemoveFromCullingList(GraphicsComponentCullingList* cullingList) const; + static ComponentIndex componentIndex; private: - inline void InvalidateBoundingVolume(); + inline void InvalidateBoundingVolume() const; void InvalidateRenderableData(const Nz::InstancedRenderable* renderable, Nz::UInt32 flags, std::size_t index); inline void InvalidateRenderables(); inline void InvalidateTransformMatrix(); @@ -72,12 +77,13 @@ namespace Ndk dataUpdated(false) { } - + Renderable(Renderable&& renderable) noexcept : data(std::move(renderable.data)), renderable(std::move(renderable.renderable)), dataUpdated(renderable.dataUpdated), - renderableInvalidationSlot(std::move(renderable.renderableInvalidationSlot)), + renderableBoundingVolumeInvalidationSlot(std::move(renderable.renderableBoundingVolumeInvalidationSlot)), + renderableDataInvalidationSlot(std::move(renderable.renderableDataInvalidationSlot)), renderableReleaseSlot(std::move(renderable.renderableReleaseSlot)) { } @@ -93,13 +99,15 @@ namespace Ndk data = std::move(r.data); dataUpdated = r.dataUpdated; renderable = std::move(r.renderable); - renderableInvalidationSlot = std::move(r.renderableInvalidationSlot); + renderableBoundingVolumeInvalidationSlot = std::move(r.renderableBoundingVolumeInvalidationSlot); + renderableDataInvalidationSlot = std::move(r.renderableDataInvalidationSlot); renderableReleaseSlot = std::move(r.renderableReleaseSlot); return *this; } - NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateData, renderableInvalidationSlot); + NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateBoundingVolume, renderableBoundingVolumeInvalidationSlot); + NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateData, renderableDataInvalidationSlot); NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableRelease, renderableReleaseSlot); mutable Nz::InstancedRenderable::InstanceData data; @@ -107,6 +115,16 @@ namespace Ndk mutable bool dataUpdated; }; + using VolumeCullingListEntry = GraphicsComponentCullingList::VolumeEntry; + + struct VolumeCullingEntry + { + VolumeCullingListEntry listEntry; + + NazaraSlot(GraphicsComponentCullingList, OnCullingListRelease, cullingListReleaseSlot); + }; + + mutable std::vector m_volumeCullingEntries; std::vector m_renderables; mutable Nz::BoundingVolumef m_boundingVolume; mutable Nz::Matrix4f m_transformMatrix; diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index bd3e6dbfb..b157c84a1 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -28,52 +28,12 @@ namespace Ndk Attach(r.renderable, r.data.renderOrder); } - /*! - * \brief Adds the renderable elements to the render queue - * - * \param renderQueue Queue to be added - */ - - inline void GraphicsComponent::AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const + inline void GraphicsComponent::AddToCullingList(GraphicsComponentCullingList* cullingList) const { - EnsureTransformMatrixUpdate(); - - Ndk::RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem(); - - for (const Renderable& object : m_renderables) - { - if (!object.dataUpdated) - { - object.data.transformMatrix = Nz::Matrix4f::ConcatenateAffine(renderSystem.GetCoordinateSystemMatrix(), Nz::Matrix4f::ConcatenateAffine(object.data.localMatrix, m_transformMatrix)); - object.renderable->UpdateData(&object.data); - object.dataUpdated = true; - } - - object.renderable->AddToRenderQueue(renderQueue, object.data); - } - } - - /*! - * \brief Attaches a renderable to the entity - * - * \param renderable Reference to a renderable element - * \param renderOrder Render order of the element - */ - - inline void GraphicsComponent::Attach(Nz::InstancedRenderableRef renderable, int renderOrder) - { - return Attach(renderable, Nz::Matrix4f::Identity(), renderOrder); - } - - inline void GraphicsComponent::Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder) - { - m_renderables.emplace_back(m_transformMatrix); - Renderable& r = m_renderables.back(); - r.data.localMatrix = localMatrix; - r.data.renderOrder = renderOrder; - r.renderable = std::move(renderable); - r.renderableInvalidationSlot.Connect(r.renderable->OnInstancedRenderableInvalidateData, std::bind(&GraphicsComponent::InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size() - 1)); - r.renderableReleaseSlot.Connect(r.renderable->OnInstancedRenderableRelease, this, &GraphicsComponent::Detach); + m_volumeCullingEntries.emplace_back(VolumeCullingEntry{}); + VolumeCullingEntry& entry = m_volumeCullingEntries.back(); + entry.cullingListReleaseSlot.Connect(cullingList->OnCullingListRelease, this, &GraphicsComponent::RemoveFromCullingList); + entry.listEntry = cullingList->RegisterVolumeTest(this); InvalidateBoundingVolume(); } @@ -167,11 +127,26 @@ namespace Ndk return m_boundingVolume; } + inline void GraphicsComponent::RemoveFromCullingList(GraphicsComponentCullingList* cullingList) const + { + for (auto it = m_volumeCullingEntries.begin(); it != m_volumeCullingEntries.end(); ++it) + { + if (it->listEntry.GetParent() == cullingList) + { + if (m_volumeCullingEntries.size() > 1) + *it = std::move(m_volumeCullingEntries.back()); + + m_volumeCullingEntries.pop_back(); + break; + } + } + } + /*! * \brief Invalidates the bounding volume */ - inline void GraphicsComponent::InvalidateBoundingVolume() + inline void GraphicsComponent::InvalidateBoundingVolume() const { m_boundingVolumeUpdated = false; } @@ -192,9 +167,9 @@ namespace Ndk inline void GraphicsComponent::InvalidateTransformMatrix() { - m_boundingVolumeUpdated = false; m_transformMatrixUpdated = false; + InvalidateBoundingVolume(); InvalidateRenderables(); } } diff --git a/SDK/include/NDK/Systems/RenderSystem.hpp b/SDK/include/NDK/Systems/RenderSystem.hpp index 488d1a7d4..a9f38d950 100644 --- a/SDK/include/NDK/Systems/RenderSystem.hpp +++ b/SDK/include/NDK/Systems/RenderSystem.hpp @@ -9,9 +9,11 @@ #define NDK_SYSTEMS_RENDERSYSTEM_HPP #include +#include #include #include #include +#include #include #include #include @@ -19,8 +21,6 @@ namespace Ndk { - class GraphicsComponent; - class NDK_API RenderSystem : public System { public: @@ -51,16 +51,19 @@ namespace Ndk void OnEntityRemoved(Entity* entity) override; void OnEntityValidation(Entity* entity, bool justAdded) override; void OnUpdate(float elapsedTime) override; + void UpdateDirectionalShadowMaps(const Nz::AbstractViewer& viewer); void UpdatePointSpotShadowMaps(); std::unique_ptr m_renderTechnique; + std::vector m_volumeEntries; EntityList m_cameras; EntityList m_drawables; EntityList m_directionalLights; EntityList m_lights; EntityList m_pointSpotLights; EntityList m_particleGroups; + GraphicsComponentCullingList m_drawableCulling; Nz::BackgroundRef m_background; Nz::DepthRenderTechnique m_shadowTechnique; Nz::Matrix4f m_coordinateSystemMatrix; diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index e06890c97..af54d4f8a 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -15,6 +15,62 @@ namespace Ndk * \brief NDK class that represents the component for graphics */ + /*! + * \brief Adds the renderable elements to the render queue + * + * \param renderQueue Queue to be added + */ + void GraphicsComponent::AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const + { + EnsureTransformMatrixUpdate(); + + RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem(); + + for (const Renderable& object : m_renderables) + { + if (!object.dataUpdated) + { + object.data.transformMatrix = Nz::Matrix4f::ConcatenateAffine(renderSystem.GetCoordinateSystemMatrix(), Nz::Matrix4f::ConcatenateAffine(object.data.localMatrix, m_transformMatrix)); + object.renderable->UpdateData(&object.data); + object.dataUpdated = true; + } + + object.renderable->AddToRenderQueue(renderQueue, object.data); + } + } + + /*! + * \brief Attaches a renderable to the entity + * + * \param renderable Reference to a renderable element + * \param renderOrder Render order of the element + */ + void GraphicsComponent::Attach(Nz::InstancedRenderableRef renderable, int renderOrder) + { + return Attach(renderable, Nz::Matrix4f::Identity(), renderOrder); + } + + /*! + * \brief Attaches a renderable to the entity with a specific matrix + * + * \param renderable Reference to a renderable element + * \param localMatrix Local matrix that will be applied to the instanced renderable + * \param renderOrder Render order of the element + */ + void GraphicsComponent::Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder) + { + m_renderables.emplace_back(m_transformMatrix); + Renderable& r = m_renderables.back(); + r.data.localMatrix = localMatrix; + r.data.renderOrder = renderOrder; + r.renderable = std::move(renderable); + r.renderableBoundingVolumeInvalidationSlot.Connect(r.renderable->OnInstancedRenderableInvalidateBoundingVolume, [this] (const Nz::InstancedRenderable*) { InvalidateBoundingVolume(); }); + r.renderableDataInvalidationSlot.Connect(r.renderable->OnInstancedRenderableInvalidateData, std::bind(&GraphicsComponent::InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size() - 1)); + r.renderableReleaseSlot.Connect(r.renderable->OnInstancedRenderableRelease, this, &GraphicsComponent::Detach); + + InvalidateBoundingVolume(); + } + /*! * \brief Invalidates the data for renderable * @@ -115,10 +171,29 @@ namespace Ndk m_boundingVolume.MakeNull(); for (const Renderable& r : m_renderables) - m_boundingVolume.ExtendTo(r.renderable->GetBoundingVolume()); + { + Nz::BoundingVolumef boundingVolume = r.renderable->GetBoundingVolume(); - m_boundingVolume.Update(m_transformMatrix); + // Adjust renderable bounding volume by local matrix + if (boundingVolume.IsFinite()) + { + Nz::Boxf localBox = boundingVolume.obb.localBox; + Nz::Vector3f newPos = r.data.localMatrix * localBox.GetPosition(); + Nz::Vector3f newLengths = r.data.localMatrix * localBox.GetLengths(); + + boundingVolume.Set(Nz::Boxf(newPos.x, newPos.y, newPos.z, newLengths.x, newLengths.y, newLengths.z)); + } + + m_boundingVolume.ExtendTo(r.renderable->GetBoundingVolume()); + } + + RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem(); + + m_boundingVolume.Update(Nz::Matrix4f::ConcatenateAffine(renderSystem.GetCoordinateSystemMatrix(), m_transformMatrix)); m_boundingVolumeUpdated = true; + + for (VolumeCullingEntry& entry : m_volumeCullingEntries) + entry.listEntry.UpdateVolume(m_boundingVolume); } /*! @@ -131,8 +206,6 @@ namespace Ndk { NazaraAssert(m_entity && m_entity->HasComponent(), "GraphicsComponent requires NodeComponent"); - Ndk::RenderSystem& renderSystem = m_entity->GetWorld()->GetSystem(); - m_transformMatrix = m_entity->GetComponent().GetTransformMatrix(); m_transformMatrixUpdated = true; } diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 107676206..303caba9d 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -28,7 +28,6 @@ namespace Ndk /*! * \brief Constructs an RenderSystem object by default */ - RenderSystem::RenderSystem() : m_coordinateSystemMatrix(Nz::Matrix4f::Identity()), m_coordinateSystemInvalidated(true) @@ -43,7 +42,6 @@ namespace Ndk * * \param entity Pointer to the entity */ - void RenderSystem::OnEntityRemoved(Entity* entity) { m_cameras.Remove(entity); @@ -52,6 +50,12 @@ namespace Ndk m_lights.Remove(entity); m_particleGroups.Remove(entity); m_pointSpotLights.Remove(entity); + + if (entity->HasComponent()) + { + GraphicsComponent& gfxComponent = entity->GetComponent(); + gfxComponent.RemoveFromCullingList(&m_drawableCulling); + } } /*! @@ -60,7 +64,6 @@ namespace Ndk * \param entity Pointer to the entity * \param justAdded Is the entity newly added */ - void RenderSystem::OnEntityValidation(Entity* entity, bool justAdded) { NazaraUnused(justAdded); @@ -77,10 +80,26 @@ namespace Ndk m_cameras.Remove(entity); if (entity->HasComponent() && entity->HasComponent()) + { m_drawables.Insert(entity); + + if (justAdded) + { + GraphicsComponent& gfxComponent = entity->GetComponent(); + gfxComponent.AddToCullingList(&m_drawableCulling); + } + } else + { m_drawables.Remove(entity); + if (entity->HasComponent()) + { + GraphicsComponent& gfxComponent = entity->GetComponent(); + gfxComponent.RemoveFromCullingList(&m_drawableCulling); + } + } + if (entity->HasComponent() && entity->HasComponent()) { LightComponent& lightComponent = entity->GetComponent(); @@ -141,16 +160,19 @@ namespace Ndk //UpdateDirectionalShadowMaps(camComponent); Nz::AbstractRenderQueue* renderQueue = m_renderTechnique->GetRenderQueue(); - renderQueue->Clear(); - //TODO: Culling + // To make sure the bounding volume used by the culling list is updated for (const Ndk::EntityHandle& drawable : m_drawables) { GraphicsComponent& graphicsComponent = drawable->GetComponent(); - NodeComponent& drawableNode = drawable->GetComponent(); - - graphicsComponent.AddToRenderQueue(renderQueue); + graphicsComponent.EnsureBoundingVolumeUpdate(); } + + m_drawableCulling.Cull(camComponent.GetFrustum()); + + renderQueue->Clear(); + for (const GraphicsComponent* gfxComponent : m_drawableCulling) + gfxComponent->AddToRenderQueue(renderQueue); for (const Ndk::EntityHandle& light : m_lights) { @@ -165,7 +187,7 @@ namespace Ndk { ParticleGroupComponent& groupComponent = particleGroup->GetComponent(); - groupComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::Identity()); //< ParticleGroup doesn't use Matrix4f + groupComponent.AddToRenderQueue(renderQueue, Nz::Matrix4f::Identity()); //< ParticleGroup doesn't use any transform matrix (yet) } camComponent.ApplyView(); diff --git a/include/Nazara/Graphics/CullingList.hpp b/include/Nazara/Graphics/CullingList.hpp new file mode 100644 index 000000000..afefa37a8 --- /dev/null +++ b/include/Nazara/Graphics/CullingList.hpp @@ -0,0 +1,174 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CULLINGLIST_HPP +#define NAZARA_CULLINGLIST_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + template + class CullingList + { + public: + template class Entry; + class NoTestEntry; + class SphereEntry; + class VolumeEntry; + + template friend class Entry; + friend NoTestEntry; + friend SphereEntry; + friend VolumeEntry; + + using ResultContainer = std::vector; + + CullingList() = default; + CullingList(const CullingList& renderable) = delete; + CullingList(CullingList&& renderable) = delete; + ~CullingList(); + + std::size_t Cull(const Frustumf& frustum); + + NoTestEntry RegisterNoTest(const T* renderable); + SphereEntry RegisterSphereTest(const T* renderable); + VolumeEntry RegisterVolumeTest(const T* renderable); + + CullingList& operator=(const CullingList& renderable) = delete; + CullingList& operator=(CullingList&& renderable) = delete; + + // STL API + typename ResultContainer::iterator begin(); + typename ResultContainer::const_iterator begin() const; + + typename ResultContainer::const_iterator cbegin() const; + typename ResultContainer::const_iterator cend() const; + typename ResultContainer::const_reverse_iterator crbegin() const; + typename ResultContainer::const_reverse_iterator crend() const; + + bool empty() const; + + typename ResultContainer::iterator end(); + typename ResultContainer::const_iterator end() const; + + typename ResultContainer::reverse_iterator rbegin(); + typename ResultContainer::const_reverse_iterator rbegin() const; + + typename ResultContainer::reverse_iterator rend(); + typename ResultContainer::const_reverse_iterator rend() const; + + typename ResultContainer::size_type size() const; + + NazaraSignal(OnCullingListRelease, CullingList* /*cullingList*/); + + private: + inline void NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr); + inline void NotifyRelease(CullTest type, std::size_t index); + inline void NotifySphereUpdate(std::size_t index, const Spheref& sphere); + inline void NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume); + + struct NoTestVisibilityEntry + { + NoTestEntry* entry; + const T* renderable; + }; + + struct SphereVisibilityEntry + { + Spheref sphere; + SphereEntry* entry; + const T* renderable; + }; + + struct VolumeVisibilityEntry + { + BoundingVolumef volume; + VolumeEntry* entry; + const T* renderable; + }; + + std::vector m_noTestList; + std::vector m_sphereTestList; + std::vector m_volumeTestList; + ResultContainer m_results; + }; + + template + template + class CullingList::Entry + { + public: + Entry(); + Entry(const Entry&) = delete; + Entry(Entry&& entry); + ~Entry(); + + CullingList* GetParent() const; + + void UpdateIndex(std::size_t index); + + Entry& operator=(const Entry&) = delete; + Entry& operator=(Entry&& entry); + + protected: + Entry(CullingList* parent, std::size_t index); + + std::size_t m_index; + CullingList* m_parent; + }; + + template + class CullingList::NoTestEntry : public CullingList::Entry + { + friend CullingList; + + public: + NoTestEntry(); + + private: + NoTestEntry(CullingList* parent, std::size_t index); + }; + + template + class CullingList::SphereEntry : public CullingList::Entry + { + friend CullingList; + + public: + SphereEntry(); + + void UpdateSphere(const Spheref& sphere); + + private: + SphereEntry(CullingList* parent, std::size_t index); + }; + + template + class CullingList::VolumeEntry : public CullingList::Entry + { + friend CullingList; + + public: + VolumeEntry(); + + void UpdateVolume(const BoundingVolumef& sphere); + + private: + VolumeEntry(CullingList* parent, std::size_t index); + }; +} + +#include + +#endif // NAZARA_CULLINGLIST_HPP diff --git a/include/Nazara/Graphics/CullingList.inl b/include/Nazara/Graphics/CullingList.inl new file mode 100644 index 000000000..64650bfa3 --- /dev/null +++ b/include/Nazara/Graphics/CullingList.inl @@ -0,0 +1,363 @@ +#include "CullingList.hpp" +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +namespace Nz +{ + template + inline CullingList::~CullingList() + { + OnCullingListRelease(this); + } + + template + std::size_t CullingList::Cull(const Frustumf& frustum) + { + m_results.clear(); + + std::size_t visibleHash = 0U; + + for (const NoTestVisibilityEntry& entry : m_noTestList) + { + m_results.push_back(entry.renderable); + Nz::HashCombine(visibleHash, entry.renderable); + } + + for (const SphereVisibilityEntry& entry : m_sphereTestList) + { + if (frustum.Contains(entry.sphere)) + { + m_results.push_back(entry.renderable); + Nz::HashCombine(visibleHash, entry.renderable); + } + } + + for (const VolumeVisibilityEntry& entry : m_volumeTestList) + { + if (frustum.Contains(entry.volume)) + { + m_results.push_back(entry.renderable); + Nz::HashCombine(visibleHash, entry.renderable); + } + } + + return visibleHash; + } + + template + typename CullingList::NoTestEntry CullingList::RegisterNoTest(const T* renderable) + { + NoTestEntry entry(this, m_noTestList.size()); + m_noTestList.emplace_back(NoTestVisibilityEntry{&entry, renderable}); //< Address of entry will be updated when moving + + return entry; + } + + template + typename CullingList::SphereEntry CullingList::RegisterSphereTest(const T* renderable) + { + SphereEntry entry(this, m_sphereTestList.size()); + m_sphereTestList.emplace_back(SphereVisibilityEntry{Nz::Spheref(), &entry, renderable}); //< Address of entry will be updated when moving + + return entry; + } + + template + typename CullingList::VolumeEntry CullingList::RegisterVolumeTest(const T* renderable) + { + VolumeEntry entry(this, m_volumeTestList.size()); + m_volumeTestList.emplace_back(VolumeVisibilityEntry{Nz::BoundingVolumef(), &entry, renderable}); //< Address of entry will be updated when moving + + return entry; + } + + // Interface STD + template + typename CullingList::ResultContainer::iterator CullingList::begin() + { + return m_results.begin(); + } + + template + typename CullingList::ResultContainer::const_iterator CullingList::begin() const + { + return m_results.begin(); + } + + template + typename CullingList::ResultContainer::const_iterator CullingList::cbegin() const + { + return m_results.cbegin(); + } + + template + typename CullingList::ResultContainer::const_iterator CullingList::cend() const + { + return m_results.cend(); + } + + template + typename CullingList::ResultContainer::const_reverse_iterator CullingList::crbegin() const + { + return m_results.crbegin(); + } + + template + typename CullingList::ResultContainer::const_reverse_iterator CullingList::crend() const + { + return m_results.crend(); + } + + template + bool CullingList::empty() const + { + return m_results.empty(); + } + + template + typename CullingList::ResultContainer::iterator CullingList::end() + { + return m_results.end(); + } + + template + typename CullingList::ResultContainer::const_iterator CullingList::end() const + { + return m_results.end(); + } + + template + typename CullingList::ResultContainer::reverse_iterator CullingList::rbegin() + { + return m_results.rbegin(); + } + + template + typename CullingList::ResultContainer::const_reverse_iterator CullingList::rbegin() const + { + return m_results.rbegin(); + } + + template + typename CullingList::ResultContainer::reverse_iterator CullingList::rend() + { + return m_results.rend(); + } + + template + typename CullingList::ResultContainer::const_reverse_iterator CullingList::rend() const + { + return m_results.rend(); + } + + template + typename CullingList::ResultContainer::size_type CullingList::size() const + { + return m_results.size(); + } + + template + void CullingList::NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr) + { + switch (type) + { + case CullTest::NoTest: + { + NoTestVisibilityEntry& entry = m_noTestList[index]; + NazaraAssert(entry.entry == oldPtr, "Invalid entry"); + + entry.entry = static_cast(newPtr); + break; + } + + case CullTest::Sphere: + { + SphereVisibilityEntry& entry = m_sphereTestList[index]; + NazaraAssert(entry.entry == oldPtr, "Invalid sphere entry"); + + entry.entry = static_cast(newPtr); + break; + } + + case CullTest::Volume: + { + VolumeVisibilityEntry& entry = m_volumeTestList[index]; + NazaraAssert(entry.entry == oldPtr, "Invalid volume entry"); + + entry.entry = static_cast(newPtr); + break; + } + + default: + NazaraInternalError("Unhandled culltype"); + break; + } + } + + template + void CullingList::NotifyRelease(CullTest type, std::size_t index) + { + switch (type) + { + case CullTest::NoTest: + { + m_noTestList[index] = std::move(m_noTestList.back()); + m_noTestList[index].entry->UpdateIndex(index); + m_noTestList.pop_back(); + break; + } + + case CullTest::Sphere: + { + m_sphereTestList[index] = std::move(m_sphereTestList.back()); + m_sphereTestList[index].entry->UpdateIndex(index); + m_sphereTestList.pop_back(); + break; + } + + case CullTest::Volume: + { + m_volumeTestList[index] = std::move(m_volumeTestList.back()); + m_volumeTestList[index].entry->UpdateIndex(index); + m_volumeTestList.pop_back(); + break; + } + + default: + NazaraInternalError("Unhandled culltype"); + break; + } + } + + template + void CullingList::NotifySphereUpdate(std::size_t index, const Spheref& sphere) + { + m_sphereTestList[index].sphere = sphere; + } + + template + void CullingList::NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume) + { + m_volumeTestList[index].volume = boundingVolume; + } + + ////////////////////////////////////////////////////////////////////////// + + template + template + CullingList::Entry::Entry() : + m_parent(nullptr) + { + } + + template + template + CullingList::Entry::Entry(CullingList* parent, std::size_t index) : + m_index(index), + m_parent(parent) + { + } + + template + template + CullingList::Entry::Entry(Entry&& entry) : + m_index(entry.m_index), + m_parent(entry.m_parent) + { + if (m_parent) + m_parent->NotifyMovement(Type, m_index, &entry, this); + + entry.m_parent = nullptr; + } + + template + template + CullingList::Entry::~Entry() + { + if (m_parent) + m_parent->NotifyRelease(Type, m_index); + } + + template + template + CullingList* CullingList::Entry::GetParent() const + { + return m_parent; + } + + template + template + void CullingList::Entry::UpdateIndex(std::size_t index) + { + m_index = index; + } + + template + template + typename CullingList::Entry& CullingList::Entry::operator=(Entry&& entry) + { + m_index = entry.m_index; + m_parent = entry.m_parent; + if (m_parent) + m_parent->NotifyMovement(Type, m_index, &entry, this); + + entry.m_parent = nullptr; + + return *this; + } + + ////////////////////////////////////////////////////////////////////////// + + template + CullingList::NoTestEntry::NoTestEntry() : + Entry() + { + } + + template + CullingList::NoTestEntry::NoTestEntry(CullingList* parent, std::size_t index) : + Entry(parent, index) + { + } + + ////////////////////////////////////////////////////////////////////////// + + template + CullingList::SphereEntry::SphereEntry() : + Entry() + { + } + + template + CullingList::SphereEntry::SphereEntry(CullingList* parent, std::size_t index) : + Entry(parent, index) + { + } + + template + void CullingList::SphereEntry::UpdateSphere(const Spheref& sphere) + { + m_parent->NotifySphereUpdate(m_index, sphere); + } + + ////////////////////////////////////////////////////////////////////////// + + template + CullingList::VolumeEntry::VolumeEntry() : + Entry() + { + } + + template + CullingList::VolumeEntry::VolumeEntry(CullingList* parent, std::size_t index) : + Entry(parent, index) + { + } + + template + void Nz::CullingList::VolumeEntry::UpdateVolume(const BoundingVolumef& volume) + { + m_parent->NotifyVolumeUpdate(m_index, volume); + } +} diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index d378058f7..fd87cac6c 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -19,6 +19,13 @@ namespace Nz BackgroundType_Max = BackgroundType_User }; + enum class CullTest + { + NoTest, + Sphere, + Volume + }; + enum ProjectionType { ProjectionType_Orthogonal, diff --git a/include/Nazara/Graphics/InstancedRenderable.hpp b/include/Nazara/Graphics/InstancedRenderable.hpp index 8b44d7512..3f117c84b 100644 --- a/include/Nazara/Graphics/InstancedRenderable.hpp +++ b/include/Nazara/Graphics/InstancedRenderable.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -31,15 +32,17 @@ namespace Nz public: struct InstanceData; - InstancedRenderable() = default; + inline InstancedRenderable(); inline InstancedRenderable(const InstancedRenderable& renderable); InstancedRenderable(InstancedRenderable&& renderable) = delete; virtual ~InstancedRenderable(); + virtual void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const = 0; + + virtual bool Cull(const Frustumf& frustum, const InstanceData& instanceData) const; + inline void EnsureBoundingVolumeUpdated() const; - virtual void AddToRenderQueue(AbstractRenderQueue* renderQueue, const InstanceData& instanceData) const = 0; - virtual bool Cull(const Frustumf& frustum, const InstanceData& instanceData) const; virtual const BoundingVolumef& GetBoundingVolume() const; virtual void InvalidateData(InstanceData* instanceData, UInt32 flags) const; virtual void UpdateBoundingVolume(InstanceData* instanceData) const; @@ -49,6 +52,7 @@ namespace Nz InstancedRenderable& operator=(InstancedRenderable&& renderable) = delete; // Signals: + NazaraSignal(OnInstancedRenderableInvalidateBoundingVolume, const InstancedRenderable* /*instancedRenderable*/); NazaraSignal(OnInstancedRenderableInvalidateData, const InstancedRenderable* /*instancedRenderable*/, UInt32 /*flags*/); NazaraSignal(OnInstancedRenderableRelease, const InstancedRenderable* /*instancedRenderable*/); @@ -83,14 +87,16 @@ namespace Nz }; protected: - virtual void MakeBoundingVolume() const = 0; - void InvalidateBoundingVolume(); + inline void InvalidateBoundingVolume(); inline void InvalidateInstanceData(UInt32 flags); - inline void UpdateBoundingVolume() const; + + virtual void MakeBoundingVolume() const = 0; mutable BoundingVolumef m_boundingVolume; private: + inline void UpdateBoundingVolume() const; + mutable bool m_boundingVolumeUpdated; static InstancedRenderableLibrary::LibraryMap s_library; diff --git a/include/Nazara/Graphics/InstancedRenderable.inl b/include/Nazara/Graphics/InstancedRenderable.inl index 7f2537fbf..5ee49dc6a 100644 --- a/include/Nazara/Graphics/InstancedRenderable.inl +++ b/include/Nazara/Graphics/InstancedRenderable.inl @@ -4,12 +4,19 @@ namespace Nz { + /*! + * \brief Constructs a InstancedRenderable object by default + */ + inline InstancedRenderable::InstancedRenderable() : + m_boundingVolumeUpdated(false) + { + } + /*! * \brief Constructs a InstancedRenderable object by assignation * * \param renderable InstancedRenderable to copy into this */ - inline InstancedRenderable::InstancedRenderable(const InstancedRenderable& renderable) : RefCounted(), m_boundingVolume(renderable.m_boundingVolume), @@ -34,6 +41,8 @@ namespace Nz inline void InstancedRenderable::InvalidateBoundingVolume() { m_boundingVolumeUpdated = false; + + OnInstancedRenderableInvalidateBoundingVolume(this); } /*! @@ -69,6 +78,7 @@ namespace Nz inline void InstancedRenderable::UpdateBoundingVolume() const { MakeBoundingVolume(); + m_boundingVolumeUpdated = true; } } diff --git a/include/Nazara/Graphics/Renderable.hpp b/include/Nazara/Graphics/Renderable.hpp index f8d441955..412caa301 100644 --- a/include/Nazara/Graphics/Renderable.hpp +++ b/include/Nazara/Graphics/Renderable.hpp @@ -25,7 +25,9 @@ namespace Nz virtual ~Renderable(); virtual void AddToRenderQueue(AbstractRenderQueue* renderQueue, const Matrix4f& transformMatrix) const = 0; + virtual bool Cull(const Frustumf& frustum, const Matrix4f& transformMatrix) const; + inline void EnsureBoundingVolumeUpdated() const; virtual const BoundingVolumef& GetBoundingVolume() const; virtual void UpdateBoundingVolume(const Matrix4f& transformMatrix); @@ -36,11 +38,12 @@ namespace Nz protected: virtual void MakeBoundingVolume() const = 0; inline void InvalidateBoundingVolume(); - inline void UpdateBoundingVolume() const; mutable BoundingVolumef m_boundingVolume; private: + inline void UpdateBoundingVolume() const; + mutable bool m_boundingVolumeUpdated; }; }