diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 56c19abfa..e79c238b4 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -217,7 +217,10 @@ namespace Ndk if (camComponent.UpdateVisibility(visibilityHash) || m_forceRenderQueueInvalidation || forceInvalidation) { renderQueue->Clear(); - for (const GraphicsComponent* gfxComponent : m_drawableCulling) + for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetFullyVisibleResults()) + gfxComponent->AddToRenderQueue(renderQueue); + + for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetPartiallyVisibleResults()) gfxComponent->AddToRenderQueue(renderQueue); for (const Ndk::EntityHandle& light : m_lights) diff --git a/include/Nazara/Graphics/CullingList.hpp b/include/Nazara/Graphics/CullingList.hpp index edc6e1621..7feb32362 100644 --- a/include/Nazara/Graphics/CullingList.hpp +++ b/include/Nazara/Graphics/CullingList.hpp @@ -23,11 +23,13 @@ namespace Nz { public: template class Entry; + class BoxEntry; class NoTestEntry; class SphereEntry; class VolumeEntry; template friend class Entry; + friend BoxEntry; friend NoTestEntry; friend SphereEntry; friend VolumeEntry; @@ -43,6 +45,10 @@ namespace Nz std::size_t FillWithAllEntries(bool* forceInvalidation = nullptr); + const ResultContainer& GetFullyVisibleResults() const; + const ResultContainer& GetPartiallyVisibleResults() const; + + BoxEntry RegisterBoxTest(const T* renderable); NoTestEntry RegisterNoTest(const T* renderable); SphereEntry RegisterSphereTest(const T* renderable); VolumeEntry RegisterVolumeTest(const T* renderable); @@ -50,37 +56,24 @@ namespace Nz 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 NotifyBoxUpdate(std::size_t index, const Boxf& boundingVolume); inline void NotifyForceInvalidation(CullTest type, std::size_t index); 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 BoxVisibilityEntry + { + Boxf box; + BoxEntry* entry; + const T* renderable; + bool forceInvalidation; + }; + struct NoTestVisibilityEntry { NoTestEntry* entry; @@ -104,10 +97,12 @@ namespace Nz bool forceInvalidation; }; + std::vector m_boxTestList; std::vector m_noTestList; std::vector m_sphereTestList; std::vector m_volumeTestList; - ResultContainer m_results; + ResultContainer m_fullyVisibleResults; + ResultContainer m_partiallyVisibleResults; }; template @@ -135,6 +130,24 @@ namespace Nz std::size_t m_index; CullingList* m_parent; }; + + template + class CullingList::BoxEntry : public CullingList::template Entry + { + friend CullingList; + + public: + BoxEntry(); + BoxEntry(BoxEntry&&) = default; + ~BoxEntry() = default; + + void UpdateBox(const Boxf& box); + + BoxEntry& operator=(BoxEntry&&) = default; + + private: + BoxEntry(CullingList* parent, std::size_t index); + }; template class CullingList::NoTestEntry : public CullingList::template Entry diff --git a/include/Nazara/Graphics/CullingList.inl b/include/Nazara/Graphics/CullingList.inl index fb278d019..1230ce9e9 100644 --- a/include/Nazara/Graphics/CullingList.inl +++ b/include/Nazara/Graphics/CullingList.inl @@ -16,16 +16,48 @@ namespace Nz template std::size_t CullingList::Cull(const Frustumf& frustum, bool* forceInvalidation) { - m_results.clear(); + m_fullyVisibleResults.clear(); + m_partiallyVisibleResults.clear(); bool forcedInvalidation = false; - std::size_t visibleHash = 0U; + std::size_t fullyVisibleHash = 5U; + std::size_t partiallyVisibleHash = 5U; + + auto CombineHash = [](std::size_t currentHash, std::size_t newHash) + { + return currentHash * 23 + newHash; + }; + + for (BoxVisibilityEntry& entry : m_boxTestList) + { + switch (frustum.Intersect(entry.box)) + { + case IntersectionSide_Inside: + m_fullyVisibleResults.push_back(entry.renderable); + CombineHash(fullyVisibleHash, std::hash()(entry.renderable)); + + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; + entry.forceInvalidation = false; + break; + + case IntersectionSide_Intersecting: + m_partiallyVisibleResults.push_back(entry.renderable); + CombineHash(partiallyVisibleHash, std::hash()(entry.renderable)); + + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; + entry.forceInvalidation = false; + break; + + case IntersectionSide_Outside: + break; + } + } for (NoTestVisibilityEntry& entry : m_noTestList) { - m_results.push_back(entry.renderable); - Nz::HashCombine(visibleHash, entry.renderable); + m_fullyVisibleResults.push_back(entry.renderable); + CombineHash(fullyVisibleHash, std::hash()(entry.renderable)); if (entry.forceInvalidation) { @@ -36,34 +68,87 @@ namespace Nz for (SphereVisibilityEntry& entry : m_sphereTestList) { - if (frustum.Contains(entry.sphere)) + switch (frustum.Intersect(entry.sphere)) { - m_results.push_back(entry.renderable); - Nz::HashCombine(visibleHash, entry.renderable); + case IntersectionSide_Inside: + m_fullyVisibleResults.push_back(entry.renderable); + CombineHash(fullyVisibleHash, std::hash()(entry.renderable)); - if (entry.forceInvalidation) - { - forcedInvalidation = true; + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; entry.forceInvalidation = false; - } + break; + + case IntersectionSide_Intersecting: + m_partiallyVisibleResults.push_back(entry.renderable); + CombineHash(partiallyVisibleHash, std::hash()(entry.renderable)); + + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; + entry.forceInvalidation = false; + break; + + case IntersectionSide_Outside: + break; } } - + for (VolumeVisibilityEntry& entry : m_volumeTestList) { - if (frustum.Contains(entry.volume)) + switch (frustum.Intersect(entry.volume)) { - m_results.push_back(entry.renderable); - Nz::HashCombine(visibleHash, entry.renderable); + case IntersectionSide_Inside: + m_fullyVisibleResults.push_back(entry.renderable); + CombineHash(fullyVisibleHash, std::hash()(entry.renderable)); - if (entry.forceInvalidation) - { - forcedInvalidation = true; + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; entry.forceInvalidation = false; - } + break; + + case IntersectionSide_Intersecting: + m_partiallyVisibleResults.push_back(entry.renderable); + CombineHash(partiallyVisibleHash, std::hash()(entry.renderable)); + + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; + entry.forceInvalidation = false; + break; + + case IntersectionSide_Outside: + break; } } + if (forceInvalidation) + *forceInvalidation = forcedInvalidation; + + return 5 + partiallyVisibleHash * 17 + fullyVisibleHash; + } + + template + std::size_t CullingList::FillWithAllEntries(bool* forceInvalidation) + { + m_fullyVisibleResults.clear(); + m_partiallyVisibleResults.clear(); + + bool forcedInvalidation = false; + + std::size_t visibleHash = 5U; + + auto FillWithList = [&](auto& testList) + { + for (auto& entry : testList) + { + m_fullyVisibleResults.push_back(entry.renderable); + visibleHash = visibleHash * 23 + std::hash()(entry.renderable); + + forcedInvalidation = forcedInvalidation | entry.forceInvalidation; + entry.forceInvalidation = false; + } + }; + + FillWithList(m_boxTestList); + FillWithList(m_noTestList); + FillWithList(m_sphereTestList); + FillWithList(m_volumeTestList); + if (forceInvalidation) *forceInvalidation = forcedInvalidation; @@ -71,53 +156,24 @@ namespace Nz } template - std::size_t CullingList::FillWithAllEntries(bool* forceInvalidation) + auto CullingList::GetFullyVisibleResults() const -> const ResultContainer& { - m_results.clear(); + return m_fullyVisibleResults; + } - bool forcedInvalidation = false; + template + auto CullingList::GetPartiallyVisibleResults() const -> const ResultContainer& + { + return m_partiallyVisibleResults; + } - std::size_t visibleHash = 0U; - for (NoTestVisibilityEntry& entry : m_noTestList) - { - m_results.push_back(entry.renderable); - Nz::HashCombine(visibleHash, entry.renderable); + template + auto CullingList::RegisterBoxTest(const T* renderable) -> BoxEntry + { + BoxEntry newEntry(this, m_boxTestList.size()); + m_boxTestList.emplace_back(BoxVisibilityEntry{ Nz::Boxf(), &newEntry, renderable, false }); //< Address of entry will be updated when moving - if (entry.forceInvalidation) - { - forcedInvalidation = true; - entry.forceInvalidation = false; - } - } - - for (SphereVisibilityEntry& entry : m_sphereTestList) - { - m_results.push_back(entry.renderable); - Nz::HashCombine(visibleHash, entry.renderable); - - if (entry.forceInvalidation) - { - forcedInvalidation = true; - entry.forceInvalidation = false; - } - } - - for (VolumeVisibilityEntry& entry : m_volumeTestList) - { - m_results.push_back(entry.renderable); - Nz::HashCombine(visibleHash, entry.renderable); - - if (entry.forceInvalidation) - { - forcedInvalidation = true; - entry.forceInvalidation = false; - } - } - - if (forceInvalidation) - *forceInvalidation = forcedInvalidation; - - return visibleHash; + return newEntry; } template @@ -132,7 +188,7 @@ namespace Nz template auto CullingList::RegisterSphereTest(const T* renderable) -> SphereEntry { - SphereEntry newEntry(this, m_sphereTestList.size() - 1); + SphereEntry newEntry(this, m_sphereTestList.size()); m_sphereTestList.emplace_back(SphereVisibilityEntry{Nz::Spheref(), &newEntry, renderable, false}); //< Address of entry will be updated when moving return newEntry; @@ -147,89 +203,10 @@ namespace Nz return newEntry; } - // Interface STD template - typename CullingList::ResultContainer::iterator CullingList::begin() + inline void CullingList::NotifyBoxUpdate(std::size_t index, const Boxf& box) { - 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(); + m_boxTestList[index].box = box; } template @@ -237,6 +214,12 @@ namespace Nz { switch (type) { + case CullTest::Box: + { + m_boxTestList[index].forceInvalidation = true; + break; + } + case CullTest::NoTest: { m_noTestList[index].forceInvalidation = true; @@ -268,6 +251,15 @@ namespace Nz switch (type) { + case CullTest::Box: + { + BoxVisibilityEntry& entry = m_boxTestList[index]; + NazaraAssert(entry.entry == oldPtr, "Invalid box entry"); + + entry.entry = static_cast(newPtr); + break; + } + case CullTest::NoTest: { NoTestVisibilityEntry& entry = m_noTestList[index]; @@ -306,6 +298,14 @@ namespace Nz { switch (type) { + case CullTest::Box: + { + m_boxTestList[index] = std::move(m_boxTestList.back()); + m_boxTestList[index].entry->UpdateIndex(index); + m_boxTestList.pop_back(); + break; + } + case CullTest::NoTest: { m_noTestList[index] = std::move(m_noTestList.back()); @@ -424,6 +424,26 @@ namespace Nz return *this; } + + ////////////////////////////////////////////////////////////////////////// + + template + CullingList::BoxEntry::BoxEntry() : + Entry() + { + } + + template + CullingList::BoxEntry::BoxEntry(CullingList* parent, std::size_t index) : + Entry(parent, index) + { + } + + template + void CullingList::BoxEntry::UpdateBox(const Boxf& box) + { + this->m_parent->NotifyBoxUpdate(this->m_index, box); + } ////////////////////////////////////////////////////////////////////////// diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index 83f46dc22..277d78a03 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -21,6 +21,7 @@ namespace Nz enum class CullTest { + Box, NoTest, Sphere, Volume