diff --git a/include/Nazara/Graphics/Components/GraphicsComponent.hpp b/include/Nazara/Graphics/Components/GraphicsComponent.hpp index 777a616cb..2001b85f7 100644 --- a/include/Nazara/Graphics/Components/GraphicsComponent.hpp +++ b/include/Nazara/Graphics/Components/GraphicsComponent.hpp @@ -21,22 +21,32 @@ namespace Nz public: struct Renderable; - GraphicsComponent(); + inline GraphicsComponent(bool initialyVisible = true); GraphicsComponent(const GraphicsComponent&) = default; GraphicsComponent(GraphicsComponent&&) = default; ~GraphicsComponent() = default; inline void AttachRenderable(std::shared_ptr renderable, UInt32 renderMask = 0xFFFFFFFF); + + inline void Clear(); + inline void DetachRenderable(const std::shared_ptr& renderable); inline const std::vector& GetRenderables() const; inline const WorldInstancePtr& GetWorldInstance() const; + inline void Hide(); + + inline bool IsVisible() const; + + inline void Show(bool show = true); + GraphicsComponent& operator=(const GraphicsComponent&) = default; GraphicsComponent& operator=(GraphicsComponent&&) = default; NazaraSignal(OnRenderableAttached, GraphicsComponent* /*graphicsComponent*/, const Renderable& /*renderable*/); NazaraSignal(OnRenderableDetach, GraphicsComponent* /*graphicsComponent*/, const Renderable& /*renderable*/); + NazaraSignal(OnVisibilityUpdate, GraphicsComponent* /*graphicsComponent*/, bool /*newVisibilityState*/); struct Renderable { @@ -47,6 +57,7 @@ namespace Nz private: std::vector m_renderables; WorldInstancePtr m_worldInstance; + bool m_isVisible; }; } diff --git a/include/Nazara/Graphics/Components/GraphicsComponent.inl b/include/Nazara/Graphics/Components/GraphicsComponent.inl index 7ef308a0d..8d59a62f1 100644 --- a/include/Nazara/Graphics/Components/GraphicsComponent.inl +++ b/include/Nazara/Graphics/Components/GraphicsComponent.inl @@ -7,7 +7,8 @@ namespace Nz { - inline GraphicsComponent::GraphicsComponent() + inline GraphicsComponent::GraphicsComponent(bool initialyVisible) : + m_isVisible(initialyVisible) { m_worldInstance = std::make_shared(); //< FIXME: Use pools } @@ -21,6 +22,14 @@ namespace Nz OnRenderableAttached(this, m_renderables.back()); } + inline void GraphicsComponent::Clear() + { + for (const auto& renderable : m_renderables) + OnRenderableDetach(this, renderable); + + m_renderables.clear(); + } + inline void GraphicsComponent::DetachRenderable(const std::shared_ptr& renderable) { auto it = std::find_if(m_renderables.begin(), m_renderables.end(), [&](const auto& renderableEntry) { return renderableEntry.renderable == renderable; }); @@ -41,6 +50,25 @@ namespace Nz { return m_worldInstance; } + + inline void GraphicsComponent::Hide() + { + return Show(false); + } + + inline bool GraphicsComponent::IsVisible() const + { + return m_isVisible; + } + + inline void GraphicsComponent::Show(bool show) + { + if (m_isVisible != show) + { + OnVisibilityUpdate(this, show); + m_isVisible = show; + } + } } #include diff --git a/include/Nazara/Graphics/Systems/RenderSystem.hpp b/include/Nazara/Graphics/Systems/RenderSystem.hpp index 14b3d8a2d..b2a331265 100644 --- a/include/Nazara/Graphics/Systems/RenderSystem.hpp +++ b/include/Nazara/Graphics/Systems/RenderSystem.hpp @@ -41,6 +41,7 @@ namespace Nz void OnGraphicsDestroy(entt::registry& registry, entt::entity entity); void OnNodeDestroy(entt::registry& registry, entt::entity entity); void UpdateInstances(entt::registry& registry); + void UpdateVisibility(entt::registry& registry); struct CameraEntity { @@ -51,6 +52,7 @@ namespace Nz { NazaraSlot(GraphicsComponent, OnRenderableAttached, onRenderableAttached); NazaraSlot(GraphicsComponent, OnRenderableDetach, onRenderableDetach); + NazaraSlot(GraphicsComponent, OnVisibilityUpdate, onVisibilityUpdate); NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation); }; @@ -64,6 +66,8 @@ namespace Nz std::unique_ptr m_pipeline; std::unordered_map m_cameraEntities; std::unordered_map m_graphicsEntities; + std::unordered_set m_newlyHiddenEntities; + std::unordered_set m_newlyVisibleEntities; }; } diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index c00c21745..0abfff828 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -96,6 +96,7 @@ namespace Nz { viewerData.rebuildDepthPrepass = true; viewerData.rebuildForwardPass = true; + viewerData.prepare = true; } }); @@ -118,6 +119,7 @@ namespace Nz { viewerData.rebuildDepthPrepass = true; viewerData.rebuildForwardPass = true; + viewerData.prepare = true; } } } @@ -447,6 +449,7 @@ namespace Nz { viewerData.rebuildDepthPrepass = true; viewerData.rebuildForwardPass = true; + viewerData.prepare = true; } } diff --git a/src/Nazara/Graphics/Systems/RenderSystem.cpp b/src/Nazara/Graphics/Systems/RenderSystem.cpp index ce99e52a5..4144ae11a 100644 --- a/src/Nazara/Graphics/Systems/RenderSystem.cpp +++ b/src/Nazara/Graphics/Systems/RenderSystem.cpp @@ -60,9 +60,12 @@ namespace Nz GraphicsComponent& entityGfx = registry.get(entity); NodeComponent& entityNode = registry.get(entity); - const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); - for (const auto& renderableEntry : entityGfx.GetRenderables()) - m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); + if (entityGfx.IsVisible()) + { + const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); + for (const auto& renderableEntry : entityGfx.GetRenderables()) + m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); + } m_invalidatedWorldNode.insert(entity); @@ -75,17 +78,38 @@ namespace Nz graphicsEntity.onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry) { + if (!gfx->IsVisible()) + return; + const WorldInstancePtr& worldInstance = gfx->GetWorldInstance(); m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); }); graphicsEntity.onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry) { + if (!gfx->IsVisible()) + return; + const WorldInstancePtr& worldInstance = gfx->GetWorldInstance(); m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get()); }); + + graphicsEntity.onVisibilityUpdate.Connect(entityGfx.OnVisibilityUpdate, [this, entity](GraphicsComponent* /*gfx*/, bool isVisible) + { + if (isVisible) + { + m_newlyHiddenEntities.erase(entity); + m_newlyVisibleEntities.insert(entity); + } + else + { + m_newlyHiddenEntities.insert(entity); + m_newlyVisibleEntities.erase(entity); + } + }); }); + UpdateVisibility(registry); UpdateInstances(registry); m_pipeline->Render(renderFrame); @@ -104,6 +128,8 @@ namespace Nz { m_graphicsEntities.erase(entity); m_invalidatedWorldNode.erase(entity); + m_newlyHiddenEntities.erase(entity); + m_newlyVisibleEntities.erase(entity); GraphicsComponent& entityGfx = registry.get(entity); const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); @@ -113,6 +139,9 @@ namespace Nz void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity) { + m_newlyHiddenEntities.erase(entity); + m_newlyVisibleEntities.erase(entity); + if (registry.try_get(entity)) OnCameraDestroy(registry, entity); @@ -146,4 +175,29 @@ namespace Nz } m_invalidatedWorldNode.clear(); } + + void RenderSystem::UpdateVisibility(entt::registry& registry) + { + // Unregister drawables for hidden entities + for (entt::entity entity : m_newlyHiddenEntities) + { + GraphicsComponent& entityGfx = registry.get(entity); + + const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); + for (const auto& renderableEntry : entityGfx.GetRenderables()) + m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get()); + } + m_newlyHiddenEntities.clear(); + + // Register drawables for newly visible entities + for (entt::entity entity : m_newlyVisibleEntities) + { + GraphicsComponent& entityGfx = registry.get(entity); + + const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); + for (const auto& renderableEntry : entityGfx.GetRenderables()) + m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); + } + m_newlyVisibleEntities.clear(); + } }