From ebbaaf7ff2b4b7fb69192ce9ec086e91ce22287f Mon Sep 17 00:00:00 2001 From: Lynix Date: Wed, 17 Jun 2015 14:32:05 +0200 Subject: [PATCH] Graphics: Add depth render technique/queue Former-commit-id: 711306ee5f84a9579068ce23240dc105cec15cde --- SDK/include/NDK/Systems/RenderSystem.hpp | 3 +- SDK/src/NDK/Systems/RenderSystem.cpp | 19 +- include/Nazara/Graphics/DepthRenderQueue.hpp | 76 ++++ .../Nazara/Graphics/DepthRenderTechnique.hpp | 53 +++ .../Nazara/Graphics/DepthRenderTechnique.inl | 3 + include/Nazara/Graphics/Enums.hpp | 1 + src/Nazara/Graphics/DepthRenderQueue.cpp | 377 ++++++++++++++++++ src/Nazara/Graphics/DepthRenderTechnique.cpp | 363 +++++++++++++++++ src/Nazara/Graphics/Graphics.cpp | 8 + src/Nazara/Graphics/RenderTechniques.cpp | 1 + 10 files changed, 890 insertions(+), 14 deletions(-) create mode 100644 include/Nazara/Graphics/DepthRenderQueue.hpp create mode 100644 include/Nazara/Graphics/DepthRenderTechnique.hpp create mode 100644 include/Nazara/Graphics/DepthRenderTechnique.inl create mode 100644 src/Nazara/Graphics/DepthRenderQueue.cpp create mode 100644 src/Nazara/Graphics/DepthRenderTechnique.cpp diff --git a/SDK/include/NDK/Systems/RenderSystem.hpp b/SDK/include/NDK/Systems/RenderSystem.hpp index 0baae7684..b06fe7ddc 100644 --- a/SDK/include/NDK/Systems/RenderSystem.hpp +++ b/SDK/include/NDK/Systems/RenderSystem.hpp @@ -7,6 +7,7 @@ #ifndef NDK_SYSTEMS_RENDERSYSTEM_HPP #define NDK_SYSTEMS_RENDERSYSTEM_HPP +#include #include #include #include @@ -37,8 +38,8 @@ namespace Ndk EntityList m_cameras; EntityList m_drawables; EntityList m_lights; + NzDepthRenderTechnique m_shadowTechnique; NzForwardRenderTechnique m_renderTechnique; - NzForwardRenderTechnique m_shadowTechnique; NzRenderTexture m_shadowRT; }; } diff --git a/SDK/src/NDK/Systems/RenderSystem.cpp b/SDK/src/NDK/Systems/RenderSystem.cpp index 4f6f27f86..fa7ea650a 100644 --- a/SDK/src/NDK/Systems/RenderSystem.cpp +++ b/SDK/src/NDK/Systems/RenderSystem.cpp @@ -100,24 +100,17 @@ namespace Ndk if (!lightComponent.IsShadowCastingEnabled() || lightComponent.GetLightType() != nzLightType_Spot) continue; - /// HACKY - NzCamera lightPOV; - lightPOV.SetPosition(lightNode.GetPosition()); - lightPOV.SetFOV(lightComponent.GetOuterAngle()); - lightPOV.SetRotation(lightNode.GetRotation()); - lightPOV.SetZFar(1000.f); - lightPOV.SetTarget(&m_shadowRT); - NzVector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize()); m_shadowRT.AttachTexture(nzAttachmentPoint_Depth, 0, lightComponent.GetShadowMap()); - NzRenderer::SetMatrix(nzMatrixType_Projection, lightPOV.GetProjectionMatrix()); - NzRenderer::SetMatrix(nzMatrixType_View, lightPOV.GetViewMatrix()); + ///TODO: Cache the matrices in the light? + NzRenderer::SetMatrix(nzMatrixType_Projection, NzMatrix4f::Perspective(lightComponent.GetOuterAngle(), 1.f, 1.f, 1000.f)); + NzRenderer::SetMatrix(nzMatrixType_View, NzMatrix4f::ViewMatrix(lightNode.GetPosition(), lightNode.GetRotation())); NzRenderer::SetTarget(&m_shadowRT); NzRenderer::SetViewport(NzRecti(0, 0, shadowMapSize.x, shadowMapSize.y)); - NzAbstractRenderQueue* renderQueue = m_renderTechnique.GetRenderQueue(); + NzAbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue(); renderQueue->Clear(); for (const Ndk::EntityHandle& drawable : m_drawables) @@ -131,9 +124,9 @@ namespace Ndk NzSceneData sceneData; sceneData.ambientColor = NzColor(0, 0, 0); sceneData.background = nullptr; - sceneData.viewer = &lightPOV; + sceneData.viewer = nullptr; //< Depth technique doesn't require any viewer - m_renderTechnique.Draw(sceneData); + m_shadowTechnique.Draw(sceneData); } } diff --git a/include/Nazara/Graphics/DepthRenderQueue.hpp b/include/Nazara/Graphics/DepthRenderQueue.hpp new file mode 100644 index 000000000..ec6550494 --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderQueue.hpp @@ -0,0 +1,76 @@ +// Copyright (C) 2015 Jérôme Leclercq +// 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_DEPTHRENDERQUEUE_HPP +#define NAZARA_DEPTHRENDERQUEUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class NAZARA_GRAPHICS_API NzDepthRenderQueue : public NzAbstractRenderQueue +{ + public: + NzDepthRenderQueue() = default; + ~NzDepthRenderQueue() = default; + + void AddBillboard(const NzMaterial* material, const NzVector3f& position, const NzVector2f& size, const NzVector2f& sinCos = NzVector2f(0.f, 1.f), const NzColor& color = NzColor::White) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr = nullptr, NzSparsePtr colorPtr = nullptr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr alphaPtr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr colorPtr = nullptr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr alphaPtr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr = nullptr, NzSparsePtr colorPtr = nullptr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr alphaPtr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr colorPtr = nullptr) override; + void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr alphaPtr) override; + void AddDrawable(const NzDrawable* drawable) override; + void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) override; + void AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay = nullptr) override; + + void Clear(bool fully = false); + + struct BillboardData + { + NzColor color; + NzVector3f center; + NzVector2f size; + NzVector2f sinCos; + }; + + struct MeshInstanceEntry + { + NazaraSlot(NzIndexBuffer, OnIndexBufferRelease, indexBufferReleaseSlot); + NazaraSlot(NzVertexBuffer, OnVertexBufferRelease, vertexBufferReleaseSlot); + + std::vector instances; + }; + + struct SpriteChain_XYZ_Color_UV + { + const NzVertexStruct_XYZ_Color_UV* vertices; + unsigned int spriteCount; + }; + + std::map meshes; + std::vector billboards; + std::vector otherDrawables; + std::vector basicSprites; + + private: + bool IsMaterialSuitable(const NzMaterial* material) const; + + void OnIndexBufferInvalidation(const NzIndexBuffer* indexBuffer); + void OnVertexBufferInvalidation(const NzVertexBuffer* vertexBuffer); +}; + +#endif // NAZARA_DEPTHRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/DepthRenderTechnique.hpp b/include/Nazara/Graphics/DepthRenderTechnique.hpp new file mode 100644 index 000000000..b33a2b6cc --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderTechnique.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2015 Jérôme Leclercq +// 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_DEPTHRENDERTECHNIQUE_HPP +#define NAZARA_DEPTHRENDERTECHNIQUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class NAZARA_GRAPHICS_API NzDepthRenderTechnique : public NzAbstractRenderTechnique +{ + public: + NzDepthRenderTechnique(); + ~NzDepthRenderTechnique() = default; + + bool Draw(const NzSceneData& sceneData) const override; + + NzAbstractRenderQueue* GetRenderQueue() override; + nzRenderTechniqueType GetType() const override; + + static bool Initialize(); + static void Uninitialize(); + + private: + void DrawBasicSprites(const NzSceneData& sceneData) const; + void DrawBillboards(const NzSceneData& sceneData) const; + void DrawOpaqueModels(const NzSceneData& sceneData) const; + + NzBuffer m_vertexBuffer; + mutable NzDepthRenderQueue m_renderQueue; + NzVertexBuffer m_billboardPointBuffer; + NzVertexBuffer m_spriteBuffer; + + static NzIndexBuffer s_quadIndexBuffer; + static NzMaterialRef s_material; + static NzVertexBuffer s_quadVertexBuffer; + static NzVertexDeclaration s_billboardInstanceDeclaration; + static NzVertexDeclaration s_billboardVertexDeclaration; +}; + +#include + +#endif // NAZARA_DEPTHRENDERTECHNIQUE_HPP diff --git a/include/Nazara/Graphics/DepthRenderTechnique.inl b/include/Nazara/Graphics/DepthRenderTechnique.inl new file mode 100644 index 000000000..ac6b052d0 --- /dev/null +++ b/include/Nazara/Graphics/DepthRenderTechnique.inl @@ -0,0 +1,3 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index 1e301ecb9..70002d7cf 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -98,6 +98,7 @@ enum nzRenderTechniqueType nzRenderTechniqueType_AdvancedForward, // NzAdvancedForwardRenderTechnique nzRenderTechniqueType_BasicForward, // NzBasicForwardRenderTechnique nzRenderTechniqueType_DeferredShading, // NzDeferredRenderTechnique + nzRenderTechniqueType_Depth, // NzDepthRenderTechnique nzRenderTechniqueType_LightPrePass, // NzLightPrePassRenderTechnique nzRenderTechniqueType_User, diff --git a/src/Nazara/Graphics/DepthRenderQueue.cpp b/src/Nazara/Graphics/DepthRenderQueue.cpp new file mode 100644 index 000000000..613033015 --- /dev/null +++ b/src/Nazara/Graphics/DepthRenderQueue.cpp @@ -0,0 +1,377 @@ +// Copyright (C) 2015 Jérôme Leclercq +// 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 + +///TODO: Remplacer les sinus/cosinus par une lookup table (va booster les perfs d'un bon x10) + +void NzDepthRenderQueue::AddBillboard(const NzMaterial* material, const NzVector3f& position, const NzVector2f& size, const NzVector2f& sinCos, const NzColor& color) +{ + NazaraAssert(material, "Invalid material"); + + if (IsMaterialSuitable(material)) + billboards.push_back(BillboardData{color, position, size, sinCos}); +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr colorPtr) +{ + ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + NzVector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + if (!colorPtr) + colorPtr.Reset(&NzColor::White, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + billboardData->center = *positionPtr++; + billboardData->color = *colorPtr++; + billboardData->sinCos = *sinCosPtr++; + billboardData->size = *sizePtr++; + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr alphaPtr) +{ + ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + NzVector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + billboardData->center = *positionPtr++; + billboardData->color = NzColor(255, 255, 255, static_cast(255.f * (*alphaPtr++))); + billboardData->sinCos = *sinCosPtr++; + billboardData->size = *sizePtr++; + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr colorPtr) +{ + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + if (!colorPtr) + colorPtr.Reset(&NzColor::White, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + float sin = std::sin(NzToRadians(*anglePtr)); + float cos = std::cos(NzToRadians(*anglePtr)); + anglePtr++; + + billboardData->center = *positionPtr++; + billboardData->color = *colorPtr++; + billboardData->sinCos.Set(sin, cos); + billboardData->size = *sizePtr++; + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr alphaPtr) +{ + ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + float sin = std::sin(NzToRadians(*anglePtr)); + float cos = std::cos(NzToRadians(*anglePtr)); + anglePtr++; + + billboardData->center = *positionPtr++; + billboardData->color = NzColor(255, 255, 255, static_cast(255.f * (*alphaPtr++))); + billboardData->sinCos.Set(sin, cos); + billboardData->size = *sizePtr++; + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr colorPtr) +{ + ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + NzVector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + if (!colorPtr) + colorPtr.Reset(&NzColor::White, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + billboardData->center = *positionPtr++; + billboardData->color = *colorPtr++; + billboardData->sinCos = *sinCosPtr++; + billboardData->size.Set(*sizePtr++); + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr alphaPtr) +{ + ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + NzVector2f defaultSinCos(0.f, 1.f); // sin(0) = 0, cos(0) = 1 + + if (!sinCosPtr) + sinCosPtr.Reset(&defaultSinCos, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + billboardData->center = *positionPtr++; + billboardData->color = NzColor(255, 255, 255, static_cast(255.f * (*alphaPtr++))); + billboardData->sinCos = *sinCosPtr++; + billboardData->size.Set(*sizePtr++); + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr colorPtr) +{ + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + if (!colorPtr) + colorPtr.Reset(&NzColor::White, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + float sin = std::sin(NzToRadians(*anglePtr)); + float cos = std::cos(NzToRadians(*anglePtr)); + anglePtr++; + + billboardData->center = *positionPtr++; + billboardData->color = *colorPtr++; + billboardData->sinCos.Set(sin, cos); + billboardData->size.Set(*sizePtr++); + billboardData++; + } +} + +void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr alphaPtr) +{ + ///DOC: sinCosPtr et alphaPtr peuvent être nuls, ils seront remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + float defaultRotation = 0.f; + + if (!anglePtr) + anglePtr.Reset(&defaultRotation, 0); // L'astuce ici est de mettre le stride sur zéro, rendant le pointeur immobile + + float defaultAlpha = 1.f; + + if (!alphaPtr) + alphaPtr.Reset(&defaultAlpha, 0); // Pareil + + unsigned int prevSize = billboards.size(); + billboards.resize(prevSize + count); + + BillboardData* billboardData = &billboards[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + float sin = std::sin(NzToRadians(*anglePtr)); + float cos = std::cos(NzToRadians(*anglePtr)); + anglePtr++; + + billboardData->center = *positionPtr++; + billboardData->color = NzColor(255, 255, 255, static_cast(255.f * (*alphaPtr++))); + billboardData->sinCos.Set(sin, cos); + billboardData->size.Set(*sizePtr++); + billboardData++; + } +} + +void NzDepthRenderQueue::AddDrawable(const NzDrawable* drawable) +{ + #if NAZARA_GRAPHICS_SAFE + if (!drawable) + { + NazaraError("Invalid drawable"); + return; + } + #endif + + otherDrawables.push_back(drawable); +} + +void NzDepthRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) +{ + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + auto it = meshes.find(meshData); + if (it == meshes.end()) + { + MeshInstanceEntry instanceEntry; + + if (meshData.indexBuffer) + instanceEntry.indexBufferReleaseSlot.Connect(meshData.indexBuffer->OnIndexBufferRelease, this, &NzDepthRenderQueue::OnIndexBufferInvalidation); + + instanceEntry.vertexBufferReleaseSlot.Connect(meshData.vertexBuffer->OnVertexBufferRelease, this, &NzDepthRenderQueue::OnVertexBufferInvalidation); + + it = meshes.insert(std::make_pair(meshData, std::move(instanceEntry))).first; + } + + std::vector& instances = it->second.instances; + instances.push_back(transformMatrix); + + // Avons-nous suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ? + //if (instances.size() >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT) + // entry.instancingEnabled = true; // Apparemment oui, activons l'instancing avec ce matériau +} + +void NzDepthRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay) +{ + NazaraAssert(material, "Invalid material"); + + if (!IsMaterialSuitable(material)) + return; + + basicSprites.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); +} + +void NzDepthRenderQueue::Clear(bool fully) +{ + NzAbstractRenderQueue::Clear(fully); + + basicSprites.clear(); + billboards.clear(); + otherDrawables.clear(); + + if (fully) + meshes.clear(); +} + +bool NzDepthRenderQueue::IsMaterialSuitable(const NzMaterial* material) const +{ + NazaraAssert(material, "Invalid material"); + + return material->IsEnabled(nzRendererParameter_DepthBuffer) && material->IsEnabled(nzRendererParameter_DepthWrite); +} + +void NzDepthRenderQueue::OnIndexBufferInvalidation(const NzIndexBuffer* indexBuffer) +{ + for (auto it = meshes.begin(); it != meshes.end();) + { + const NzMeshData& renderData = it->first; + if (renderData.indexBuffer == indexBuffer) + it = meshes.erase(it); + else + ++it; + } +} + +void NzDepthRenderQueue::OnVertexBufferInvalidation(const NzVertexBuffer* vertexBuffer) +{ + for (auto it = meshes.begin(); it != meshes.end();) + { + const NzMeshData& renderData = it->first; + if (renderData.vertexBuffer == vertexBuffer) + it = meshes.erase(it); + else + ++it; + } +} + diff --git a/src/Nazara/Graphics/DepthRenderTechnique.cpp b/src/Nazara/Graphics/DepthRenderTechnique.cpp new file mode 100644 index 000000000..c6da59276 --- /dev/null +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -0,0 +1,363 @@ +// Copyright (C) 2015 Jérôme Leclercq +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + struct BillboardPoint + { + NzColor color; + NzVector3f position; + NzVector2f size; + NzVector2f sinCos; // must follow `size` (both will be sent as a Vector4f) + NzVector2f uv; + }; + + static_assert(NzOffsetOf(BillboardPoint, sinCos) - NzOffsetOf(BillboardPoint, size) == sizeof(NzVector2f), "size and sinCos members should be packed"); + + unsigned int s_maxQuads = std::numeric_limits::max()/6; + unsigned int s_vertexBufferSize = 4*1024*1024; // 4 MiB +} + +NzDepthRenderTechnique::NzDepthRenderTechnique() : +m_vertexBuffer(nzBufferType_Vertex) +{ + NzErrorFlags flags(nzErrorFlag_ThrowException, true); + + m_vertexBuffer.Create(s_vertexBufferSize, nzDataStorage_Hardware, nzBufferUsage_Dynamic); + + m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer); + m_spriteBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Color_UV), &m_vertexBuffer); +} + +bool NzDepthRenderTechnique::Draw(const NzSceneData& sceneData) const +{ + NzRenderer::Enable(nzRendererParameter_DepthBuffer, true); + NzRenderer::Enable(nzRendererParameter_DepthWrite, true); + NzRenderer::Clear(nzRendererBuffer_Depth); + + // Just in case the background does render depth + if (sceneData.background) + sceneData.background->Draw(sceneData.viewer); + + if (!m_renderQueue.meshes.empty()) + DrawOpaqueModels(sceneData); + + if (!m_renderQueue.basicSprites.empty()) + DrawBasicSprites(sceneData); + + if (!m_renderQueue.billboards.empty()) + DrawBillboards(sceneData); + + // Other custom drawables + for (const NzDrawable* drawable : m_renderQueue.otherDrawables) + drawable->Draw(); + + return true; +} + +NzAbstractRenderQueue* NzDepthRenderTechnique::GetRenderQueue() +{ + return &m_renderQueue; +} + +nzRenderTechniqueType NzDepthRenderTechnique::GetType() const +{ + return nzRenderTechniqueType_BasicForward; +} + +bool NzDepthRenderTechnique::Initialize() +{ + try + { + NzErrorFlags flags(nzErrorFlag_ThrowException, true); + + s_quadIndexBuffer.Reset(false, s_maxQuads*6, nzDataStorage_Hardware, nzBufferUsage_Static); + + NzBufferMapper mapper(s_quadIndexBuffer, nzBufferAccess_WriteOnly); + nzUInt16* indices = static_cast(mapper.GetPointer()); + + for (unsigned int i = 0; i < s_maxQuads; ++i) + { + *indices++ = i*4 + 0; + *indices++ = i*4 + 2; + *indices++ = i*4 + 1; + + *indices++ = i*4 + 2; + *indices++ = i*4 + 3; + *indices++ = i*4 + 1; + } + + mapper.Unmap(); // Inutile de garder le buffer ouvert plus longtemps + + // Quad buffer (utilisé pour l'instancing de billboard et de sprites) + //Note: Les UV sont calculés dans le shader + s_quadVertexBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_XY), 4, nzDataStorage_Hardware, nzBufferUsage_Static); + + float vertices[2*4] = { + -0.5f, -0.5f, + 0.5f, -0.5f, + -0.5f, 0.5f, + 0.5f, 0.5f, + }; + + s_quadVertexBuffer.FillRaw(vertices, 0, sizeof(vertices)); + + // Déclaration lors du rendu des billboards par sommet + s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(BillboardPoint, color)); + s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(BillboardPoint, position)); + s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(BillboardPoint, uv)); + s_billboardVertexDeclaration.EnableComponent(nzVertexComponent_Userdata0, nzComponentType_Float4, NzOffsetOf(BillboardPoint, size)); // Englobe sincos + + // Declaration utilisée lors du rendu des billboards par instancing + // L'avantage ici est la copie directe (std::memcpy) des données de la RenderQueue vers le buffer GPU + s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData0, nzComponentType_Float3, NzOffsetOf(NzForwardRenderQueue::BillboardData, center)); + s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData1, nzComponentType_Float4, NzOffsetOf(NzForwardRenderQueue::BillboardData, size)); // Englobe sincos + s_billboardInstanceDeclaration.EnableComponent(nzVertexComponent_InstanceData2, nzComponentType_Color, NzOffsetOf(NzForwardRenderQueue::BillboardData, color)); + + // Material + s_material = NzMaterial::New(); + s_material->Enable(nzRendererParameter_ColorWrite, false); + s_material->Enable(nzRendererParameter_FaceCulling, false); + } + catch (const std::exception& e) + { + NazaraError("Failed to initialise: " + NzString(e.what())); + return false; + } + + return true; +} + +void NzDepthRenderTechnique::Uninitialize() +{ + s_material.Reset(); + s_quadIndexBuffer.Reset(); + s_quadVertexBuffer.Reset(); +} + +void NzDepthRenderTechnique::DrawBasicSprites(const NzSceneData& sceneData) const +{ + NzRenderer::SetIndexBuffer(&s_quadIndexBuffer); + NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity()); + NzRenderer::SetVertexBuffer(&m_spriteBuffer); + + auto& spriteChainVector = m_renderQueue.basicSprites; + unsigned int spriteChainCount = spriteChainVector.size(); + if (spriteChainCount > 0) + { + s_material->Apply(); + + unsigned int spriteChain = 0; // Quelle chaîne de sprite traitons-nous + unsigned int spriteChainOffset = 0; // À quel offset dans la dernière chaîne nous sommes-nous arrêtés + + do + { + // On ouvre le buffer en écriture + NzBufferMapper vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite); + NzVertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(vertexMapper.GetPointer()); + + unsigned int spriteCount = 0; + unsigned int maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount()/4); + + do + { + NzDepthRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; + unsigned int count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); + + std::memcpy(vertices, currentChain.vertices + spriteChainOffset*4, 4*count*sizeof(NzVertexStruct_XYZ_Color_UV)); + vertices += count*4; + + spriteCount += count; + spriteChainOffset += count; + + // Avons-nous traité la chaîne entière ? + if (spriteChainOffset == currentChain.spriteCount) + { + spriteChain++; + spriteChainOffset = 0; + } + } + while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); + + vertexMapper.Unmap(); + + NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, spriteCount*6); + } + while (spriteChain < spriteChainCount); + + spriteChainVector.clear(); + } +} + +void NzDepthRenderTechnique::DrawBillboards(const NzSceneData& sceneData) const +{ + if (NzRenderer::HasCapability(nzRendererCap_Instancing)) + { + NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + NzRenderer::SetVertexBuffer(&s_quadVertexBuffer); + + auto& billboardVector = m_renderQueue.billboards; + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) + { + s_material->Apply(nzShaderFlags_Billboard | nzShaderFlags_Instancing); + + const NzDepthRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + do + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; + + instanceBuffer->Fill(data, 0, renderedBillboardCount, true); + data += renderedBillboardCount; + + NzRenderer::DrawPrimitivesInstanced(renderedBillboardCount, nzPrimitiveMode_TriangleStrip, 0, 4); + } + while (billboardCount > 0); + + billboardVector.clear(); + } + } + else + { + NzRenderer::SetIndexBuffer(&s_quadIndexBuffer); + NzRenderer::SetVertexBuffer(&m_billboardPointBuffer); + + auto& billboardVector = m_renderQueue.billboards; + + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) + { + s_material->Apply(nzShaderFlags_Billboard); + + const NzDepthRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount()/4); + + do + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; + + NzBufferMapper vertexMapper(m_billboardPointBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4); + BillboardPoint* vertices = reinterpret_cast(vertexMapper.GetPointer()); + + for (unsigned int i = 0; i < renderedBillboardCount; ++i) + { + const NzDepthRenderQueue::BillboardData& billboard = *data++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(0.f, 1.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(1.f, 1.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(0.f, 0.f); + vertices++; + + vertices->color = billboard.color; + vertices->position = billboard.center; + vertices->sinCos = billboard.sinCos; + vertices->size = billboard.size; + vertices->uv.Set(1.f, 0.f); + vertices++; + } + + vertexMapper.Unmap(); + + NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, renderedBillboardCount*6); + } + while (billboardCount > 0); + + billboardVector.clear(); + } + } +} + +void NzDepthRenderTechnique::DrawOpaqueModels(const NzSceneData& sceneData) const +{ + s_material->Apply(); + + for (auto& meshIt : m_renderQueue.meshes) + { + const NzMeshData& meshData = meshIt.first; + auto& meshEntry = meshIt.second; + + std::vector& instances = meshEntry.instances; + if (!instances.empty()) + { + const NzIndexBuffer* indexBuffer = meshData.indexBuffer; + const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer; + + // Gestion du draw call avant la boucle de rendu + NzRenderer::DrawCall drawFunc; + NzRenderer::DrawCallInstanced instancedDrawFunc; + unsigned int indexCount; + + if (indexBuffer) + { + drawFunc = NzRenderer::DrawIndexedPrimitives; + instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; + indexCount = indexBuffer->GetIndexCount(); + } + else + { + drawFunc = NzRenderer::DrawPrimitives; + instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; + indexCount = vertexBuffer->GetVertexCount(); + } + + NzRenderer::SetIndexBuffer(indexBuffer); + NzRenderer::SetVertexBuffer(vertexBuffer); + + // Sans instancing, on doit effectuer un draw call pour chaque instance + // Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances + // À cause du temps de modification du buffer d'instancing + for (const NzMatrix4f& matrix : instances) + { + NzRenderer::SetMatrix(nzMatrixType_World, matrix); + drawFunc(meshData.primitiveMode, 0, indexCount); + } + instances.clear(); + } + } +} + +NzIndexBuffer NzDepthRenderTechnique::s_quadIndexBuffer; +NzMaterialRef NzDepthRenderTechnique::s_material; +NzVertexBuffer NzDepthRenderTechnique::s_quadVertexBuffer; +NzVertexDeclaration NzDepthRenderTechnique::s_billboardInstanceDeclaration; +NzVertexDeclaration NzDepthRenderTechnique::s_billboardVertexDeclaration; diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 691423019..f81de8fce 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,12 @@ bool NzGraphics::Initialize() NzLoaders_Texture_Register(); // RenderTechniques + if (!NzDepthRenderTechnique::Initialize()) + { + NazaraError("Failed to initialize Depth Rendering"); + return false; + } + if (!NzForwardRenderTechnique::Initialize()) { NazaraError("Failed to initialize Forward Rendering"); @@ -164,6 +171,7 @@ void NzGraphics::Uninitialize() NzLoaders_Texture_Unregister(); NzDeferredRenderTechnique::Uninitialize(); + NzDepthRenderTechnique::Uninitialize(); NzForwardRenderTechnique::Uninitialize(); NzSkinningManager::Uninitialize(); NzParticleRenderer::Uninitialize(); diff --git a/src/Nazara/Graphics/RenderTechniques.cpp b/src/Nazara/Graphics/RenderTechniques.cpp index 820700451..9a96ac71e 100644 --- a/src/Nazara/Graphics/RenderTechniques.cpp +++ b/src/Nazara/Graphics/RenderTechniques.cpp @@ -15,6 +15,7 @@ namespace { "Advanced Forward", "Basic Forward", + "Depth Pass", "Deferred Shading", "Light Pre-Pass", "User"