diff --git a/examples/Showcase/main.cpp b/examples/Showcase/main.cpp index f6140e612..c5fa2ce3b 100644 --- a/examples/Showcase/main.cpp +++ b/examples/Showcase/main.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -111,18 +112,27 @@ int main() const Nz::Boxf& bobAABB = bobMesh->GetAABB(); std::shared_ptr bobGfxMesh = Nz::GraphicalMesh::BuildFromMesh(*bobMesh); - std::shared_ptr material = Nz::Graphics::Instance()->GetDefaultMaterials().basicMaterial; + //std::shared_ptr material = Nz::Graphics::Instance()->GetDefaultMaterials().basicTransparent; std::shared_ptr bobModel = std::make_shared(std::move(bobGfxMesh), bobAABB); std::vector> materials(bobMesh->GetMaterialCount()); + std::bitset<5> alphaMaterials("01010"); for (std::size_t i = 0; i < bobMesh->GetMaterialCount(); ++i) { - materials[i] = std::make_shared(material); - - std::string matPath = bobMesh->GetMaterialData(i).GetStringParameter(Nz::MaterialData::BaseColorTexturePath).GetValueOr(""); + const Nz::ParameterList& materialData = bobMesh->GetMaterialData(i); + std::string matPath = materialData.GetStringParameter(Nz::MaterialData::BaseColorTexturePath).GetValueOr(""); if (!matPath.empty()) - materials[i]->SetTextureProperty("BaseColorMap", Nz::Texture::LoadFromFile(matPath, texParams)); + { + Nz::MaterialInstanceParams params; + params.lightingType = Nz::MaterialLightingType::Phong; + if (alphaMaterials.test(i)) + params.custom.SetParameter("EnableAlphaBlending", true); + + materials[i] = Nz::MaterialInstance::LoadFromFile(matPath, params); + } + else + materials[i] = Nz::Graphics::Instance()->GetDefaultMaterials().basicDefault; } for (std::size_t i = 0; i < bobMesh->GetSubMeshCount(); ++i) @@ -149,7 +159,21 @@ int main() }*/ entt::handle bobEntity = entt::handle(registry, registry.create()); + entt::entity bobLight = registry.create(); { + auto& lightNode = registry.emplace(bobLight); + lightNode.SetPosition(Nz::Vector3f::Up() * 3.f); + lightNode.SetRotation(Nz::EulerAnglesf(-90.f, 0.f, 0.f)); + + auto spotLight = std::make_shared(); + spotLight->UpdateAmbientFactor(1.f); + spotLight->UpdateInnerAngle(Nz::DegreeAnglef(15.f)); + spotLight->UpdateOuterAngle(Nz::DegreeAnglef(20.f)); + spotLight->EnableShadowCasting(true); + + auto& cameraLight = registry.emplace(bobLight); + cameraLight.AttachLight(spotLight, 0xFFFFFFFF); + auto& bobNode = bobEntity.emplace(); //bobNode.SetRotation(Nz::EulerAnglesf(-90.f, -90.f, 0.f)); //bobNode.SetScale(1.f / 40.f * 0.5f); @@ -172,9 +196,6 @@ int main() std::shared_ptr gfxMesh = Nz::GraphicalMesh::BuildFromMesh(*sphereMesh); // Textures - Nz::TextureParams texParams; - texParams.renderDevice = device; - Nz::TextureParams srgbTexParams = texParams; srgbTexParams.loadFormat = Nz::PixelFormat::RGBA8_SRGB; @@ -209,6 +230,7 @@ int main() entt::entity planeEntity = registry.create(); + Nz::Boxf floorBox; { Nz::MeshParams meshPrimitiveParams; meshPrimitiveParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV); @@ -230,13 +252,15 @@ int main() std::shared_ptr planeMat = Nz::Graphics::Instance()->GetDefaultMaterials().phongMaterial->Instantiate(); planeMat->SetTextureProperty("BaseColorMap", Nz::Texture::LoadFromFile(resourceDir / "dev_grey.png", texParams), planeSampler); + floorBox = planeMesh.GetAABB(); + std::shared_ptr planeModel = std::make_shared(std::move(planeMeshGfx), planeMesh.GetAABB()); planeModel->SetMaterial(0, planeMat); auto& planeGfx = registry.emplace(planeEntity); planeGfx.AttachRenderable(planeModel, 0xFFFFFFFF); - auto& planeNode = registry.emplace(planeEntity); + registry.emplace(planeEntity); auto& planeBody = registry.emplace(planeEntity, &physSytem.GetPhysWorld()); planeBody.SetGeom(std::make_shared(Nz::Vector3f(planeSize.x, 0.5f, planeSize.y), Nz::Vector3f(0.f, -0.25f, 0.f))); @@ -255,10 +279,6 @@ int main() while (window.IsOpen()) { - Nz::UInt64 now = Nz::GetElapsedMicroseconds(); - Nz::UInt64 elapsedTime = (now - lastTime) / 1'000'000.f; - lastTime = now; - Nz::WindowEvent event; while (window.PollEvent(&event)) { @@ -389,8 +409,14 @@ int main() playerShipBody.AddForce(Nz::Vector3f::Down() * 3.f * mass, Nz::CoordSys::Local);*/ } - Nz::DebugDrawer& debugDrawer = renderSystem.GetFramePipeline().GetDebugDrawer(); - //debugDrawer.DrawSkeleton(*skeleton, Nz::Color::Red); + + /*Nz::DebugDrawer& debugDrawer = renderSystem.GetFramePipeline().GetDebugDrawer(); + Nz::Boxf test = spotLight->GetBoundingVolume().aabb; + debugDrawer.DrawBox(test, Nz::Color::Blue); + debugDrawer.DrawBox(floorBox, Nz::Color::Red); + Nz::Boxf intersection; + if (floorBox.Intersect(test, &intersection)) + debugDrawer.DrawBox(intersection, Nz::Color::Green);*/ systemGraph.Update(); diff --git a/include/Nazara/Graphics/BakedFrameGraph.hpp b/include/Nazara/Graphics/BakedFrameGraph.hpp index f82ef2c9c..6ddc482ef 100644 --- a/include/Nazara/Graphics/BakedFrameGraph.hpp +++ b/include/Nazara/Graphics/BakedFrameGraph.hpp @@ -89,6 +89,7 @@ namespace Nz TextureUsageFlags usage; unsigned int width; unsigned int height; + bool hasFixedSize; }; std::shared_ptr m_commandPool; diff --git a/include/Nazara/Graphics/Camera.inl b/include/Nazara/Graphics/Camera.inl index 9e21b57c3..e7fd95397 100644 --- a/include/Nazara/Graphics/Camera.inl +++ b/include/Nazara/Graphics/Camera.inl @@ -178,12 +178,21 @@ namespace Nz inline void Camera::UpdateViewport(const Recti& viewport) { - NazaraAssert(m_renderTarget, "no render target"); + if (m_renderTarget) + { + // We compute the region necessary to make this view port with the actual size of the target + Vector2f invSize = 1.f / Vector2f(m_renderTarget->GetSize()); - // We compute the region necessary to make this view port with the actual size of the target - Vector2f invSize = 1.f / Vector2f(m_renderTarget->GetSize()); + UpdateTargetRegion(Rectf(invSize.x * viewport.x, invSize.y * viewport.y, invSize.x * viewport.width, invSize.y * viewport.height)); + } + else + { + m_aspectRatio = float(viewport.width) / float(viewport.height); + m_viewport = viewport; + m_viewerInstance.UpdateTargetSize(Vector2f(viewport.GetLengths())); - UpdateTargetRegion(Rectf(invSize.x * viewport.x, invSize.y * viewport.y, invSize.x * viewport.width, invSize.y * viewport.height)); + UpdateProjectionMatrix(); + } } inline void Camera::UpdateSize(const Vector2f& size) diff --git a/include/Nazara/Graphics/DebugDrawPipelinePass.hpp b/include/Nazara/Graphics/DebugDrawPipelinePass.hpp index 85e33e246..3a7873344 100644 --- a/include/Nazara/Graphics/DebugDrawPipelinePass.hpp +++ b/include/Nazara/Graphics/DebugDrawPipelinePass.hpp @@ -21,6 +21,7 @@ namespace Nz { class AbstractViewer; class FrameGraph; + class FramePass; class FramePipeline; class Material; @@ -34,7 +35,7 @@ namespace Nz void Prepare(RenderFrame& renderFrame); - void RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t inputColorBufferIndex, std::size_t outputColorBufferIndex); + FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t inputColorBufferIndex, std::size_t outputColorBufferIndex); DebugDrawPipelinePass& operator=(const DebugDrawPipelinePass&) = delete; DebugDrawPipelinePass& operator=(DebugDrawPipelinePass&&) = delete; diff --git a/include/Nazara/Graphics/DepthPipelinePass.hpp b/include/Nazara/Graphics/DepthPipelinePass.hpp index ab96c9b5e..4a50f9b80 100644 --- a/include/Nazara/Graphics/DepthPipelinePass.hpp +++ b/include/Nazara/Graphics/DepthPipelinePass.hpp @@ -24,6 +24,7 @@ namespace Nz class AbstractViewer; class ElementRendererRegistry; class FrameGraph; + class FramePass; class FramePipeline; class NAZARA_GRAPHICS_API DepthPipelinePass : public FramePipelinePass @@ -40,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); - void RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex); + FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex); void UnregisterMaterialInstance(const MaterialInstance& materialInstance); diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 7470526ff..3ffe59030 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -76,9 +77,13 @@ namespace Nz struct LightData { std::shared_ptr light; + std::size_t shadowMapAttachmentIndex; + std::unique_ptr camera; + std::unique_ptr pass; UInt32 renderMask; NazaraSlot(Light, OnLightDataInvalided, onLightInvalidated); + NazaraSlot(Light, OnLightShadowCastingChanged, onLightShadowCastingChanged); }; struct MaterialInstanceData @@ -145,6 +150,7 @@ namespace Nz std::vector m_visibleLights; robin_hood::unordered_set m_transferSet; BakedFrameGraph m_bakedFrameGraph; + Bitset m_shadowCastingLights; Bitset m_removedSkeletonInstances; Bitset m_removedViewerInstances; Bitset m_removedWorldInstances; diff --git a/include/Nazara/Graphics/ForwardPipelinePass.hpp b/include/Nazara/Graphics/ForwardPipelinePass.hpp index 9a95009e4..3392a0b7b 100644 --- a/include/Nazara/Graphics/ForwardPipelinePass.hpp +++ b/include/Nazara/Graphics/ForwardPipelinePass.hpp @@ -26,6 +26,7 @@ namespace Nz class AbstractViewer; class ElementRendererRegistry; class FrameGraph; + class FramePass; class FramePipeline; class Light; @@ -43,7 +44,7 @@ namespace Nz void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash); void RegisterMaterialInstance(const MaterialInstance& material); - void RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass); + FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass); void UnregisterMaterialInstance(const MaterialInstance& material); diff --git a/include/Nazara/Graphics/FrameGraph.hpp b/include/Nazara/Graphics/FrameGraph.hpp index 5f248498d..b0201bfde 100644 --- a/include/Nazara/Graphics/FrameGraph.hpp +++ b/include/Nazara/Graphics/FrameGraph.hpp @@ -91,6 +91,7 @@ namespace Nz TextureUsageFlags usage; unsigned int width; unsigned int height; + bool hasFixedSize; }; struct WorkData diff --git a/include/Nazara/Graphics/FramePassAttachment.hpp b/include/Nazara/Graphics/FramePassAttachment.hpp index ce921bda1..26b04ef63 100644 --- a/include/Nazara/Graphics/FramePassAttachment.hpp +++ b/include/Nazara/Graphics/FramePassAttachment.hpp @@ -20,6 +20,7 @@ namespace Nz PixelFormat format; unsigned int width = 100'000; unsigned int height = 100'000; + bool hasFixedSize = false; }; } diff --git a/include/Nazara/Graphics/Light.hpp b/include/Nazara/Graphics/Light.hpp index 817fa942e..a8c350696 100644 --- a/include/Nazara/Graphics/Light.hpp +++ b/include/Nazara/Graphics/Light.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,21 +21,32 @@ namespace Nz class CommandBufferBuilder; class RenderBuffer; class RenderFrame; + class Texture; class NAZARA_GRAPHICS_API Light { public: - inline Light(UInt8 lightType); + Light(UInt8 lightType); Light(const Light&) = delete; Light(Light&&) noexcept = default; virtual ~Light(); virtual float ComputeContributionScore(const BoundingVolumef& boundingVolume) const = 0; + inline void EnableShadowCasting(bool castShadows); + virtual void FillLightData(void* data) const = 0; inline const BoundingVolumef& GetBoundingVolume() const; inline UInt8 GetLightType() const; + inline PixelFormat GetShadowMapFormat() const; + inline UInt32 GetShadowMapSize() const; + + inline bool IsShadowCaster() const; + + inline void UpdateShadowMapFormat(PixelFormat format); + inline void UpdateShadowMapSettings(UInt32 size, PixelFormat format); + inline void UpdateShadowMapSize(UInt32 size); virtual void UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& scale) = 0; @@ -42,13 +54,18 @@ namespace Nz Light& operator=(Light&&) noexcept = default; NazaraSignal(OnLightDataInvalided, Light* /*emitter*/); + NazaraSignal(OnLightShadowCastingChanged, Light* /*light*/, bool /*isShadowCasting*/); + NazaraSignal(OnLightShadowMapSettingChange, Light* /*light*/, PixelFormat /*newPixelFormat*/, UInt32 /*newSize*/); protected: inline void UpdateBoundingVolume(const BoundingVolumef& boundingVolume); private: BoundingVolumef m_boundingVolume; + PixelFormat m_shadowMapFormat; UInt8 m_lightType; + UInt32 m_shadowMapSize; + bool m_isShadowCaster; }; } diff --git a/include/Nazara/Graphics/Light.inl b/include/Nazara/Graphics/Light.inl index 7a34eb78b..6aa243d3f 100644 --- a/include/Nazara/Graphics/Light.inl +++ b/include/Nazara/Graphics/Light.inl @@ -8,10 +8,13 @@ namespace Nz { - inline Light::Light(UInt8 lightType) : - m_boundingVolume(BoundingVolumef::Null()), - m_lightType(lightType) + inline void Light::EnableShadowCasting(bool castShadows) { + if (m_isShadowCaster != castShadows) + { + m_isShadowCaster = castShadows; + OnLightShadowCastingChanged(this, castShadows); + } } inline const BoundingVolumef& Light::GetBoundingVolume() const @@ -24,6 +27,58 @@ namespace Nz return m_lightType; } + inline PixelFormat Light::GetShadowMapFormat() const + { + return m_shadowMapFormat; + } + + inline UInt32 Light::GetShadowMapSize() const + { + return m_shadowMapSize; + } + + inline bool Light::IsShadowCaster() const + { + return m_isShadowCaster; + } + + inline void Light::UpdateShadowMapFormat(PixelFormat format) + { + if (m_shadowMapFormat != format) + { + PixelFormatContent content = PixelFormatInfo::GetContent(format); + NazaraAssert(content != PixelFormatContent::Depth && content != PixelFormatContent::DepthStencil, "invalid shadow map format (has no depth)"); + + OnLightShadowMapSettingChange(this, format, m_shadowMapSize); + m_shadowMapFormat = format; + } + } + + inline void Light::UpdateShadowMapSettings(UInt32 size, PixelFormat format) + { + if (m_shadowMapFormat != format || m_shadowMapSize != size) + { + NazaraAssert(size > 0, "invalid shadow map size"); + PixelFormatContent content = PixelFormatInfo::GetContent(format); + NazaraAssert(content != PixelFormatContent::Depth && content != PixelFormatContent::DepthStencil, "invalid shadow map format (has no depth)"); + + OnLightShadowMapSettingChange(this, format, size); + m_shadowMapFormat = format; + m_shadowMapSize = size; + } + } + + inline void Light::UpdateShadowMapSize(UInt32 size) + { + if (m_shadowMapSize != size) + { + NazaraAssert(size > 0, "invalid shadow map size"); + + OnLightShadowMapSettingChange(this, m_shadowMapFormat, size); + m_shadowMapSize = size; + } + } + inline void Light::UpdateBoundingVolume(const BoundingVolumef& boundingVolume) { m_boundingVolume = boundingVolume; diff --git a/src/Nazara/Graphics/BakedFrameGraph.cpp b/src/Nazara/Graphics/BakedFrameGraph.cpp index 5344faf0a..9152ebf4e 100644 --- a/src/Nazara/Graphics/BakedFrameGraph.cpp +++ b/src/Nazara/Graphics/BakedFrameGraph.cpp @@ -131,8 +131,8 @@ namespace Nz bool BakedFrameGraph::Resize(RenderFrame& renderFrame) { - auto [width, height] = renderFrame.GetSize(); - if (m_width == width && m_height == height) + auto [frameWidth, frameHeight] = renderFrame.GetSize(); + if (m_width == frameWidth && m_height == frameHeight) return false; const std::shared_ptr& renderDevice = Graphics::Instance()->GetRenderDevice(); @@ -151,10 +151,18 @@ namespace Nz { TextureInfo textureCreationParams; textureCreationParams.type = ImageType::E2D; - textureCreationParams.width = textureData.width * width / 100'000; - textureCreationParams.height = textureData.height * height / 100'000; textureCreationParams.usageFlags = textureData.usage; textureCreationParams.pixelFormat = textureData.format; + if (textureData.hasFixedSize) + { + textureCreationParams.width = textureData.width; + textureCreationParams.height = textureData.height; + } + else + { + textureCreationParams.width = textureData.width * frameWidth / 100'000; + textureCreationParams.height = textureData.height * frameHeight / 100'000; + } textureData.texture = renderDevice->InstantiateTexture(textureCreationParams); if (!textureData.name.empty()) @@ -173,12 +181,22 @@ namespace Nz auto& textureData = m_textures[textureId]; textures.push_back(textureData.texture); - framebufferWidth = std::min(framebufferWidth, textureData.width); - framebufferHeight = std::min(framebufferHeight, textureData.height); - } + unsigned int width; + unsigned int height; + if (textureData.hasFixedSize) + { + width = textureData.width; + height = textureData.height; + } + else + { + width = frameWidth * textureData.width / 100'000; + height = frameHeight * textureData.height / 100'000; + } - framebufferWidth = framebufferWidth * width / 100'000; - framebufferHeight = framebufferHeight * height / 100'000; + framebufferWidth = std::min(framebufferWidth, width); + framebufferHeight = std::min(framebufferHeight, height); + } passData.renderRect.Set(0, 0, int(framebufferWidth), int(framebufferHeight)); @@ -189,8 +207,8 @@ namespace Nz passData.forceCommandBufferRegeneration = true; } - m_width = width; - m_height = height; + m_width = frameWidth; + m_height = frameHeight; return true; } diff --git a/src/Nazara/Graphics/DebugDrawPipelinePass.cpp b/src/Nazara/Graphics/DebugDrawPipelinePass.cpp index 2eb17dddf..1daf10d4a 100644 --- a/src/Nazara/Graphics/DebugDrawPipelinePass.cpp +++ b/src/Nazara/Graphics/DebugDrawPipelinePass.cpp @@ -26,7 +26,7 @@ namespace Nz debugDrawer.Prepare(renderFrame); } - void DebugDrawPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t inputColorBufferIndex, std::size_t outputColorBufferIndex) + FramePass& DebugDrawPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t inputColorBufferIndex, std::size_t outputColorBufferIndex) { FramePass& debugDrawPass = frameGraph.AddPass("Debug draw pass"); debugDrawPass.AddInput(inputColorBufferIndex); @@ -47,5 +47,7 @@ namespace Nz DebugDrawer& debugDrawer = m_pipeline.GetDebugDrawer(); debugDrawer.Draw(builder); }); + + return debugDrawPass; } } diff --git a/src/Nazara/Graphics/DepthPipelinePass.cpp b/src/Nazara/Graphics/DepthPipelinePass.cpp index eb183d7cc..72cf9ca04 100644 --- a/src/Nazara/Graphics/DepthPipelinePass.cpp +++ b/src/Nazara/Graphics/DepthPipelinePass.cpp @@ -129,7 +129,7 @@ namespace Nz it->second.usedCount++; } - void DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex) + FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex) { FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass"); depthPrepass.SetDepthStencilOutput(depthBufferIndex); @@ -157,6 +157,8 @@ namespace Nz m_rebuildCommandBuffer = false; }); + + return depthPrepass; } void DepthPipelinePass::UnregisterMaterialInstance(const MaterialInstance& materialInstance) diff --git a/src/Nazara/Graphics/DirectionalLight.cpp b/src/Nazara/Graphics/DirectionalLight.cpp index c560a759a..89c34693c 100644 --- a/src/Nazara/Graphics/DirectionalLight.cpp +++ b/src/Nazara/Graphics/DirectionalLight.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 2f5ffda80..1ade55421 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,25 @@ namespace Nz } }); + lightData->onLightShadowCastingChanged.Connect(lightData->light->OnLightShadowCastingChanged, [=](Light* light, bool isCastingShadows) + { + if (isCastingShadows) + m_shadowCastingLights.UnboundedSet(lightIndex); + else + { + m_shadowCastingLights.Reset(lightIndex); + lightData->pass.reset(); + } + + m_rebuildFrameGraph = true; + }); + + if (lightData->light->IsShadowCaster()) + { + m_shadowCastingLights.UnboundedSet(lightIndex); + m_rebuildFrameGraph = true; + } + return lightIndex; } @@ -248,6 +268,48 @@ namespace Nz 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); + } + // Render queues handling for (auto& viewerData : m_viewerPool) { @@ -398,6 +460,7 @@ namespace Nz void ForwardFramePipeline::UnregisterLight(std::size_t lightIndex) { m_lightPool.Free(lightIndex); + m_shadowCastingLights.UnboundedReset(lightIndex); } void ForwardFramePipeline::UnregisterRenderable(std::size_t renderableIndex) @@ -486,6 +549,56 @@ namespace Nz { FrameGraph frameGraph; + 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, + shadowMapSize, shadowMapSize, + true // fixed size + }); + + 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()); + lightData->camera->UpdateZFar(spotLight.GetRadius()); + lightData->camera->UpdateViewport(Recti(0, 0, SafeCast(shadowMapSize), SafeCast(shadowMapSize))); + + viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(spotLight.GetPosition(), spotLight.GetRotation())); + } + + if (!lightData->pass) + { + lightData->pass = std::make_unique(*this, m_elementRegistry, lightData->camera.get()); + for (RenderableData& renderable : m_renderablePool) + { + std::size_t matCount = renderable.renderable->GetMaterialCount(); + for (std::size_t i = 0; i < matCount; ++i) + { + if (MaterialInstance* mat = renderable.renderable->GetMaterial(i).get()) + lightData->pass->RegisterMaterialInstance(*mat); + } + } + + lightData->pass->RegisterToFrameGraph(frameGraph, lightData->shadowMapAttachmentIndex); + } + } + for (auto& viewerData : m_viewerPool) { viewerData.forwardColorAttachment = frameGraph.AddAttachment({ @@ -503,7 +616,12 @@ namespace Nz if (viewerData.depthPrepass) viewerData.depthPrepass->RegisterToFrameGraph(frameGraph, viewerData.depthStencilAttachment); - viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.forwardColorAttachment, viewerData.depthStencilAttachment, viewerData.depthPrepass != nullptr); + FramePass& forwardPass = viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.forwardColorAttachment, viewerData.depthStencilAttachment, viewerData.depthPrepass != nullptr); + 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); + } viewerData.debugDrawPass->RegisterToFrameGraph(frameGraph, viewerData.forwardColorAttachment, viewerData.debugColorAttachment); } @@ -548,7 +666,7 @@ namespace Nz mergePass.AddOutput(renderTargetData.finalAttachment); mergePass.SetClearColor(0, Color::Black); - mergePass.SetCommandCallback([&targetViewers](CommandBufferBuilder& builder, const Nz::FramePassEnvironment& env) + mergePass.SetCommandCallback([&targetViewers](CommandBufferBuilder& builder, const FramePassEnvironment& env) { builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); diff --git a/src/Nazara/Graphics/ForwardPipelinePass.cpp b/src/Nazara/Graphics/ForwardPipelinePass.cpp index a860a6ddc..6727a646e 100644 --- a/src/Nazara/Graphics/ForwardPipelinePass.cpp +++ b/src/Nazara/Graphics/ForwardPipelinePass.cpp @@ -265,7 +265,7 @@ namespace Nz it->second.usedCount++; } - void ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass) + FramePass& ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass) { FramePass& forwardPass = frameGraph.AddPass("Forward pass"); forwardPass.AddOutput(colorBufferIndex); @@ -299,6 +299,8 @@ namespace Nz m_rebuildCommandBuffer = false; }); + + return forwardPass; } void ForwardPipelinePass::UnregisterMaterialInstance(const MaterialInstance& materialInstance) diff --git a/src/Nazara/Graphics/FrameGraph.cpp b/src/Nazara/Graphics/FrameGraph.cpp index 5d467d41e..08936469c 100644 --- a/src/Nazara/Graphics/FrameGraph.cpp +++ b/src/Nazara/Graphics/FrameGraph.cpp @@ -115,6 +115,7 @@ namespace Nz auto& bakedTexture = bakedTextures.emplace_back(); bakedTexture.name = std::move(texture.name); bakedTexture.format = texture.format; + bakedTexture.hasFixedSize = texture.hasFixedSize; bakedTexture.height = texture.height; bakedTexture.usage = texture.usage; bakedTexture.width = texture.width; @@ -952,7 +953,8 @@ namespace Nz TextureData& data = m_pending.textures[textureId]; if (data.format != attachmentData.format || data.width != attachmentData.width || - data.height != attachmentData.height) + data.height != attachmentData.height || + data.hasFixedSize != attachmentData.hasFixedSize) continue; m_pending.texturePool.erase(it); @@ -972,6 +974,7 @@ namespace Nz data.format = attachmentData.format; data.width = attachmentData.width; data.height = attachmentData.height; + data.hasFixedSize = attachmentData.hasFixedSize; return textureId; } diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 0469d8a6b..cb5ab0032 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -81,6 +81,7 @@ namespace Nz */ Graphics::Graphics(Config config) : ModuleBase("Graphics", this), + m_preferredDepthFormat(PixelFormat::Undefined), m_preferredDepthStencilFormat(PixelFormat::Undefined) { Renderer* renderer = Renderer::Instance(); @@ -213,6 +214,7 @@ namespace Nz void Graphics::BuildDefaultMaterials() { std::size_t depthPassIndex = m_materialPassRegistry.GetPassIndex("DepthPass"); + std::size_t shadowPassIndex = m_materialPassRegistry.GetPassIndex("ShadowPass"); std::size_t forwardPassIndex = m_materialPassRegistry.GetPassIndex("ForwardPass"); // BasicMaterial @@ -228,6 +230,7 @@ namespace Nz MaterialPass depthPass = forwardPass; depthPass.options[CRC32("DepthPass")] = true; settings.AddPass(depthPassIndex, depthPass); + settings.AddPass(shadowPassIndex, depthPass); m_defaultMaterials.basicMaterial = std::make_shared(std::move(settings), "BasicMaterial"); } @@ -246,6 +249,7 @@ namespace Nz MaterialPass depthPass = forwardPass; depthPass.options[CRC32("DepthPass")] = true; settings.AddPass(depthPassIndex, depthPass); + settings.AddPass(shadowPassIndex, depthPass); m_defaultMaterials.pbrMaterial = std::make_shared(std::move(settings), "PhysicallyBasedMaterial"); } @@ -264,6 +268,7 @@ namespace Nz MaterialPass depthPass = forwardPass; depthPass.options[CRC32("DepthPass")] = true; settings.AddPass(depthPassIndex, depthPass); + settings.AddPass(shadowPassIndex, depthPass); m_defaultMaterials.phongMaterial = std::make_shared(std::move(settings), "PhongMaterial"); } @@ -272,6 +277,7 @@ namespace Nz m_defaultMaterials.basicNoDepth = m_defaultMaterials.basicMaterial->Instantiate(); m_defaultMaterials.basicNoDepth->DisablePass(depthPassIndex); + m_defaultMaterials.basicNoDepth->DisablePass(shadowPassIndex); m_defaultMaterials.basicNoDepth->UpdatePassStates(forwardPassIndex, [](RenderStates& states) { states.depthBuffer = false; @@ -279,6 +285,7 @@ namespace Nz m_defaultMaterials.basicTransparent = m_defaultMaterials.basicMaterial->Instantiate(); m_defaultMaterials.basicTransparent->DisablePass(depthPassIndex); + m_defaultMaterials.basicTransparent->DisablePass(shadowPassIndex); m_defaultMaterials.basicTransparent->UpdatePassStates(forwardPassIndex, [](RenderStates& renderStates) { renderStates.depthWrite = false; @@ -316,6 +323,7 @@ namespace Nz { m_materialPassRegistry.RegisterPass("ForwardPass"); m_materialPassRegistry.RegisterPass("DepthPass"); + m_materialPassRegistry.RegisterPass("ShadowPass"); } void Graphics::RegisterShaderModules() diff --git a/src/Nazara/Graphics/Light.cpp b/src/Nazara/Graphics/Light.cpp index dc94e501e..e3c01592c 100644 --- a/src/Nazara/Graphics/Light.cpp +++ b/src/Nazara/Graphics/Light.cpp @@ -3,9 +3,19 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include namespace Nz { + Light::Light(UInt8 lightType) : + m_boundingVolume(BoundingVolumef::Null()), + m_shadowMapFormat(Graphics::Instance()->GetPreferredDepthFormat()), + m_shadowMapSize(512), + m_lightType(lightType), + m_isShadowCaster(false) + { + } + Light::~Light() = default; } diff --git a/src/Nazara/Graphics/PointLight.cpp b/src/Nazara/Graphics/PointLight.cpp index 70ae8715c..44894f1e2 100644 --- a/src/Nazara/Graphics/PointLight.cpp +++ b/src/Nazara/Graphics/PointLight.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl index ab2589167..7d30fcaee 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl @@ -38,6 +38,8 @@ option VertexUvLoc: i32 = -1; option VertexJointIndicesLoc: i32 = -1; option VertexJointWeightsLoc: i32 = -1; +option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types + const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); @@ -93,7 +95,9 @@ external [tag("InstanceData")] instanceData: uniform[InstanceData], [tag("ViewerData")] viewerData: uniform[ViewerData], [tag("SkeletalData")] skeletalData: uniform[SkeletalData], - [tag("LightData")] lightData: uniform[LightData] + [tag("LightData")] lightData: uniform[LightData], + [tag("ShadowMaps2D")] shadowMaps2D: array[sampler2D[f32], MaxLightCount], + [tag("ShadowMapsCube")] shadowMapsCube: array[samplerCube[f32], MaxLightCount] } struct VertToFrag diff --git a/src/Nazara/Graphics/SpotLight.cpp b/src/Nazara/Graphics/SpotLight.cpp index 62ed9acac..e740307c3 100644 --- a/src/Nazara/Graphics/SpotLight.cpp +++ b/src/Nazara/Graphics/SpotLight.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include