diff --git a/SDK/include/NDK/Components/GraphicsComponent.hpp b/SDK/include/NDK/Components/GraphicsComponent.hpp index cd62f9b2f..0b422c52a 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.hpp +++ b/SDK/include/NDK/Components/GraphicsComponent.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Ndk { @@ -34,7 +35,7 @@ namespace Ndk inline void AddToCullingList(GraphicsComponentCullingList* cullingList) const; void AddToRenderQueue(Nz::AbstractRenderQueue* renderQueue) const; - void Attach(Nz::InstancedRenderableRef renderable, int renderOrder = 0); + inline void Attach(Nz::InstancedRenderableRef renderable, int renderOrder = 0); void Attach(Nz::InstancedRenderableRef renderable, const Nz::Matrix4f& localMatrix, int renderOrder = 0); inline void Clear(); @@ -54,22 +55,42 @@ namespace Ndk static ComponentIndex componentIndex; private: + struct Renderable; + + void ConnectInstancedRenderableSignals(Renderable& renderable); + inline void InvalidateBoundingVolume() const; void InvalidateRenderableData(const Nz::InstancedRenderable* renderable, Nz::UInt32 flags, std::size_t index); + void InvalidateRenderableMaterial(const Nz::InstancedRenderable* renderable, std::size_t skinIndex, std::size_t matIndex, const Nz::MaterialRef& newMat); inline void InvalidateRenderables(); inline void InvalidateTransformMatrix(); + void RegisterMaterial(Nz::Material* material, std::size_t count = 1); + void OnAttached() override; void OnComponentAttached(BaseComponent& component) override; void OnComponentDetached(BaseComponent& component) override; void OnDetached() override; + + void OnInstancedRenderableResetMaterials(const Nz::InstancedRenderable* renderable, std::size_t newMaterialCount); + void OnInstancedRenderableSkinChange(const Nz::InstancedRenderable* renderable, std::size_t newSkinIndex); + void OnMaterialReflectionChange(const Nz::Material* material, Nz::ReflectionMode reflectionMode); void OnNodeInvalidated(const Nz::Node* node); + void UnregisterMaterial(Nz::Material* material); + void UpdateBoundingVolume() const; void UpdateTransformMatrix() const; NazaraSlot(Nz::Node, OnNodeInvalidation, m_nodeInvalidationSlot); + struct MaterialEntry + { + NazaraSlot(Nz::Material, OnMaterialReflectionChange, reflectionModelChangeSlot); + + std::size_t renderableCounter; + }; + struct Renderable { Renderable(const Nz::Matrix4f& transformMatrix) : @@ -78,15 +99,8 @@ namespace Ndk { } - Renderable(Renderable&& rhs) noexcept : - renderableBoundingVolumeInvalidationSlot(std::move(rhs.renderableBoundingVolumeInvalidationSlot)), - renderableDataInvalidationSlot(std::move(rhs.renderableDataInvalidationSlot)), - renderableReleaseSlot(std::move(rhs.renderableReleaseSlot)), - data(std::move(rhs.data)), - renderable(std::move(rhs.renderable)), - dataUpdated(rhs.dataUpdated) - { - } + Renderable(const Renderable&) = delete; + Renderable(Renderable&& rhs) noexcept = default; ~Renderable() { @@ -94,21 +108,15 @@ namespace Ndk renderableReleaseSlot.Disconnect(); } - Renderable& operator=(Renderable&& r) noexcept - { - data = std::move(r.data); - dataUpdated = r.dataUpdated; - renderable = std::move(r.renderable); - renderableBoundingVolumeInvalidationSlot = std::move(r.renderableBoundingVolumeInvalidationSlot); - renderableDataInvalidationSlot = std::move(r.renderableDataInvalidationSlot); - renderableReleaseSlot = std::move(r.renderableReleaseSlot); - - return *this; - } + Renderable& operator=(const Renderable&) = delete; + Renderable& operator=(Renderable&& r) noexcept = default; NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateBoundingVolume, renderableBoundingVolumeInvalidationSlot); NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateData, renderableDataInvalidationSlot); + NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableInvalidateMaterial, renderableMaterialInvalidationSlot); NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableRelease, renderableReleaseSlot); + NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableResetMaterials, renderableResetMaterialsSlot); + NazaraSlot(Nz::InstancedRenderable, OnInstancedRenderableSkinChange, renderableSkinChangeSlot); mutable Nz::InstancedRenderable::InstanceData data; Nz::InstancedRenderableRef renderable; @@ -124,11 +132,14 @@ namespace Ndk NazaraSlot(GraphicsComponentCullingList, OnCullingListRelease, cullingListReleaseSlot); }; + std::size_t m_reflectiveMaterialCount; mutable std::vector m_volumeCullingEntries; std::vector m_renderables; + std::unordered_map m_materialEntries; mutable Nz::BoundingVolumef m_boundingVolume; mutable Nz::Matrix4f m_transformMatrix; mutable bool m_boundingVolumeUpdated; + bool m_shouldRenderReflectionMap; mutable bool m_transformMatrixUpdated; }; } diff --git a/SDK/include/NDK/Components/GraphicsComponent.inl b/SDK/include/NDK/Components/GraphicsComponent.inl index e67b6f323..81d9b121f 100644 --- a/SDK/include/NDK/Components/GraphicsComponent.inl +++ b/SDK/include/NDK/Components/GraphicsComponent.inl @@ -38,6 +38,17 @@ namespace Ndk InvalidateBoundingVolume(); } + /*! + * \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(std::move(renderable), Nz::Matrix4f::Identity(), renderOrder); + } + /*! * \brief Clears every renderable elements */ @@ -62,6 +73,11 @@ namespace Ndk if (it->renderable == renderable) { InvalidateBoundingVolume(); + + std::size_t materialCount = renderable->GetMaterialCount(); + for (std::size_t i = 0; i < materialCount; ++i) + UnregisterMaterial(renderable->GetMaterial(i)); + m_renderables.erase(it); break; } @@ -172,4 +188,4 @@ namespace Ndk InvalidateBoundingVolume(); InvalidateRenderables(); } -} \ No newline at end of file +} diff --git a/SDK/src/NDK/Components/GraphicsComponent.cpp b/SDK/src/NDK/Components/GraphicsComponent.cpp index 8ec5319cd..aaab86b43 100644 --- a/SDK/src/NDK/Components/GraphicsComponent.cpp +++ b/SDK/src/NDK/Components/GraphicsComponent.cpp @@ -39,17 +39,6 @@ namespace Ndk } } - /*! - * \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 * @@ -60,26 +49,29 @@ namespace Ndk 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); + Renderable& entry = m_renderables.back(); + entry.data.localMatrix = localMatrix; + entry.data.renderOrder = renderOrder; + entry.renderable = std::move(renderable); + + ConnectInstancedRenderableSignals(entry); + + std::size_t materialCount = entry.renderable->GetMaterialCount(); + for (std::size_t i = 0; i < materialCount; ++i) + RegisterMaterial(entry.renderable->GetMaterial(i)); InvalidateBoundingVolume(); } - /*! - * \brief Invalidates the data for renderable - * - * \param renderable Renderable to invalidate - * \param flags Flags for the instance - * \param index Index of the renderable to invalidate - * - * \remark Produces a NazaraAssert if index is out of bound - */ + void GraphicsComponent::ConnectInstancedRenderableSignals(Renderable& entry) + { + entry.renderableBoundingVolumeInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateBoundingVolume, [this](const Nz::InstancedRenderable*) { InvalidateBoundingVolume(); }); + entry.renderableDataInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateData, std::bind(&GraphicsComponent::InvalidateRenderableData, this, std::placeholders::_1, std::placeholders::_2, m_renderables.size() - 1)); + entry.renderableMaterialInvalidationSlot.Connect(entry.renderable->OnInstancedRenderableInvalidateMaterial, this, &GraphicsComponent::InvalidateRenderableMaterial); + entry.renderableReleaseSlot.Connect(entry.renderable->OnInstancedRenderableRelease, this, &GraphicsComponent::Detach); + entry.renderableResetMaterialsSlot.Connect(entry.renderable->OnInstancedRenderableResetMaterials, this, &GraphicsComponent::OnInstancedRenderableResetMaterials); + entry.renderableSkinChangeSlot.Connect(entry.renderable->OnInstancedRenderableSkinChange, this, &GraphicsComponent::OnInstancedRenderableSkinChange); + } void GraphicsComponent::InvalidateRenderableData(const Nz::InstancedRenderable* renderable , Nz::UInt32 flags, std::size_t index) { @@ -94,6 +86,38 @@ namespace Ndk entry.listEntry.ForceInvalidation(); } + void GraphicsComponent::InvalidateRenderableMaterial(const Nz::InstancedRenderable* renderable, std::size_t skinIndex, std::size_t matIndex, const Nz::MaterialRef& newMat) + { + if (renderable->GetSkin() != skinIndex) + return; + + RegisterMaterial(newMat); + + const Nz::MaterialRef& oldMat = renderable->GetMaterial(skinIndex, matIndex); + UnregisterMaterial(oldMat); + } + + void GraphicsComponent::RegisterMaterial(Nz::Material* material, std::size_t count) + { + auto it = m_materialEntries.find(material); + if (it == m_materialEntries.end()) + { + MaterialEntry matEntry; + matEntry.reflectionModelChangeSlot.Connect(material->OnMaterialReflectionChange, this, &GraphicsComponent::OnMaterialReflectionChange); + matEntry.renderableCounter = count; + + if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime) + { + if (m_reflectiveMaterialCount++ == 0) + m_entity->Invalidate(); + } + + m_materialEntries.emplace(material, std::move(matEntry)); + } + else + it->second.renderableCounter += count; + } + /*! * \brief Operation to perform when component is attached to an entity */ @@ -150,11 +174,39 @@ namespace Ndk InvalidateTransformMatrix(); } - /*! - * \brief Operation to perform when the node is invalidated - * - * \param node Pointer to the node - */ + void GraphicsComponent::OnInstancedRenderableResetMaterials(const Nz::InstancedRenderable* renderable, std::size_t newMaterialCount) + { + RegisterMaterial(Nz::Material::GetDefault(), newMaterialCount); + + std::size_t materialCount = renderable->GetMaterialCount(); + for (std::size_t i = 0; i < materialCount; ++i) + UnregisterMaterial(renderable->GetMaterial(i)); + } + + void GraphicsComponent::OnInstancedRenderableSkinChange(const Nz::InstancedRenderable* renderable, std::size_t newSkinIndex) + { + std::size_t materialCount = renderable->GetMaterialCount(); + for (std::size_t i = 0; i < materialCount; ++i) + RegisterMaterial(renderable->GetMaterial(newSkinIndex, i)); + + for (std::size_t i = 0; i < materialCount; ++i) + UnregisterMaterial(renderable->GetMaterial(i)); + } + + void GraphicsComponent::OnMaterialReflectionChange(const Nz::Material* material, Nz::ReflectionMode reflectionMode) + { + // Since this signal is only called when the new reflection mode is different from the current one, no need to compare both + if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime) + { + if (--m_reflectiveMaterialCount == 0) + m_entity->Invalidate(); + } + else if (reflectionMode == Nz::ReflectionMode_RealTime) + { + if (m_reflectiveMaterialCount++ == 0) + m_entity->Invalidate(); + } + } void GraphicsComponent::OnNodeInvalidated(const Nz::Node* node) { @@ -168,6 +220,24 @@ namespace Ndk entry.listEntry.ForceInvalidation(); //< Force invalidation on movement } + void GraphicsComponent::UnregisterMaterial(Nz::Material* material) + { + auto it = m_materialEntries.find(material); + NazaraAssert(it != m_materialEntries.end(), "Material not registered"); + + MaterialEntry& matEntry = it->second; + if (--matEntry.renderableCounter == 0) + { + if (material->GetReflectionMode() == Nz::ReflectionMode_RealTime) + { + if (--m_reflectiveMaterialCount == 0) + m_entity->Invalidate(); + } + + m_materialEntries.erase(it); + } + } + /*! * \brief Updates the bounding volume */ diff --git a/include/Nazara/Graphics/InstancedRenderable.hpp b/include/Nazara/Graphics/InstancedRenderable.hpp index 1934f1082..25cc74ec6 100644 --- a/include/Nazara/Graphics/InstancedRenderable.hpp +++ b/include/Nazara/Graphics/InstancedRenderable.hpp @@ -68,6 +68,8 @@ namespace Nz NazaraSignal(OnInstancedRenderableInvalidateData, const InstancedRenderable* /*instancedRenderable*/, UInt32 /*flags*/); NazaraSignal(OnInstancedRenderableInvalidateMaterial, const InstancedRenderable* /*instancedRenderable*/, std::size_t /*skinIndex*/, std::size_t /*matIndex*/, const MaterialRef& /*newMat*/); NazaraSignal(OnInstancedRenderableRelease, const InstancedRenderable* /*instancedRenderable*/); + NazaraSignal(OnInstancedRenderableResetMaterials, const InstancedRenderable* /*instancedRenderable*/, std::size_t /*newMaterialCount*/); + NazaraSignal(OnInstancedRenderableSkinChange, const InstancedRenderable* /*instancedRenderable*/, std::size_t /*newSkinIndex*/); struct InstanceData { diff --git a/include/Nazara/Graphics/InstancedRenderable.inl b/include/Nazara/Graphics/InstancedRenderable.inl index 4d12bd5e4..c2a8f1b70 100644 --- a/include/Nazara/Graphics/InstancedRenderable.inl +++ b/include/Nazara/Graphics/InstancedRenderable.inl @@ -124,10 +124,15 @@ namespace Nz { NazaraAssert(skinIndex < m_skinCount, "Skin index out of bounds"); - m_skin = skinIndex; + if (m_skin != skinIndex) + { + OnInstancedRenderableSkinChange(this, skinIndex); - // Force render queue invalidation - InvalidateInstanceData(0); + m_skin = skinIndex; + + // Force render queue invalidation + InvalidateInstanceData(0); + } } /*! @@ -186,6 +191,8 @@ namespace Nz { NazaraAssert(skinCount != 0, "Invalid skin count (cannot be zero)"); + OnInstancedRenderableResetMaterials(this, matCount); + m_materials.clear(); m_materials.resize(matCount * skinCount, Material::GetDefault());