diff --git a/include/Nazara/Graphics/DepthRenderQueue.hpp b/include/Nazara/Graphics/DepthRenderQueue.hpp index ec6550494..ed96b7d6b 100644 --- a/include/Nazara/Graphics/DepthRenderQueue.hpp +++ b/include/Nazara/Graphics/DepthRenderQueue.hpp @@ -18,10 +18,10 @@ #include #include -class NAZARA_GRAPHICS_API NzDepthRenderQueue : public NzAbstractRenderQueue +class NAZARA_GRAPHICS_API NzDepthRenderQueue : public NzForwardRenderQueue { public: - NzDepthRenderQueue() = default; + NzDepthRenderQueue(); ~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; @@ -33,44 +33,16 @@ class NAZARA_GRAPHICS_API NzDepthRenderQueue : public NzAbstractRenderQueue 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 AddDirectionalLight(const DirectionalLight& light) override; void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) override; + void AddPointLight(const PointLight& light) override; + void AddSpotLight(const SpotLight& light) 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); + NzMaterialRef m_baseMaterial; }; #endif // NAZARA_DEPTHRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/DepthRenderTechnique.hpp b/include/Nazara/Graphics/DepthRenderTechnique.hpp index b33a2b6cc..9da641c3b 100644 --- a/include/Nazara/Graphics/DepthRenderTechnique.hpp +++ b/include/Nazara/Graphics/DepthRenderTechnique.hpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -32,22 +31,51 @@ class NAZARA_GRAPHICS_API NzDepthRenderTechnique : public NzAbstractRenderTechni static void Uninitialize(); private: + struct ShaderUniforms; + void DrawBasicSprites(const NzSceneData& sceneData) const; void DrawBillboards(const NzSceneData& sceneData) const; void DrawOpaqueModels(const NzSceneData& sceneData) const; + const ShaderUniforms* GetShaderUniforms(const NzShader* shader) const; + void OnShaderInvalidated(const NzShader* shader) const; + struct LightIndex + { + nzLightType type; + float score; + unsigned int index; + }; + + struct ShaderUniforms + { + NazaraSlot(NzShader, OnShaderUniformInvalidated, shaderUniformInvalidatedSlot); + NazaraSlot(NzShader, OnShaderRelease, shaderReleaseSlot); + + NzLightUniforms lightUniforms; + bool hasLightUniforms; + + /// Moins coûteux en mémoire que de stocker un NzLightUniforms par index de lumière, + /// à voir si ça fonctionne chez tout le monde + int lightOffset; // "Distance" entre Lights[0].type et Lights[1].type + + // Autre uniformes + int eyePosition; + int sceneAmbient; + int textureOverlay; + }; + + mutable std::unordered_map m_shaderUniforms; 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 +#include #endif // NAZARA_DEPTHRENDERTECHNIQUE_HPP diff --git a/src/Nazara/Graphics/DepthRenderQueue.cpp b/src/Nazara/Graphics/DepthRenderQueue.cpp index 9b5a2c170..9ff02dcbd 100644 --- a/src/Nazara/Graphics/DepthRenderQueue.cpp +++ b/src/Nazara/Graphics/DepthRenderQueue.cpp @@ -7,76 +7,58 @@ #include #include -///TODO: Remplacer les sinus/cosinus par une lookup table (va booster les perfs d'un bon x10) +NzDepthRenderQueue::NzDepthRenderQueue() +{ + // Material + m_baseMaterial = NzMaterial::New(); + m_baseMaterial->Enable(nzRendererParameter_ColorWrite, false); + m_baseMaterial->Enable(nzRendererParameter_FaceCulling, false); + //m_baseMaterial->SetFaceCulling(nzFaceSide_Front); +} 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}); + if (!IsMaterialSuitable(material)) + return; + + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; + + NzForwardRenderQueue::AddBillboard(material, position, size, sinCos, color); } 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); } 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); } void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr colorPtr) @@ -86,129 +68,57 @@ void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, anglePtr, colorPtr); } 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, anglePtr, alphaPtr); } 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); } 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, alphaPtr); } void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr anglePtr, NzSparsePtr colorPtr) @@ -218,80 +128,33 @@ void NzDepthRenderQueue::AddBillboards(const NzMaterial* material, unsigned int 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, anglePtr, colorPtr); } 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 (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - 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++; - } + NzForwardRenderQueue::AddBillboards(material, count, positionPtr, sizePtr, anglePtr, alphaPtr); } -void NzDepthRenderQueue::AddDrawable(const NzDrawable* drawable) +void NzDepthRenderQueue::AddDirectionalLight(const DirectionalLight& light) { - #if NAZARA_GRAPHICS_SAFE - if (!drawable) - { - NazaraError("Invalid drawable"); - return; - } - #endif - - otherDrawables.push_back(drawable); + NazaraAssert(false, "Depth render queue doesn't handle lights"); + NazaraUnused(light); } void NzDepthRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) @@ -302,25 +165,24 @@ void NzDepthRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData& m if (!IsMaterialSuitable(material)) return; - auto it = meshes.find(meshData); - if (it == meshes.end()) - { - MeshInstanceEntry instanceEntry; + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; - if (meshData.indexBuffer) - instanceEntry.indexBufferReleaseSlot.Connect(meshData.indexBuffer->OnIndexBufferRelease, this, &NzDepthRenderQueue::OnIndexBufferInvalidation); + NzForwardRenderQueue::AddMesh(material, meshData, meshAABB, transformMatrix); +} - instanceEntry.vertexBufferReleaseSlot.Connect(meshData.vertexBuffer->OnVertexBufferRelease, this, &NzDepthRenderQueue::OnVertexBufferInvalidation); +void NzDepthRenderQueue::AddPointLight(const PointLight& light) +{ + NazaraAssert(false, "Depth render queue doesn't handle lights"); + NazaraUnused(light); +} - 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::AddSpotLight(const SpotLight& light) +{ + NazaraAssert(false, "Depth render queue doesn't handle lights"); + NazaraUnused(light); } void NzDepthRenderQueue::AddSprites(const NzMaterial* material, const NzVertexStruct_XYZ_Color_UV* vertices, unsigned int spriteCount, const NzTexture* overlay) @@ -331,49 +193,17 @@ void NzDepthRenderQueue::AddSprites(const NzMaterial* material, const NzVertexSt if (!IsMaterialSuitable(material)) return; - basicSprites.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); -} + if (material->HasDepthMaterial()) + material = material->GetDepthMaterial(); + else + material = m_baseMaterial; -void NzDepthRenderQueue::Clear(bool fully) -{ - NzAbstractRenderQueue::Clear(fully); - - basicSprites.clear(); - billboards.clear(); - otherDrawables.clear(); - - if (fully) - meshes.clear(); + NzForwardRenderQueue::AddSprites(material, vertices, spriteCount, overlay); } bool NzDepthRenderQueue::IsMaterialSuitable(const NzMaterial* material) const { NazaraAssert(material, "Invalid material"); - return material->IsEnabled(nzRendererParameter_DepthBuffer) && material->IsEnabled(nzRendererParameter_DepthWrite) && material->IsShadowCastingEnabled(); + return material->HasDepthMaterial() || (material->IsEnabled(nzRendererParameter_DepthBuffer) && material->IsEnabled(nzRendererParameter_DepthWrite) && material->IsShadowCastingEnabled()); } - -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 index 6ec9dfaf3..31d63d56a 100644 --- a/src/Nazara/Graphics/DepthRenderTechnique.cpp +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ bool NzDepthRenderTechnique::Draw(const NzSceneData& sceneData) const if (sceneData.background) sceneData.background->Draw(sceneData.viewer); - if (!m_renderQueue.meshes.empty()) + if (!m_renderQueue.opaqueModels.empty()) DrawOpaqueModels(sceneData); if (!m_renderQueue.basicSprites.empty()) @@ -78,7 +79,7 @@ NzAbstractRenderQueue* NzDepthRenderTechnique::GetRenderQueue() nzRenderTechniqueType NzDepthRenderTechnique::GetType() const { - return nzRenderTechniqueType_BasicForward; + return nzRenderTechniqueType_Depth; } bool NzDepthRenderTechnique::Initialize() @@ -129,11 +130,6 @@ bool NzDepthRenderTechnique::Initialize() 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) { @@ -146,7 +142,6 @@ bool NzDepthRenderTechnique::Initialize() void NzDepthRenderTechnique::Uninitialize() { - s_material.Reset(); s_quadIndexBuffer.Reset(); s_quadVertexBuffer.Reset(); } @@ -155,55 +150,101 @@ void NzDepthRenderTechnique::DrawBasicSprites(const NzSceneData& sceneData) cons { NazaraUnused(sceneData); + const NzShader* lastShader = nullptr; + const ShaderUniforms* shaderUniforms = nullptr; + 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) + for (auto& matIt : m_renderQueue.basicSprites) { - s_material->Apply(); + const NzMaterial* material = matIt.first; + auto& matEntry = matIt.second; - 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 + if (matEntry.enabled) { - // 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 + auto& overlayMap = matEntry.overlayMap; + for (auto& overlayIt : overlayMap) { - NzDepthRenderQueue::SpriteChain_XYZ_Color_UV& currentChain = spriteChainVector[spriteChain]; - unsigned int count = std::min(maxSpriteCount - spriteCount, currentChain.spriteCount - spriteChainOffset); + const NzTexture* overlay = overlayIt.first; + auto& spriteChainVector = overlayIt.second.spriteChains; - 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) + unsigned int spriteChainCount = spriteChainVector.size(); + if (spriteChainCount > 0) { - spriteChain++; - spriteChainOffset = 0; + // On commence par appliquer du matériau (et récupérer le shader ainsi activé) + nzUInt32 flags = nzShaderFlags_VertexColor; + if (overlay) + flags |= nzShaderFlags_TextureOverlay; + + nzUInt8 overlayUnit; + const NzShader* shader = material->Apply(flags, 0, &overlayUnit); + + if (overlay) + { + overlayUnit++; + NzRenderer::SetTexture(overlayUnit, overlay); + NzRenderer::SetTextureSampler(overlayUnit, material->GetDiffuseSampler()); + } + + // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + if (shader != lastShader) + { + // Index des uniformes dans le shader + shaderUniforms = GetShaderUniforms(shader); + + // Overlay + shader->SendInteger(shaderUniforms->textureOverlay, overlayUnit); + + lastShader = shader; + } + + 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 + { + NzForwardRenderQueue::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(); } } - while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount); - vertexMapper.Unmap(); - - NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, spriteCount*6); + // On remet à zéro + matEntry.enabled = false; } - while (spriteChain < spriteChainCount); - - spriteChainVector.clear(); } } @@ -218,27 +259,34 @@ void NzDepthRenderTechnique::DrawBillboards(const NzSceneData& sceneData) const NzRenderer::SetVertexBuffer(&s_quadVertexBuffer); - auto& billboardVector = m_renderQueue.billboards; - unsigned int billboardCount = billboardVector.size(); - if (billboardCount > 0) + for (auto& matIt : m_renderQueue.billboards) { - s_material->Apply(nzShaderFlags_Billboard | nzShaderFlags_Instancing); + const NzMaterial* material = matIt.first; + auto& entry = matIt.second; + auto& billboardVector = entry.billboards; - const NzDepthRenderQueue::BillboardData* data = &billboardVector[0]; - unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount(); - do + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) { - unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; + // On commence par appliquer du matériau + material->Apply(nzShaderFlags_Billboard | nzShaderFlags_Instancing | nzShaderFlags_VertexColor); - instanceBuffer->Fill(data, 0, renderedBillboardCount, true); - data += renderedBillboardCount; + const NzForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = instanceBuffer->GetVertexCount(); + do + { + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; - NzRenderer::DrawPrimitivesInstanced(renderedBillboardCount, nzPrimitiveMode_TriangleStrip, 0, 4); + instanceBuffer->Fill(data, 0, renderedBillboardCount, true); + data += renderedBillboardCount; + + NzRenderer::DrawPrimitivesInstanced(renderedBillboardCount, nzPrimitiveMode_TriangleStrip, 0, 4); + } + while (billboardCount > 0); + + billboardVector.clear(); } - while (billboardCount > 0); - - billboardVector.clear(); } } else @@ -246,64 +294,70 @@ void NzDepthRenderTechnique::DrawBillboards(const NzSceneData& sceneData) const NzRenderer::SetIndexBuffer(&s_quadIndexBuffer); NzRenderer::SetVertexBuffer(&m_billboardPointBuffer); - auto& billboardVector = m_renderQueue.billboards; - - unsigned int billboardCount = billboardVector.size(); - if (billboardCount > 0) + for (auto& matIt : m_renderQueue.billboards) { - s_material->Apply(nzShaderFlags_Billboard); + const NzMaterial* material = matIt.first; + auto& entry = matIt.second; + auto& billboardVector = entry.billboards; - const NzDepthRenderQueue::BillboardData* data = &billboardVector[0]; - unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount()/4); - - do + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) { - unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); - billboardCount -= renderedBillboardCount; + // On commence par appliquer du matériau + material->Apply(nzShaderFlags_Billboard | nzShaderFlags_VertexColor); - NzBufferMapper vertexMapper(m_billboardPointBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4); - BillboardPoint* vertices = reinterpret_cast(vertexMapper.GetPointer()); + const NzForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = std::min(s_maxQuads, m_billboardPointBuffer.GetVertexCount()/4); - for (unsigned int i = 0; i < renderedBillboardCount; ++i) + do { - const NzDepthRenderQueue::BillboardData& billboard = *data++; + unsigned int renderedBillboardCount = std::min(billboardCount, maxBillboardPerDraw); + billboardCount -= renderedBillboardCount; - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(0.f, 1.f); - vertices++; + NzBufferMapper vertexMapper(m_billboardPointBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedBillboardCount*4); + BillboardPoint* vertices = reinterpret_cast(vertexMapper.GetPointer()); - vertices->color = billboard.color; - vertices->position = billboard.center; - vertices->sinCos = billboard.sinCos; - vertices->size = billboard.size; - vertices->uv.Set(1.f, 1.f); - vertices++; + for (unsigned int i = 0; i < renderedBillboardCount; ++i) + { + const NzForwardRenderQueue::BillboardData& billboard = *data++; - 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(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, 0.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); - vertexMapper.Unmap(); - - NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, renderedBillboardCount*6); + billboardVector.clear(); } - while (billboardCount > 0); - - billboardVector.clear(); } } } @@ -312,55 +366,130 @@ void NzDepthRenderTechnique::DrawOpaqueModels(const NzSceneData& sceneData) cons { NazaraUnused(sceneData); - s_material->Apply(); - - for (auto& meshIt : m_renderQueue.meshes) + for (auto& matIt : m_renderQueue.opaqueModels) { - const NzMeshData& meshData = meshIt.first; - auto& meshEntry = meshIt.second; + auto& matEntry = matIt.second; - std::vector& instances = meshEntry.instances; - if (!instances.empty()) + if (matEntry.enabled) { - const NzIndexBuffer* indexBuffer = meshData.indexBuffer; - const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer; + NzForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; - // Gestion du draw call avant la boucle de rendu - NzRenderer::DrawCall drawFunc; - NzRenderer::DrawCallInstanced instancedDrawFunc; - unsigned int indexCount; + if (!meshInstances.empty()) + { + const NzMaterial* material = matIt.first; - if (indexBuffer) - { - drawFunc = NzRenderer::DrawIndexedPrimitives; - instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = NzRenderer::DrawPrimitives; - instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; - indexCount = vertexBuffer->GetVertexCount(); + // Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active + // Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches + // (Le deferred shading n'a pas ce problème) + bool noPointSpotLight = m_renderQueue.pointLights.empty() && m_renderQueue.spotLights.empty(); + bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || noPointSpotLight) && matEntry.instancingEnabled; + + // On commence par appliquer du matériau (et récupérer le shader ainsi activé) + material->Apply((instancing) ? nzShaderFlags_Instancing : 0); + + // Meshes + for (auto& meshIt : meshInstances) + { + 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); + + if (instancing) + { + // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) + NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4)); + + const NzMatrix4f* instanceMatrices = &instances[0]; + unsigned int instanceCount = instances.size(); + unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre maximum d'instances en une fois + + while (instanceCount > 0) + { + // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) + unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); + instanceCount -= renderedInstanceCount; + + // On remplit l'instancing buffer avec nos matrices world + instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); + instanceMatrices += renderedInstanceCount; + + // Et on affiche + instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); + } + } + else + { + // 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(); + } + } } - 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(); + // Et on remet à zéro les données + matEntry.enabled = false; + matEntry.instancingEnabled = false; } } } +const NzDepthRenderTechnique::ShaderUniforms* NzDepthRenderTechnique::GetShaderUniforms(const NzShader* shader) const +{ + auto it = m_shaderUniforms.find(shader); + if (it == m_shaderUniforms.end()) + { + ShaderUniforms uniforms; + uniforms.shaderReleaseSlot.Connect(shader->OnShaderRelease, this, &NzDepthRenderTechnique::OnShaderInvalidated); + uniforms.shaderUniformInvalidatedSlot.Connect(shader->OnShaderUniformInvalidated, this, &NzDepthRenderTechnique::OnShaderInvalidated); + + uniforms.textureOverlay = shader->GetUniformLocation("TextureOverlay"); + + it = m_shaderUniforms.emplace(shader, std::move(uniforms)).first; + } + + return &it->second; +} + +void NzDepthRenderTechnique::OnShaderInvalidated(const NzShader* shader) const +{ + m_shaderUniforms.erase(shader); +} + NzIndexBuffer NzDepthRenderTechnique::s_quadIndexBuffer; -NzMaterialRef NzDepthRenderTechnique::s_material; NzVertexBuffer NzDepthRenderTechnique::s_quadVertexBuffer; NzVertexDeclaration NzDepthRenderTechnique::s_billboardInstanceDeclaration; NzVertexDeclaration NzDepthRenderTechnique::s_billboardVertexDeclaration;