From 659eb317572e291e2f516019db483798ec83d13a Mon Sep 17 00:00:00 2001 From: Lynix Date: Wed, 3 Sep 2014 13:16:56 +0200 Subject: [PATCH] Added support for billboads Improved ForwardRenderTechnique code Former-commit-id: 2386de85c26839565f087885ffcb098ef263bdfa --- .../Nazara/Graphics/AbstractRenderQueue.hpp | 4 + .../Nazara/Graphics/DeferredRenderQueue.hpp | 2 + .../Nazara/Graphics/ForwardRenderQueue.hpp | 12 + .../Graphics/ForwardRenderTechnique.hpp | 15 +- src/Nazara/Graphics/DeferredRenderQueue.cpp | 15 + src/Nazara/Graphics/ForwardRenderQueue.cpp | 59 +++- .../Graphics/ForwardRenderTechnique.cpp | 306 +++++++++++++++--- src/Nazara/Graphics/Graphics.cpp | 15 +- 8 files changed, 375 insertions(+), 53 deletions(-) diff --git a/include/Nazara/Graphics/AbstractRenderQueue.hpp b/include/Nazara/Graphics/AbstractRenderQueue.hpp index c3a9de2e4..77e7942e5 100644 --- a/include/Nazara/Graphics/AbstractRenderQueue.hpp +++ b/include/Nazara/Graphics/AbstractRenderQueue.hpp @@ -8,7 +8,9 @@ #define NAZARA_ABSTRACTRENDERQUEUE_HPP #include +#include #include +#include #include #include #include @@ -25,6 +27,8 @@ class NAZARA_API NzAbstractRenderQueue : NzNonCopyable NzAbstractRenderQueue() = default; virtual ~NzAbstractRenderQueue(); + virtual void AddBillboard(const NzMaterial* material, const NzVector3f& position, const NzVector2f& size, const NzVector2f& sinCos = NzVector2f(0.f, 1.f), const NzColor& color = NzColor::White) = 0; + virtual void AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr = nullptr, NzSparsePtr colorPtr = nullptr) = 0; virtual void AddDrawable(const NzDrawable* drawable) = 0; virtual void AddLight(const NzLight* light) = 0; virtual void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) = 0; diff --git a/include/Nazara/Graphics/DeferredRenderQueue.hpp b/include/Nazara/Graphics/DeferredRenderQueue.hpp index 8cec0ecea..3e610b758 100644 --- a/include/Nazara/Graphics/DeferredRenderQueue.hpp +++ b/include/Nazara/Graphics/DeferredRenderQueue.hpp @@ -28,6 +28,8 @@ class NAZARA_API NzDeferredRenderQueue : public NzAbstractRenderQueue, NzResourc NzDeferredRenderQueue(NzForwardRenderQueue* forwardQueue); ~NzDeferredRenderQueue(); + 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 AddDrawable(const NzDrawable* drawable) override; void AddLight(const NzLight* light) override; void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) override; diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index 85710cd0d..2533e22a5 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -30,6 +30,8 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource NzForwardRenderQueue() = default; ~NzForwardRenderQueue(); + 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 AddDrawable(const NzDrawable* drawable) override; void AddLight(const NzLight* light) override; void AddMesh(const NzMaterial* material, const NzMeshData& meshData, const NzBoxf& meshAABB, const NzMatrix4f& transformMatrix) override; @@ -43,6 +45,14 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource bool OnResourceDestroy(const NzResource* resource, int index) override; void OnResourceReleased(const NzResource* resource, int index) override; + struct BillboardData + { + NzColor color; + NzVector3f center; + NzVector2f size; + NzVector2f sinCos; + }; + struct TransparentModelData { NzMatrix4f transformMatrix; @@ -68,11 +78,13 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource typedef std::map>, MeshDataComparator> MeshInstanceContainer; typedef std::map, BatchedModelMaterialComparator> ModelBatches; + typedef std::map> BatchedBillboardContainer; typedef std::map> BatchedSpriteContainer; typedef std::vector LightContainer; typedef std::vector TransparentModelContainer; ModelBatches opaqueModels; + BatchedBillboardContainer billboards; BatchedSpriteContainer sprites; TransparentModelContainer transparentModels; std::vector transparentModelData; diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index 9050cda20..a1133b497 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -20,7 +20,7 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique, Nz { public: NzForwardRenderTechnique(); - ~NzForwardRenderTechnique(); + ~NzForwardRenderTechnique() = default; void Clear(const NzScene* scene) const; bool Draw(const NzScene* scene) const; @@ -31,9 +31,13 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique, Nz void SetMaxLightPassPerObject(unsigned int passCount); + static bool Initialize(); + static void Uninitialize(); + private: struct LightUniforms; + void DrawBillboards(const NzScene* scene) const; void DrawOpaqueModels(const NzScene* scene) const; void DrawSprites(const NzScene* scene) const; void DrawTransparentModels(const NzScene* scene) const; @@ -49,12 +53,19 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique, Nz }; mutable std::unordered_map m_lightUniforms; + NzBuffer m_vertexBuffer; mutable NzForwardRenderQueue m_renderQueue; - NzIndexBufferRef m_indexBuffer; mutable NzLightManager m_directionalLights; mutable NzLightManager m_lights; + NzVertexBuffer m_billboardPointBuffer; NzVertexBuffer m_spriteBuffer; unsigned int m_maxLightPassPerObject; + + static NzIndexBuffer s_quadIndexBuffer; + static NzVertexBuffer s_quadVertexBuffer; + static NzVertexDeclaration s_billboardInstanceDeclaration; + static NzVertexDeclaration s_billboardVertexDeclaration; + static NzVertexDeclaration s_spriteDeclaration; }; #endif // NAZARA_FORWARDRENDERTECHNIQUE_HPP diff --git a/src/Nazara/Graphics/DeferredRenderQueue.cpp b/src/Nazara/Graphics/DeferredRenderQueue.cpp index c4e393ec1..db6ce13fd 100644 --- a/src/Nazara/Graphics/DeferredRenderQueue.cpp +++ b/src/Nazara/Graphics/DeferredRenderQueue.cpp @@ -31,6 +31,18 @@ NzDeferredRenderQueue::~NzDeferredRenderQueue() Clear(true); } +void NzDeferredRenderQueue::AddBillboard(const NzMaterial* material, const NzVector3f& position, const NzVector2f& size, const NzVector2f& sinCos, const NzColor& color) +{ + ///TODO: Rendre les billboards via Deferred Shading si possible + m_forwardQueue->AddBillboard(material, position, size, sinCos, color); +} + +void NzDeferredRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr colorPtr) +{ + ///TODO: Rendre les billboards via Deferred Shading si possible + m_forwardQueue->AddBillboards(material, count, positionPtr, sizePtr, sinCosPtr, colorPtr); +} + void NzDeferredRenderQueue::AddDrawable(const NzDrawable* drawable) { m_forwardQueue->AddDrawable(drawable); @@ -81,8 +93,10 @@ void NzDeferredRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData bool& enableInstancing = std::get<1>(it->second); MeshInstanceContainer& meshMap = std::get<2>(it->second); + // On indique la présence de modèles dans cette partie de la map used = true; + // Si nous insérons ce mesh pour la première fois, nous ajoutons des listeners sur ses buffers MeshInstanceContainer::iterator it2 = meshMap.find(meshData); if (it2 == meshMap.end()) { @@ -94,6 +108,7 @@ void NzDeferredRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData meshData.vertexBuffer->AddResourceListener(this, ResourceType_VertexBuffer); } + // On ajoute la matrice à la liste des instances de cet objet std::vector& instances = it2->second; instances.push_back(transformMatrix); diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index 90770660d..fd867e45c 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -27,6 +27,37 @@ NzForwardRenderQueue::~NzForwardRenderQueue() Clear(true); } +void NzForwardRenderQueue::AddBillboard(const NzMaterial* material, const NzVector3f& position, const NzVector2f& size, const NzVector2f& sinCos, const NzColor& color) +{ + billboards[material].push_back(BillboardData{color, position, size, sinCos}); +} + +void NzForwardRenderQueue::AddBillboards(const NzMaterial* material, unsigned int count, NzSparsePtr positionPtr, NzSparsePtr sizePtr, NzSparsePtr sinCosPtr, NzSparsePtr colorPtr) +{ + ///DOC: sinCosPtr et colorPtr peuvent être nuls, ils seont remplacés respectivement par Vector2f(0.f, 1.f) et Color::White + 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 + + std::vector& billboardVec = billboards[material]; + unsigned int prevSize = billboardVec.size(); + billboardVec.resize(prevSize + count); + + BillboardData* billboardData = &billboardVec[prevSize]; + for (unsigned int i = 0; i < count; ++i) + { + billboardData->center = *positionPtr++; + billboardData->color = *colorPtr++; + billboardData->sinCos = *sinCosPtr++; + billboardData->size = *sizePtr++; + billboardData++; + } +} + void NzForwardRenderQueue::AddDrawable(const NzDrawable* drawable) { #if NAZARA_GRAPHICS_SAFE @@ -72,6 +103,7 @@ void NzForwardRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData& { if (material->IsEnabled(nzRendererParameter_Blend)) { + // Le matériau est transparent, nous devons rendre ce mesh d'une autre façon (après le rendu des objets opaques et en les triant) unsigned int index = transparentModelData.size(); transparentModelData.resize(index+1); @@ -88,6 +120,7 @@ void NzForwardRenderQueue::AddMesh(const NzMaterial* material, const NzMeshData& ModelBatches::iterator it = opaqueModels.find(material); if (it == opaqueModels.end()) { + // Première utilisation du matériau, ajoutons-nous comme listener it = opaqueModels.insert(std::make_pair(material, ModelBatches::mapped_type())).first; material->AddResourceListener(this, ResourceType_Material); } @@ -166,6 +199,8 @@ void NzForwardRenderQueue::Clear(bool fully) renderData.vertexBuffer->RemoveResourceListener(this); } } + + billboards.clear(); opaqueModels.clear(); sprites.clear(); } @@ -174,6 +209,7 @@ void NzForwardRenderQueue::Clear(bool fully) void NzForwardRenderQueue::Sort(const NzAbstractViewer* viewer) { NzPlanef nearPlane = viewer->GetFrustum().GetPlane(nzFrustumPlane_Near); + NzVector3f viewerPos = viewer->GetEyePosition(); NzVector3f viewerNormal = viewer->GetForward(); std::sort(transparentModels.begin(), transparentModels.end(), [this, &nearPlane, &viewerNormal](unsigned int index1, unsigned int index2) @@ -186,6 +222,20 @@ void NzForwardRenderQueue::Sort(const NzAbstractViewer* viewer) return nearPlane.Distance(position1) > nearPlane.Distance(position2); }); + + for (auto& pair : billboards) + { + const NzMaterial* mat = pair.first; + auto& container = pair.second; + + if (mat->IsEnabled(nzRendererParameter_Blend)) + { + std::sort(container.begin(), container.end(), [&viewerPos](const BillboardData& data1, const BillboardData& data2) + { + return viewerPos.SquaredDistance(data1.center) > viewerPos.SquaredDistance(data2.center); + }); + } + } } bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int index) @@ -210,8 +260,13 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind } case ResourceType_Material: - opaqueModels.erase(static_cast(resource)); + { + const NzMaterial* material = static_cast(resource); + + billboards.erase(material); + opaqueModels.erase(material); break; + } case ResourceType_VertexBuffer: { @@ -299,7 +354,6 @@ bool NzForwardRenderQueue::BatchedModelMaterialComparator::operator()(const NzMa const NzShader* shader1 = mat1->GetShaderInstance()->GetShader(); const NzShader* shader2 = mat2->GetShaderInstance()->GetShader(); - if (shader1 != shader2) return shader1 < shader2; @@ -320,7 +374,6 @@ bool NzForwardRenderQueue::BatchedSpriteMaterialComparator::operator()(const NzM const NzShader* shader1 = mat1->GetShaderInstance()->GetShader(); const NzShader* shader2 = mat2->GetShaderInstance()->GetShader(); - if (shader1 != shader2) return shader1 < shader2; diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 68726c809..44cd9d172 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -3,6 +3,8 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include #include #include #include @@ -22,46 +24,36 @@ namespace { - static NzIndexBuffer* s_indexBuffer = nullptr; - unsigned int s_maxSprites = 8192; - - NzIndexBuffer* BuildIndexBuffer() + struct BillboardPoint { - std::unique_ptr indexBuffer(new NzIndexBuffer(false, s_maxSprites*6, nzBufferStorage_Hardware, nzBufferUsage_Static)); - indexBuffer->SetPersistent(false); + NzColor color; + NzVector3f position; + NzVector2f size; + NzVector2f sinCos; // doit suivre size + NzVector2f uv; + }; - NzBufferMapper mapper(indexBuffer.get(), nzBufferAccess_WriteOnly); - nzUInt16* indices = static_cast(mapper.GetPointer()); + struct SpriteVertex + { + NzColor color; + NzVector3f position; + NzVector2f uv; + }; - for (unsigned int i = 0; i < s_maxSprites; ++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; - } - - return indexBuffer.release(); - } + unsigned int s_maxBillboardSprites = std::numeric_limits::max()/6; + unsigned int s_vertexBufferSize = 1024*1024; // 1 MB } NzForwardRenderTechnique::NzForwardRenderTechnique() : -m_spriteBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_UV), s_maxSprites*4, nzBufferStorage_Hardware, nzBufferUsage_Dynamic), +m_vertexBuffer(nzBufferType_Vertex), m_maxLightPassPerObject(3) { - if (!s_indexBuffer) - s_indexBuffer = BuildIndexBuffer(); + NzErrorFlags flags(nzErrorFlag_ThrowException, true); - m_indexBuffer = s_indexBuffer; -} + m_vertexBuffer.Create(s_vertexBufferSize, nzBufferStorage_Hardware, nzBufferUsage_Dynamic); -NzForwardRenderTechnique::~NzForwardRenderTechnique() -{ - if (m_indexBuffer.Reset()) - s_indexBuffer = nullptr; + m_billboardPointBuffer.Reset(&s_billboardVertexDeclaration, &m_vertexBuffer); + m_spriteBuffer.Reset(&s_spriteDeclaration, &m_vertexBuffer); } void NzForwardRenderTechnique::Clear(const NzScene* scene) const @@ -87,6 +79,9 @@ bool NzForwardRenderTechnique::Draw(const NzScene* scene) const if (!m_renderQueue.transparentModels.empty()) DrawTransparentModels(scene); + if (!m_renderQueue.billboards.empty()) + DrawBillboards(scene); + if (!m_renderQueue.sprites.empty()) DrawSprites(scene); @@ -162,6 +157,208 @@ void NzForwardRenderTechnique::SetMaxLightPassPerObject(unsigned int passCount) m_maxLightPassPerObject = passCount; } +bool NzForwardRenderTechnique::Initialize() +{ + try + { + NzErrorFlags flags(nzErrorFlag_ThrowException, true); + + s_quadIndexBuffer.Reset(false, s_maxBillboardSprites*6, nzBufferStorage_Hardware, nzBufferUsage_Static); + + NzBufferMapper mapper(s_quadIndexBuffer, nzBufferAccess_WriteOnly); + nzUInt16* indices = static_cast(mapper.GetPointer()); + + for (unsigned int i = 0; i < s_maxBillboardSprites; ++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, nzBufferStorage_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)); + + s_spriteDeclaration.EnableComponent(nzVertexComponent_Color, nzComponentType_Color, NzOffsetOf(SpriteVertex, color)); + s_spriteDeclaration.EnableComponent(nzVertexComponent_Position, nzComponentType_Float3, NzOffsetOf(SpriteVertex, position)); + s_spriteDeclaration.EnableComponent(nzVertexComponent_TexCoord, nzComponentType_Float2, NzOffsetOf(SpriteVertex, uv)); + + } + catch (const std::exception& e) + { + NazaraError("Failed to initialise: " + NzString(e.what())); + return false; + } + + return true; +} + +void NzForwardRenderTechnique::Uninitialize() +{ + s_quadIndexBuffer.Reset(); +} + +void NzForwardRenderTechnique::DrawBillboards(const NzScene* scene) const +{ + NzAbstractViewer* viewer = scene->GetViewer(); + const NzShader* lastShader = nullptr; + + if (NzRenderer::HasCapability(nzRendererCap_Instancing)) + { + NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); + instanceBuffer->SetVertexDeclaration(&s_billboardInstanceDeclaration); + + NzRenderer::SetVertexBuffer(&s_quadVertexBuffer); + + for (auto& matIt : m_renderQueue.billboards) + { + const NzMaterial* material = matIt.first; + auto& billboardVector = matIt.second; + + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) + { + // On commence par appliquer du matériau (et récupérer le shader ainsi activé) + const NzShader* shader = material->Apply(nzShaderFlags_Billboard | nzShaderFlags_Instancing | nzShaderFlags_VertexColor); + + // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + if (shader != lastShader) + { + // Couleur ambiante de la scène + shader->SendColor(shader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); + // Position de la caméra + shader->SendVector(shader->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition()); + + lastShader = shader; + } + + const NzForwardRenderQueue::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); + + for (auto& matIt : m_renderQueue.billboards) + { + const NzMaterial* material = matIt.first; + auto& billboardVector = matIt.second; + + unsigned int billboardCount = billboardVector.size(); + if (billboardCount > 0) + { + // On commence par appliquer du matériau (et récupérer le shader ainsi activé) + const NzShader* shader = material->Apply(nzShaderFlags_Billboard | nzShaderFlags_VertexColor); + + // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + if (shader != lastShader) + { + // Couleur ambiante de la scène + shader->SendColor(shader->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); + // Position de la caméra + shader->SendVector(shader->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition()); + + lastShader = shader; + } + + const NzForwardRenderQueue::BillboardData* data = &billboardVector[0]; + unsigned int maxBillboardPerDraw = std::min(s_maxBillboardSprites, 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 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, 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 NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const { NzAbstractViewer* viewer = scene->GetViewer(); @@ -378,7 +575,7 @@ void NzForwardRenderTechnique::DrawSprites(const NzScene* scene) const NzAbstractViewer* viewer = scene->GetViewer(); const NzShader* lastShader = nullptr; - NzRenderer::SetIndexBuffer(m_indexBuffer); + NzRenderer::SetIndexBuffer(&s_quadIndexBuffer); NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity()); NzRenderer::SetVertexBuffer(&m_spriteBuffer); @@ -391,7 +588,7 @@ void NzForwardRenderTechnique::DrawSprites(const NzScene* scene) const if (spriteCount > 0) { // On commence par appliquer du matériau (et récupérer le shader ainsi activé) - const NzShader* shader = material->Apply(); + const NzShader* shader = material->Apply(nzShaderFlags_VertexColor); // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas if (shader != lastShader) @@ -405,36 +602,47 @@ void NzForwardRenderTechnique::DrawSprites(const NzScene* scene) const } const NzSprite** spritePtr = &spriteVector[0]; + unsigned int maxSpritePerDraw = std::min(s_maxBillboardSprites, m_spriteBuffer.GetVertexCount()/4); do { - unsigned int renderedSpriteCount = std::min(spriteCount, 64U); + unsigned int renderedSpriteCount = std::min(spriteCount, maxSpritePerDraw); spriteCount -= renderedSpriteCount; NzBufferMapper vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedSpriteCount*4); - NzVertexStruct_XYZ_UV* vertices = reinterpret_cast(vertexMapper.GetPointer()); + SpriteVertex* vertices = reinterpret_cast(vertexMapper.GetPointer()); for (unsigned int i = 0; i < renderedSpriteCount; ++i) { const NzSprite* sprite = *spritePtr++; + + const NzColor& color = sprite->GetColor(); const NzRectf& textureCoords = sprite->GetTextureCoords(); - const NzVector2f& halfSize = sprite->GetSize()*0.5f; - NzVector3f center = sprite->GetPosition(); + NzVector2f size = sprite->GetSize(); + NzVector3f origin = sprite->GetPosition(); + NzVector3f scale = sprite->GetScale(); NzQuaternionf rotation = sprite->GetRotation(); - vertices->position = center + rotation * NzVector3f(-halfSize.x, -halfSize.y, 0.f); - vertices->uv.Set(textureCoords.x, textureCoords.y + textureCoords.height); - vertices++; + size.x *= scale.x; + size.y *= scale.y; - vertices->position = center + rotation * NzVector3f(halfSize.x, -halfSize.y, 0.f); - vertices->uv.Set(textureCoords.width, textureCoords.y + textureCoords.height); - vertices++; - - vertices->position = center + rotation * NzVector3f(-halfSize.x, halfSize.y, 0.f); + vertices->color = color; + vertices->position = origin; vertices->uv.Set(textureCoords.x, textureCoords.y); vertices++; - vertices->position = center + rotation * NzVector3f(halfSize.x, halfSize.y, 0.f); - vertices->uv.Set(textureCoords.width, textureCoords.y); + vertices->color = color; + vertices->position = origin + rotation * NzVector3f(size.x, 0.f, 0.f); + vertices->uv.Set(textureCoords.x + textureCoords.width, textureCoords.y); + vertices++; + + vertices->color = color; + vertices->position = origin + rotation * NzVector3f(0.f, size.y, 0.f); + vertices->uv.Set(textureCoords.x, textureCoords.y + textureCoords.height); + vertices++; + + vertices->color = color; + vertices->position = origin + rotation * NzVector3f(size.x, size.y, 0.f); + vertices->uv.Set(textureCoords.x + textureCoords.width, textureCoords.y + textureCoords.height); vertices++; } @@ -557,3 +765,9 @@ const NzForwardRenderTechnique::LightUniforms* NzForwardRenderTechnique::GetLigh return &(pair.first->second); } } + +NzIndexBuffer NzForwardRenderTechnique::s_quadIndexBuffer; +NzVertexBuffer NzForwardRenderTechnique::s_quadVertexBuffer; +NzVertexDeclaration NzForwardRenderTechnique::s_billboardInstanceDeclaration; +NzVertexDeclaration NzForwardRenderTechnique::s_billboardVertexDeclaration; +NzVertexDeclaration NzForwardRenderTechnique::s_spriteDeclaration; diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 1058e484b..f1e8e33dc 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -65,12 +65,22 @@ bool NzGraphics::Initialize() NzLoaders_Texture_Register(); // RenderTechniques + if (!NzForwardRenderTechnique::Initialize()) + { + NazaraError("Failed to initialize Forward Rendering"); + return false; + } + NzRenderTechniques::Register(NzRenderTechniques::ToString(nzRenderTechniqueType_BasicForward), 0, []() -> NzAbstractRenderTechnique* { return new NzForwardRenderTechnique; }); if (NzDeferredRenderTechnique::IsSupported()) { - NzDeferredRenderTechnique::Initialize(); - NzRenderTechniques::Register(NzRenderTechniques::ToString(nzRenderTechniqueType_DeferredShading), 20, []() -> NzAbstractRenderTechnique* { return new NzDeferredRenderTechnique; }); + if (NzDeferredRenderTechnique::Initialize()) + NzRenderTechniques::Register(NzRenderTechniques::ToString(nzRenderTechniqueType_DeferredShading), 20, []() -> NzAbstractRenderTechnique* { return new NzDeferredRenderTechnique; }); + else + { + NazaraWarning("Failed to initialize Deferred Rendering"); + } } onExit.Reset(); @@ -104,6 +114,7 @@ void NzGraphics::Uninitialize() NzLoaders_Texture_Unregister(); NzDeferredRenderTechnique::Uninitialize(); + NzForwardRenderTechnique::Uninitialize(); NzMaterial::Uninitialize(); NzParticleDeclaration::Uninitialize(); NzSkinningManager::Uninitialize();