From 1768f2036534433020137c8977a1b6c78b97f441 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 26 Nov 2022 18:33:12 +0100 Subject: [PATCH] Graphics: Move shadow-mapping related code to LightShadow classes --- include/Nazara/Graphics/AbstractViewer.hpp | 2 +- include/Nazara/Graphics/Camera.hpp | 2 +- include/Nazara/Graphics/DepthPipelinePass.hpp | 2 +- include/Nazara/Graphics/DirectionalLight.hpp | 2 + .../Nazara/Graphics/ForwardFramePipeline.hpp | 20 +- include/Nazara/Graphics/FramePipeline.hpp | 12 +- include/Nazara/Graphics/Light.hpp | 5 + include/Nazara/Graphics/LightShadowData.hpp | 49 ++++ include/Nazara/Graphics/LightShadowData.inl | 12 + include/Nazara/Graphics/Model.hpp | 1 - include/Nazara/Graphics/PointLight.hpp | 2 + include/Nazara/Graphics/ShadowViewer.hpp | 46 ++++ include/Nazara/Graphics/ShadowViewer.inl | 28 +++ include/Nazara/Graphics/SpotLight.hpp | 2 + .../Nazara/Graphics/SpotLightShadowData.hpp | 56 +++++ .../Nazara/Graphics/SpotLightShadowData.inl | 12 + src/Nazara/Graphics/Camera.cpp | 2 +- src/Nazara/Graphics/DepthPipelinePass.cpp | 4 +- src/Nazara/Graphics/DirectionalLight.cpp | 5 + src/Nazara/Graphics/ForwardFramePipeline.cpp | 214 +++++++----------- src/Nazara/Graphics/ForwardPipelinePass.cpp | 12 +- src/Nazara/Graphics/LightShadowData.cpp | 11 + src/Nazara/Graphics/PointLight.cpp | 5 + src/Nazara/Graphics/ShadowViewer.cpp | 39 ++++ src/Nazara/Graphics/SpotLight.cpp | 6 + src/Nazara/Graphics/SpotLightShadowData.cpp | 99 ++++++++ src/Nazara/Graphics/Systems/RenderSystem.cpp | 6 +- 27 files changed, 496 insertions(+), 160 deletions(-) create mode 100644 include/Nazara/Graphics/LightShadowData.hpp create mode 100644 include/Nazara/Graphics/LightShadowData.inl create mode 100644 include/Nazara/Graphics/ShadowViewer.hpp create mode 100644 include/Nazara/Graphics/ShadowViewer.inl create mode 100644 include/Nazara/Graphics/SpotLightShadowData.hpp create mode 100644 include/Nazara/Graphics/SpotLightShadowData.inl create mode 100644 src/Nazara/Graphics/LightShadowData.cpp create mode 100644 src/Nazara/Graphics/ShadowViewer.cpp create mode 100644 src/Nazara/Graphics/SpotLightShadowData.cpp diff --git a/include/Nazara/Graphics/AbstractViewer.hpp b/include/Nazara/Graphics/AbstractViewer.hpp index bc7b05c9e..895f07737 100644 --- a/include/Nazara/Graphics/AbstractViewer.hpp +++ b/include/Nazara/Graphics/AbstractViewer.hpp @@ -25,7 +25,7 @@ namespace Nz virtual const Color& GetClearColor() const = 0; virtual UInt32 GetRenderMask() const = 0; - virtual const RenderTarget& GetRenderTarget() = 0; + virtual const RenderTarget& GetRenderTarget() const = 0; virtual ViewerInstance& GetViewerInstance() = 0; virtual const ViewerInstance& GetViewerInstance() const = 0; virtual const Recti& GetViewport() const = 0; diff --git a/include/Nazara/Graphics/Camera.hpp b/include/Nazara/Graphics/Camera.hpp index f5bc46bd5..9f251be34 100644 --- a/include/Nazara/Graphics/Camera.hpp +++ b/include/Nazara/Graphics/Camera.hpp @@ -32,7 +32,7 @@ namespace Nz inline DegreeAnglef GetFOV() const; UInt32 GetRenderMask() const override; inline Int32 GetRenderOrder() const; - const RenderTarget& GetRenderTarget() override; + const RenderTarget& GetRenderTarget() const; inline const Vector2f& GetSize() const; inline const Rectf& GetTargetRegion() const; ViewerInstance& GetViewerInstance() override; diff --git a/include/Nazara/Graphics/DepthPipelinePass.hpp b/include/Nazara/Graphics/DepthPipelinePass.hpp index fe0830a3e..0de4c8ca3 100644 --- a/include/Nazara/Graphics/DepthPipelinePass.hpp +++ b/include/Nazara/Graphics/DepthPipelinePass.hpp @@ -41,7 +41,7 @@ namespace Nz void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, std::size_t visibilityHash); void RegisterMaterialInstance(const MaterialInstance& materialInstance); - FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex); + FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t outputAttachment); void UnregisterMaterialInstance(const MaterialInstance& materialInstance); diff --git a/include/Nazara/Graphics/DirectionalLight.hpp b/include/Nazara/Graphics/DirectionalLight.hpp index 1e93ec200..bfff9acbb 100644 --- a/include/Nazara/Graphics/DirectionalLight.hpp +++ b/include/Nazara/Graphics/DirectionalLight.hpp @@ -28,6 +28,8 @@ namespace Nz void FillLightData(void* data) const override; + std::unique_ptr InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override; + inline float GetAmbientFactor() const; inline float GetDiffuseFactor() const; inline Color GetColor() const; diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 7a9697418..331931159 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,13 @@ namespace Nz ForwardFramePipeline(ForwardFramePipeline&&) = delete; ~ForwardFramePipeline(); - std::size_t RegisterLight(std::shared_ptr light, UInt32 renderMask) override; + const std::vector& FrustumCull(const Frustumf& frustum, UInt32 mask, std::size_t& visibilityHash) const override; + + void ForEachRegisteredMaterialInstance(FunctionRef callback) override; + + void QueueTransfer(TransferInterface* transfer) override; + + std::size_t RegisterLight(const Light* light, UInt32 renderMask) override; std::size_t RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) override; std::size_t RegisterSkeleton(SkeletonInstancePtr skeletonInstance) override; std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override; @@ -79,14 +86,11 @@ namespace Nz struct LightData { - std::shared_ptr light; - std::size_t shadowMapAttachmentIndex; - std::unique_ptr camera; - std::unique_ptr pass; + std::unique_ptr shadowData; + const Light* light; UInt32 renderMask; NazaraSlot(Light, OnLightDataInvalided, onLightInvalidated); - NazaraSlot(Light, OnLightTransformInvalided, onLightTransformInvalidated); NazaraSlot(Light, OnLightShadowCastingChanged, onLightShadowCastingChanged); }; @@ -150,7 +154,7 @@ namespace Nz std::unordered_map m_renderTargets; std::unordered_map m_materialInstances; std::vector m_renderStates; - std::vector m_visibleRenderables; + mutable std::vector m_visibleRenderables; std::vector m_visibleLights; robin_hood::unordered_set m_transferSet; BakedFrameGraph m_bakedFrameGraph; @@ -159,7 +163,7 @@ namespace Nz Bitset m_removedViewerInstances; Bitset m_removedWorldInstances; ElementRendererRegistry& m_elementRegistry; - MemoryPool m_renderablePool; + mutable MemoryPool m_renderablePool; //< FIXME: has to be mutable because MemoryPool has no const_iterator MemoryPool m_lightPool; MemoryPool m_skeletonInstances; MemoryPool m_viewerPool; diff --git a/include/Nazara/Graphics/FramePipeline.hpp b/include/Nazara/Graphics/FramePipeline.hpp index 1597f4a95..36926ddc6 100644 --- a/include/Nazara/Graphics/FramePipeline.hpp +++ b/include/Nazara/Graphics/FramePipeline.hpp @@ -9,10 +9,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -21,6 +23,7 @@ namespace Nz class AbstractViewer; class InstancedRenderable; class Light; + class MaterialInstance; class RenderFrame; class NAZARA_GRAPHICS_API FramePipeline @@ -31,9 +34,16 @@ namespace Nz FramePipeline(FramePipeline&&) noexcept = default; virtual ~FramePipeline(); + // TODO: Move RenderQueue handling to proper classes (allowing to reuse them) + virtual const std::vector& FrustumCull(const Frustumf& frustum, UInt32 mask, std::size_t& visibilityHash) const = 0; + + virtual void ForEachRegisteredMaterialInstance(FunctionRef callback) = 0; + inline DebugDrawer& GetDebugDrawer(); - virtual std::size_t RegisterLight(std::shared_ptr light, UInt32 renderMask) = 0; + virtual void QueueTransfer(TransferInterface* transfer) = 0; + + virtual std::size_t RegisterLight(const Light* light, UInt32 renderMask) = 0; virtual std::size_t RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) = 0; virtual std::size_t RegisterSkeleton(SkeletonInstancePtr skeletonInstance) = 0; virtual std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0; diff --git a/include/Nazara/Graphics/Light.hpp b/include/Nazara/Graphics/Light.hpp index e97902307..310db3d21 100644 --- a/include/Nazara/Graphics/Light.hpp +++ b/include/Nazara/Graphics/Light.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,8 @@ namespace Nz { class CommandBufferBuilder; + class ElementRendererRegistry; + class FramePipeline; class RenderBuffer; class RenderFrame; class Texture; @@ -42,6 +45,8 @@ namespace Nz inline PixelFormat GetShadowMapFormat() const; inline UInt32 GetShadowMapSize() const; + virtual std::unique_ptr InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const = 0; + inline bool IsShadowCaster() const; inline void UpdateShadowMapFormat(PixelFormat format); diff --git a/include/Nazara/Graphics/LightShadowData.hpp b/include/Nazara/Graphics/LightShadowData.hpp new file mode 100644 index 000000000..c16b81873 --- /dev/null +++ b/include/Nazara/Graphics/LightShadowData.hpp @@ -0,0 +1,49 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// 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_GRAPHICS_LIGHTSHADOWDATA_HPP +#define NAZARA_GRAPHICS_LIGHTSHADOWDATA_HPP + +#include +#include + +namespace Nz +{ + class BakedFrameGraph; + class FrameGraph; + class FramePass; + class MaterialInstance; + class RenderFrame; + class Texture; + + class NAZARA_GRAPHICS_API LightShadowData + { + public: + LightShadowData() = default; + LightShadowData(const LightShadowData&) = delete; + LightShadowData(LightShadowData&&) = delete; + virtual ~LightShadowData(); + + virtual void PrepareRendering(RenderFrame& renderFrame) = 0; + + virtual void RegisterMaterialInstance(const MaterialInstance& matInstance) = 0; + virtual void RegisterPassInputs(FramePass& pass) = 0; + virtual void RegisterToFrameGraph(FrameGraph& frameGraph) = 0; + + virtual const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const = 0; + + virtual void UnregisterMaterialInstance(const MaterialInstance& matInstance) = 0; + + LightShadowData& operator=(const LightShadowData&) = delete; + LightShadowData& operator=(LightShadowData&&) = delete; + + private: + }; +} + +#include + +#endif // NAZARA_GRAPHICS_LIGHTSHADOWDATA_HPP diff --git a/include/Nazara/Graphics/LightShadowData.inl b/include/Nazara/Graphics/LightShadowData.inl new file mode 100644 index 000000000..4bcad907a --- /dev/null +++ b/include/Nazara/Graphics/LightShadowData.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index f2426a3ad..8b09bd39d 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -54,7 +54,6 @@ namespace Nz std::shared_ptr m_graphicalMesh; std::vector m_submeshes; - Recti m_scissorBox; }; } diff --git a/include/Nazara/Graphics/PointLight.hpp b/include/Nazara/Graphics/PointLight.hpp index f3ad337c4..17cb73382 100644 --- a/include/Nazara/Graphics/PointLight.hpp +++ b/include/Nazara/Graphics/PointLight.hpp @@ -27,6 +27,8 @@ namespace Nz void FillLightData(void* data) const override; + std::unique_ptr InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override; + inline float GetAmbientFactor() const; inline float GetDiffuseFactor() const; inline Color GetColor() const; diff --git a/include/Nazara/Graphics/ShadowViewer.hpp b/include/Nazara/Graphics/ShadowViewer.hpp new file mode 100644 index 000000000..2562fb1bb --- /dev/null +++ b/include/Nazara/Graphics/ShadowViewer.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// 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_GRAPHICS_SHADOWVIEWER_HPP +#define NAZARA_GRAPHICS_SHADOWVIEWER_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API ShadowViewer : public AbstractViewer + { + public: + inline ShadowViewer(const Recti& viewport, UInt32 renderMask); + ShadowViewer(const ShadowViewer&) = delete; + ShadowViewer(ShadowViewer&&) = delete; + ~ShadowViewer() = default; + + const Color& GetClearColor() const override; + UInt32 GetRenderMask() const override; + const RenderTarget& GetRenderTarget() const override; + ViewerInstance& GetViewerInstance() override; + const ViewerInstance& GetViewerInstance() const override; + const Recti& GetViewport() const override; + + inline void UpdateRenderMask(UInt32 renderMask); + inline void UpdateViewport(const Recti& viewport); + + ShadowViewer& operator=(const ShadowViewer&) = delete; + ShadowViewer& operator=(ShadowViewer&&) = delete; + + private: + Recti m_viewport; + ViewerInstance m_viewerInstance; + UInt32 m_renderMask; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_SHADOWVIEWER_HPP diff --git a/include/Nazara/Graphics/ShadowViewer.inl b/include/Nazara/Graphics/ShadowViewer.inl new file mode 100644 index 000000000..d7dcddfd3 --- /dev/null +++ b/include/Nazara/Graphics/ShadowViewer.inl @@ -0,0 +1,28 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline ShadowViewer::ShadowViewer(const Recti& viewport, UInt32 renderMask) : + m_renderMask(renderMask) + { + UpdateViewport(viewport); + } + + inline void ShadowViewer::UpdateRenderMask(UInt32 renderMask) + { + m_renderMask = renderMask; + } + + inline void ShadowViewer::UpdateViewport(const Recti& viewport) + { + m_viewport = viewport; + m_viewerInstance.UpdateTargetSize({ float(m_viewport.width), float(m_viewport.height) }); + } +} + +#include diff --git a/include/Nazara/Graphics/SpotLight.hpp b/include/Nazara/Graphics/SpotLight.hpp index 1438336cd..95cd4f238 100644 --- a/include/Nazara/Graphics/SpotLight.hpp +++ b/include/Nazara/Graphics/SpotLight.hpp @@ -38,6 +38,8 @@ namespace Nz inline const Quaternionf& GetRotation() const; inline float GetRadius() const; + std::unique_ptr InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override; + inline void UpdateAmbientFactor(float factor); inline void UpdateColor(Color color); inline void UpdateDiffuseFactor(float factor); diff --git a/include/Nazara/Graphics/SpotLightShadowData.hpp b/include/Nazara/Graphics/SpotLightShadowData.hpp new file mode 100644 index 000000000..a1ecaffa8 --- /dev/null +++ b/include/Nazara/Graphics/SpotLightShadowData.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// 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_GRAPHICS_SPOTLIGHTSHADOWDATA_HPP +#define NAZARA_GRAPHICS_SPOTLIGHTSHADOWDATA_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class FramePipeline; + class SpotLight; + + class NAZARA_GRAPHICS_API SpotLightShadowData : public LightShadowData + { + public: + SpotLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const SpotLight& light); + SpotLightShadowData(const SpotLightShadowData&) = delete; + SpotLightShadowData(SpotLightShadowData&&) = delete; + ~SpotLightShadowData() = default; + + void PrepareRendering(RenderFrame& renderFrame) override; + + void RegisterMaterialInstance(const MaterialInstance& matInstance) override; + void RegisterPassInputs(FramePass& pass) override; + void RegisterToFrameGraph(FrameGraph& frameGraph) override; + + const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const override; + + void UnregisterMaterialInstance(const MaterialInstance& matInstance) override; + + SpotLightShadowData& operator=(const SpotLightShadowData&) = delete; + SpotLightShadowData& operator=(SpotLightShadowData&&) = delete; + + private: + NazaraSlot(Light, OnLightShadowMapSettingChange, m_onLightShadowMapSettingChange); + NazaraSlot(Light, OnLightTransformInvalided, m_onLightTransformInvalidated); + + std::optional m_depthPass; + std::size_t m_attachmentIndex; + FramePipeline& m_pipeline; + const SpotLight& m_light; + ShadowViewer m_viewer; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_SPOTLIGHTSHADOWDATA_HPP diff --git a/include/Nazara/Graphics/SpotLightShadowData.inl b/include/Nazara/Graphics/SpotLightShadowData.inl new file mode 100644 index 000000000..5f3dec0af --- /dev/null +++ b/include/Nazara/Graphics/SpotLightShadowData.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/src/Nazara/Graphics/Camera.cpp b/src/Nazara/Graphics/Camera.cpp index 208374c2a..548771193 100644 --- a/src/Nazara/Graphics/Camera.cpp +++ b/src/Nazara/Graphics/Camera.cpp @@ -18,7 +18,7 @@ namespace Nz return m_renderMask; } - const RenderTarget& Camera::GetRenderTarget() + const RenderTarget& Camera::GetRenderTarget() const { if (!m_renderTarget) throw std::runtime_error("no rendertarget set"); diff --git a/src/Nazara/Graphics/DepthPipelinePass.cpp b/src/Nazara/Graphics/DepthPipelinePass.cpp index c938a7835..e6702ba79 100644 --- a/src/Nazara/Graphics/DepthPipelinePass.cpp +++ b/src/Nazara/Graphics/DepthPipelinePass.cpp @@ -129,10 +129,10 @@ namespace Nz it->second.usedCount++; } - FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex) + FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t outputAttachment) { FramePass& depthPrepass = frameGraph.AddPass(m_passName); - depthPrepass.SetDepthStencilOutput(depthBufferIndex); + depthPrepass.SetDepthStencilOutput(outputAttachment); depthPrepass.SetDepthStencilClear(1.f, 0); depthPrepass.SetExecutionCallback([&]() diff --git a/src/Nazara/Graphics/DirectionalLight.cpp b/src/Nazara/Graphics/DirectionalLight.cpp index e2b7b4749..82eefbc73 100644 --- a/src/Nazara/Graphics/DirectionalLight.cpp +++ b/src/Nazara/Graphics/DirectionalLight.cpp @@ -30,6 +30,11 @@ namespace Nz AccessByOffset(data, lightOffset.lightMemberOffsets.shadowMapSize) = Vector2f(-1.f, -1.f); } + std::unique_ptr DirectionalLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const + { + return nullptr; //< TODO + } + void DirectionalLight::UpdateTransform(const Vector3f& /*position*/, const Quaternionf& rotation, const Vector3f& /*scale*/) { UpdateRotation(rotation); diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 3bb51a8b7..782c033af 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -44,11 +44,67 @@ namespace Nz m_viewerPool.Clear(); } - std::size_t ForwardFramePipeline::RegisterLight(std::shared_ptr light, UInt32 renderMask) + const std::vector& ForwardFramePipeline::FrustumCull(const Frustumf& frustum, UInt32 mask, std::size_t& visibilityHash) const + { + auto CombineHash = [](std::size_t currentHash, std::size_t newHash) + { + return currentHash * 23 + newHash; + }; + + m_visibleRenderables.clear(); + for (const RenderableData& renderableData : m_renderablePool) + { + if ((mask & renderableData.renderMask) == 0) + continue; + + const WorldInstancePtr& worldInstance = m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex)->worldInstance; + + // Get global AABB + BoundingVolumef boundingVolume(renderableData.renderable->GetAABB()); + boundingVolume.Update(worldInstance->GetWorldMatrix()); + + if (!frustum.Contains(boundingVolume)) + continue; + + auto& visibleRenderable = m_visibleRenderables.emplace_back(); + visibleRenderable.instancedRenderable = renderableData.renderable; + visibleRenderable.scissorBox = renderableData.scissorBox; + visibleRenderable.worldInstance = worldInstance.get(); + + if (renderableData.skeletonInstanceIndex != NoSkeletonInstance) + visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->skeleton.get(); + else + visibleRenderable.skeletonInstance = nullptr; + + visibilityHash = CombineHash(visibilityHash, std::hash()(&renderableData)); + } + + return m_visibleRenderables; + } + + void ForwardFramePipeline::ForEachRegisteredMaterialInstance(FunctionRef callback) + { + for (RenderableData& renderable : m_renderablePool) + { + std::size_t matCount = renderable.renderable->GetMaterialCount(); + for (std::size_t j = 0; j < matCount; ++j) + { + if (MaterialInstance* mat = renderable.renderable->GetMaterial(j).get()) + callback(*mat); + } + } + } + + void ForwardFramePipeline::QueueTransfer(TransferInterface* transfer) + { + m_transferSet.insert(transfer); + } + + std::size_t ForwardFramePipeline::RegisterLight(const Light* light, UInt32 renderMask) { std::size_t lightIndex; LightData* lightData = m_lightPool.Allocate(lightIndex); - lightData->light = std::move(light); + lightData->light = light; lightData->renderMask = renderMask; lightData->onLightInvalidated.Connect(lightData->light->OnLightDataInvalided, [=](Light*) { @@ -65,11 +121,14 @@ namespace Nz lightData->onLightShadowCastingChanged.Connect(lightData->light->OnLightShadowCastingChanged, [=](Light* light, bool isCastingShadows) { if (isCastingShadows) + { m_shadowCastingLights.UnboundedSet(lightIndex); + lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry); + } else { m_shadowCastingLights.Reset(lightIndex); - lightData->pass.reset(); + lightData->shadowData.reset(); } m_rebuildFrameGraph = true; @@ -78,6 +137,7 @@ namespace Nz if (lightData->light->IsShadowCaster()) { m_shadowCastingLights.UnboundedSet(lightIndex); + lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry); m_rebuildFrameGraph = true; } @@ -215,7 +275,7 @@ namespace Nz const Light* ForwardFramePipeline::RetrieveLight(std::size_t lightIndex) const { - return m_lightPool.RetrieveFromIndex(lightIndex)->light.get(); + return m_lightPool.RetrieveFromIndex(lightIndex)->light; } const Texture* ForwardFramePipeline::RetrieveLightShadowmap(std::size_t lightIndex) const @@ -223,8 +283,7 @@ namespace Nz if (!m_shadowCastingLights.UnboundedTest(lightIndex)) return nullptr; - std::size_t shadowmapIndex = m_lightPool.RetrieveFromIndex(lightIndex)->shadowMapAttachmentIndex; - return m_bakedFrameGraph.GetAttachmentTexture(shadowmapIndex).get(); + return m_lightPool.RetrieveFromIndex(lightIndex)->shadowData->RetrieveLightShadowmap(m_bakedFrameGraph); } void ForwardFramePipeline::Render(RenderFrame& renderFrame) @@ -279,51 +338,11 @@ namespace Nz builder.EndDebugRegion(); }, QueueType::Transfer); - auto CombineHash = [](std::size_t currentHash, std::size_t newHash) - { - return currentHash * 23 + newHash; - }; - // Shadow map handling for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i)) { LightData* lightData = m_lightPool.RetrieveFromIndex(i); - - const Matrix4f& viewProjMatrix = lightData->camera->GetViewerInstance().GetViewProjMatrix(); - - Frustumf frustum = Frustumf::Extract(viewProjMatrix); - - std::size_t visibilityHash = 5U; - - m_visibleRenderables.clear(); - for (const RenderableData& renderableData : m_renderablePool) - { - if ((lightData->renderMask & renderableData.renderMask) == 0) - continue; - - WorldInstancePtr& worldInstance = m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex)->worldInstance; - - // Get global AABB - BoundingVolumef boundingVolume(renderableData.renderable->GetAABB()); - boundingVolume.Update(worldInstance->GetWorldMatrix()); - - if (!frustum.Contains(boundingVolume)) - continue; - - auto& visibleRenderable = m_visibleRenderables.emplace_back(); - visibleRenderable.instancedRenderable = renderableData.renderable; - visibleRenderable.scissorBox = renderableData.scissorBox; - visibleRenderable.worldInstance = worldInstance.get(); - - if (renderableData.skeletonInstanceIndex != NoSkeletonInstance) - visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->skeleton.get(); - else - visibleRenderable.skeletonInstance = nullptr; - - visibilityHash = CombineHash(visibilityHash, std::hash()(&renderableData)); - } - - lightData->pass->Prepare(renderFrame, frustum, m_visibleRenderables, visibilityHash); + lightData->shadowData->PrepareRendering(renderFrame); } // Render queues handling @@ -335,36 +354,8 @@ namespace Nz const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix(); Frustumf frustum = Frustumf::Extract(viewProjMatrix); - - std::size_t visibilityHash = 5U; - - m_visibleRenderables.clear(); - for (const RenderableData& renderableData : m_renderablePool) - { - if ((renderMask & renderableData.renderMask) == 0) - continue; - - WorldInstancePtr& worldInstance = m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex)->worldInstance; - - // Get global AABB - BoundingVolumef boundingVolume(renderableData.renderable->GetAABB()); - boundingVolume.Update(worldInstance->GetWorldMatrix()); - - if (!frustum.Contains(boundingVolume)) - continue; - - auto& visibleRenderable = m_visibleRenderables.emplace_back(); - visibleRenderable.instancedRenderable = renderableData.renderable; - visibleRenderable.scissorBox = renderableData.scissorBox; - visibleRenderable.worldInstance = worldInstance.get(); - - if (renderableData.skeletonInstanceIndex != NoSkeletonInstance) - visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->skeleton.get(); - else - visibleRenderable.skeletonInstance = nullptr; - - visibilityHash = CombineHash(visibilityHash, std::hash()(&renderableData)); - } + std::size_t visibilityHash = 5; + const auto& visibleRenderables = FrustumCull(frustum, renderMask, visibilityHash); // Lights update don't trigger a rebuild of the depth pre-pass std::size_t depthVisibilityHash = visibilityHash; @@ -381,14 +372,20 @@ namespace Nz if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume)) { m_visibleLights.push_back(lightIndex); - visibilityHash = CombineHash(visibilityHash, std::hash()(lightData.light.get())); + + auto CombineHash = [](std::size_t currentHash, std::size_t newHash) + { + return currentHash * 23 + newHash; + }; + + visibilityHash = CombineHash(visibilityHash, std::hash()(lightData.light)); } } if (viewerData.depthPrepass) - viewerData.depthPrepass->Prepare(renderFrame, frustum, m_visibleRenderables, depthVisibilityHash); + viewerData.depthPrepass->Prepare(renderFrame, frustum, visibleRenderables, depthVisibilityHash); - viewerData.forwardPass->Prepare(renderFrame, frustum, m_visibleRenderables, m_visibleLights, visibilityHash); + viewerData.forwardPass->Prepare(renderFrame, frustum, visibleRenderables, m_visibleLights, visibilityHash); viewerData.debugDrawPass->Prepare(renderFrame); } @@ -571,60 +568,7 @@ namespace Nz for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i)) { LightData* lightData = m_lightPool.RetrieveFromIndex(i); - - assert(lightData->light->GetLightType() == UnderlyingCast(BasicLightType::Spot)); - SpotLight& spotLight = SafeCast(*lightData->light); - - PixelFormat shadowMapFormat = lightData->light->GetShadowMapFormat(); - UInt32 shadowMapSize = lightData->light->GetShadowMapSize(); - - lightData->shadowMapAttachmentIndex = frameGraph.AddAttachment({ - "Shadowmap", - shadowMapFormat, - FramePassAttachmentSize::Fixed, - shadowMapSize, shadowMapSize, - }); - - if (!lightData->camera) - { - lightData->camera = std::make_unique(nullptr); - ViewerInstance& viewerInstance = lightData->camera->GetViewerInstance(); - viewerInstance.OnTransferRequired.Connect([this](TransferInterface* transferInterface) - { - m_transferSet.insert(transferInterface); - }); - - lightData->camera->UpdateFOV(spotLight.GetOuterAngle() * 2.f); - lightData->camera->UpdateZNear(0.01f); - lightData->camera->UpdateZFar(spotLight.GetRadius()); - lightData->camera->UpdateViewport(Recti(0, 0, SafeCast(shadowMapSize), SafeCast(shadowMapSize))); - - lightData->onLightTransformInvalidated.Connect(lightData->light->OnLightTransformInvalided, [lightData](Light* light) - { - SpotLight& spotLight = SafeCast(*light); - ViewerInstance& viewerInstance = lightData->camera->GetViewerInstance(); - viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(spotLight.GetPosition(), spotLight.GetRotation())); - }); - viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(spotLight.GetPosition(), spotLight.GetRotation())); - } - - if (!lightData->pass) - { - std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass"); - - lightData->pass = std::make_unique(*this, m_elementRegistry, lightData->camera.get(), shadowPassIndex, "Spot shadowmap"); - for (RenderableData& renderable : m_renderablePool) - { - std::size_t matCount = renderable.renderable->GetMaterialCount(); - for (std::size_t j = 0; j < matCount; ++j) - { - if (MaterialInstance* mat = renderable.renderable->GetMaterial(j).get()) - lightData->pass->RegisterMaterialInstance(*mat); - } - } - - lightData->pass->RegisterToFrameGraph(frameGraph, lightData->shadowMapAttachmentIndex); - } + lightData->shadowData->RegisterToFrameGraph(frameGraph); } for (auto& viewerData : m_viewerPool) @@ -648,7 +592,7 @@ namespace Nz for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i)) { LightData* lightData = m_lightPool.RetrieveFromIndex(i); - forwardPass.AddInput(lightData->shadowMapAttachmentIndex); + lightData->shadowData->RegisterPassInputs(forwardPass); } viewerData.debugDrawPass->RegisterToFrameGraph(frameGraph, viewerData.forwardColorAttachment, viewerData.debugColorAttachment); diff --git a/src/Nazara/Graphics/ForwardPipelinePass.cpp b/src/Nazara/Graphics/ForwardPipelinePass.cpp index 912f4a353..d5a176f1b 100644 --- a/src/Nazara/Graphics/ForwardPipelinePass.cpp +++ b/src/Nazara/Graphics/ForwardPipelinePass.cpp @@ -165,8 +165,8 @@ namespace Nz perElementData.lightCount = lightCount; perElementData.lightUniformBuffer = lightUboView; - for (std::size_t i = 0; i < lightCount; ++i) - perElementData.shadowMaps[i] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[i].lightIndex); + for (std::size_t j = 0; j < lightCount; ++j) + perElementData.shadowMaps[j] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[j].lightIndex); m_lightPerRenderElement.emplace(element, perElementData); } @@ -240,18 +240,18 @@ namespace Nz auto& renderStates = m_renderStates.emplace_back(); renderStates.lightData = lightData.lightUniformBuffer; - for (std::size_t i = 0; i < lightData.lightCount; ++i) + for (std::size_t j = 0; j < lightData.lightCount; ++j) { - const Texture* texture = lightData.shadowMaps[i]; + const Texture* texture = lightData.shadowMaps[j]; if (!texture) continue; if (texture->GetType() == ImageType::E2D) - renderStates.shadowMaps2D[i] = texture; + renderStates.shadowMaps2D[j] = texture; else { assert(texture->GetType() == ImageType::Cubemap); - renderStates.shadowMapsCube[i] = texture; + renderStates.shadowMapsCube[j] = texture; } } } diff --git a/src/Nazara/Graphics/LightShadowData.cpp b/src/Nazara/Graphics/LightShadowData.cpp new file mode 100644 index 000000000..aaf28ea5a --- /dev/null +++ b/src/Nazara/Graphics/LightShadowData.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + LightShadowData::~LightShadowData() = default; +} diff --git a/src/Nazara/Graphics/PointLight.cpp b/src/Nazara/Graphics/PointLight.cpp index 1a9c93fdb..6f2af89e4 100644 --- a/src/Nazara/Graphics/PointLight.cpp +++ b/src/Nazara/Graphics/PointLight.cpp @@ -30,6 +30,11 @@ namespace Nz AccessByOffset(data, lightOffset.lightMemberOffsets.shadowMapSize) = Vector2f(-1.f, -1.f); } + std::unique_ptr PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const + { + return nullptr; //< TODO + } + void PointLight::UpdateTransform(const Vector3f& position, const Quaternionf& /*rotation*/, const Vector3f& /*scale*/) { UpdatePosition(position); diff --git a/src/Nazara/Graphics/ShadowViewer.cpp b/src/Nazara/Graphics/ShadowViewer.cpp new file mode 100644 index 000000000..c816ffde7 --- /dev/null +++ b/src/Nazara/Graphics/ShadowViewer.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + const Color& ShadowViewer::GetClearColor() const + { + throw std::runtime_error("no clear color"); + } + + UInt32 ShadowViewer::GetRenderMask() const + { + return m_renderMask; + } + + const RenderTarget& ShadowViewer::GetRenderTarget() const + { + throw std::runtime_error("no render target"); + } + + ViewerInstance& ShadowViewer::GetViewerInstance() + { + return m_viewerInstance; + } + + const ViewerInstance& ShadowViewer::GetViewerInstance() const + { + return m_viewerInstance; + } + + const Recti& ShadowViewer::GetViewport() const + { + return m_viewport; + } +} diff --git a/src/Nazara/Graphics/SpotLight.cpp b/src/Nazara/Graphics/SpotLight.cpp index fbbf74a2f..4e9d96d87 100644 --- a/src/Nazara/Graphics/SpotLight.cpp +++ b/src/Nazara/Graphics/SpotLight.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,11 @@ namespace Nz AccessByOffset(data, lightOffset.lightMemberOffsets.viewProjMatrix) = m_viewProjMatrix; } + std::unique_ptr SpotLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const + { + return std::make_unique(pipeline, elementRegistry, *this); + } + void SpotLight::UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& /*scale*/) { m_position = position; //< don't call UpdatePosition to prevent double update diff --git a/src/Nazara/Graphics/SpotLightShadowData.cpp b/src/Nazara/Graphics/SpotLightShadowData.cpp new file mode 100644 index 000000000..d112ca669 --- /dev/null +++ b/src/Nazara/Graphics/SpotLightShadowData.cpp @@ -0,0 +1,99 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + SpotLightShadowData::SpotLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const SpotLight& light) : + m_pipeline(pipeline), + m_light(light), + m_viewer(Recti(0, 0, 1, 1), 0xFFFFFFFF) + { + UInt32 shadowMapSize = light.GetShadowMapSize(); + m_viewer.UpdateViewport(Recti(0, 0, SafeCast(shadowMapSize), SafeCast(shadowMapSize))); + + ViewerInstance& viewerInstance = m_viewer.GetViewerInstance(); + viewerInstance.UpdateProjectionMatrix(Matrix4f::Perspective(m_light.GetOuterAngle() * 2.f, 1.f, 0.01f, m_light.GetRadius())); + + m_onLightShadowMapSettingChange.Connect(m_light.OnLightShadowMapSettingChange, [this](Light* /*light*/, PixelFormat /*newPixelFormat*/, UInt32 newSize) + { + m_viewer.UpdateViewport(Recti(0, 0, SafeCast(newSize), SafeCast(newSize))); + }); + + m_onLightTransformInvalidated.Connect(m_light.OnLightTransformInvalided, [this]([[maybe_unused]] Light* light) + { + assert(&m_light == light); + + ViewerInstance& viewerInstance = m_viewer.GetViewerInstance(); + viewerInstance.UpdateEyePosition(m_light.GetPosition()); + viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(m_light.GetPosition(), m_light.GetRotation())); + + m_pipeline.QueueTransfer(&viewerInstance); + }); + viewerInstance.UpdateEyePosition(m_light.GetPosition()); + viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(m_light.GetPosition(), m_light.GetRotation())); + m_pipeline.QueueTransfer(&viewerInstance); + + std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass"); + + m_depthPass.emplace(m_pipeline, elementRegistry, &m_viewer, shadowPassIndex, "Spotlight shadow mapping"); + m_pipeline.ForEachRegisteredMaterialInstance([this](const MaterialInstance& matInstance) + { + m_depthPass->RegisterMaterialInstance(matInstance); + }); + } + + void SpotLightShadowData::PrepareRendering(RenderFrame& renderFrame) +{ + const Matrix4f& viewProjMatrix = m_viewer.GetViewerInstance().GetViewProjMatrix(); + + Frustumf frustum = Frustumf::Extract(viewProjMatrix); + + std::size_t visibilityHash = 5U; + const auto& visibleRenderables = m_pipeline.FrustumCull(frustum, 0xFFFFFFFF, visibilityHash); + + m_depthPass->Prepare(renderFrame, frustum, visibleRenderables, visibilityHash); + } + + void SpotLightShadowData::RegisterMaterialInstance(const MaterialInstance& matInstance) + { + m_depthPass->RegisterMaterialInstance(matInstance); + } + + void SpotLightShadowData::RegisterPassInputs(FramePass& pass) + { + pass.AddInput(m_attachmentIndex); + } + + void SpotLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph) + { + UInt32 shadowMapSize = m_light.GetShadowMapSize(); + + m_attachmentIndex = frameGraph.AddAttachment({ + "Shadowmap", + m_light.GetShadowMapFormat(), + FramePassAttachmentSize::Fixed, + shadowMapSize, shadowMapSize, + }); + + m_depthPass->RegisterToFrameGraph(frameGraph, m_attachmentIndex); + } + + const Nz::Texture* SpotLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const + { + return bakedGraph.GetAttachmentTexture(m_attachmentIndex).get(); + } + + void SpotLightShadowData::UnregisterMaterialInstance(const MaterialInstance& matInstance) + { + m_depthPass->UnregisterMaterialInstance(matInstance); + } +} diff --git a/src/Nazara/Graphics/Systems/RenderSystem.cpp b/src/Nazara/Graphics/Systems/RenderSystem.cpp index 653f25776..93202396e 100644 --- a/src/Nazara/Graphics/Systems/RenderSystem.cpp +++ b/src/Nazara/Graphics/Systems/RenderSystem.cpp @@ -378,7 +378,7 @@ namespace Nz return; const auto& lightEntry = light->GetLightEntry(lightIndex); - lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); + lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light.get(), lightEntry.renderMask); }); lightEntity->onLightDetach.Connect(entityLight.OnLightDetach, [this, lightEntity](LightComponent* light, std::size_t lightIndex) @@ -413,7 +413,7 @@ namespace Nz if (!lightEntry.light) continue; - lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); + lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light.get(), lightEntry.renderMask); } } @@ -515,7 +515,7 @@ namespace Nz if (!lightEntry.light) continue; - lightEntity->lightIndices[renderableIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); + lightEntity->lightIndices[renderableIndex] = m_pipeline->RegisterLight(lightEntry.light.get(), lightEntry.renderMask); } } m_newlyVisibleGfxEntities.clear();