diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index b081641c1..bc5c2456e 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -153,7 +153,7 @@ namespace Nz using MeshPipelineBatches = std::map; - struct TransparentModelData + struct UnbatchedModelData { Matrix4f transformMatrix; MeshData meshData; @@ -161,16 +161,23 @@ namespace Nz const Material* material; }; - using TransparentModelContainer = std::vector; + struct UnbatchedSpriteData + { + std::size_t spriteCount; + const Material* material; + const Texture* overlay; + const VertexStruct_XYZ_Color_UV* vertices; + }; struct Layer { BillboardPipelineBatches billboards; SpritePipelineBatches opaqueSprites; - SpritePipelineBatches depthSortedSprites; MeshPipelineBatches opaqueModels; - TransparentModelContainer depthSortedMeshes; - std::vector depthSortedMeshData; + std::vector depthSortedMeshes; + std::vector depthSortedSprites; + std::vector depthSortedMeshData; + std::vector depthSortedSpriteData; std::vector otherDrawables; unsigned int clearCount = 0; }; diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index 9b5c152df..cadb60473 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -43,6 +43,7 @@ namespace Nz void DrawBasicSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawBillboards(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawOpaqueModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; + void DrawOrderedSprites(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; void DrawTransparentModels(const SceneData& sceneData, ForwardRenderQueue::Layer& layer) const; const ShaderUniforms* GetShaderUniforms(const Shader* shader) const; void OnShaderInvalidated(const Shader* shader) const; diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index c594157b9..ba8c68358 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -386,7 +386,7 @@ namespace Nz std::size_t index = transparentData.size(); transparentData.resize(index+1); - TransparentModelData& data = transparentData.back(); + UnbatchedModelData& data = transparentData.back(); data.material = material; data.meshData = meshData; data.obbSphere = Spheref(transformMatrix.GetTranslation() + meshAABB.GetCenter(), meshAABB.GetSquaredRadius()); @@ -462,48 +462,69 @@ namespace Nz NazaraAssert(material, "Invalid material"); Layer& currentLayer = GetLayer(renderOrder); - SpritePipelineBatches& basicSprites = currentLayer.opaqueSprites; - const MaterialPipeline* materialPipeline = material->GetPipeline(); - - auto pipelineIt = basicSprites.find(materialPipeline); - if (pipelineIt == basicSprites.end()) + if (material->IsDepthSortingEnabled()) { - BatchedSpritePipelineEntry materialEntry; - pipelineIt = basicSprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + auto& transparentSprites = currentLayer.depthSortedSprites; + auto& transparentData = currentLayer.depthSortedSpriteData; + + // The material is marked for depth sorting, we must draw this mesh using another way (after the rendering of opaques objects while sorting them) + std::size_t index = transparentData.size(); + transparentData.resize(index + 1); + + UnbatchedSpriteData& data = transparentData.back(); + data.material = material; + data.overlay = overlay; + data.spriteCount = spriteCount; + data.vertices = vertices; + + transparentSprites.push_back(index); } - - BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second; - pipelineEntry.enabled = true; - - SpriteMaterialBatches& materialMap = pipelineEntry.materialMap; - - auto matIt = materialMap.find(material); - if (matIt == materialMap.end()) + else { - BatchedBasicSpriteEntry entry; - entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); + SpritePipelineBatches& sprites = currentLayer.opaqueSprites; - matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; + const MaterialPipeline* materialPipeline = material->GetPipeline(); + + auto pipelineIt = sprites.find(materialPipeline); + if (pipelineIt == sprites.end()) + { + BatchedSpritePipelineEntry materialEntry; + pipelineIt = sprites.insert(SpritePipelineBatches::value_type(materialPipeline, std::move(materialEntry))).first; + } + + BatchedSpritePipelineEntry& pipelineEntry = pipelineIt->second; + pipelineEntry.enabled = true; + + SpriteMaterialBatches& materialMap = pipelineEntry.materialMap; + + auto matIt = materialMap.find(material); + if (matIt == materialMap.end()) + { + BatchedBasicSpriteEntry entry; + entry.materialReleaseSlot.Connect(material->OnMaterialRelease, this, &ForwardRenderQueue::OnMaterialInvalidation); + + matIt = materialMap.insert(SpriteMaterialBatches::value_type(material, std::move(entry))).first; + } + + BatchedBasicSpriteEntry& entry = matIt->second; + entry.enabled = true; + + auto& overlayMap = entry.overlayMap; + + auto overlayIt = overlayMap.find(overlay); + if (overlayIt == overlayMap.end()) + { + BatchedSpriteEntry overlayEntry; + if (overlay) + overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation); + + overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first; + } + + auto& spriteVector = overlayIt->second.spriteChains; + spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); } - - BatchedBasicSpriteEntry& entry = matIt->second; - entry.enabled = true; - - auto& overlayMap = entry.overlayMap; - - auto overlayIt = overlayMap.find(overlay); - if (overlayIt == overlayMap.end()) - { - BatchedSpriteEntry overlayEntry; - if (overlay) - overlayEntry.textureReleaseSlot.Connect(overlay->OnTextureRelease, this, &ForwardRenderQueue::OnTextureInvalidation); - - overlayIt = overlayMap.insert(std::make_pair(overlay, std::move(overlayEntry))).first; - } - - auto& spriteVector = overlayIt->second.spriteChains; - spriteVector.push_back(SpriteChain_XYZ_Color_UV({vertices, spriteCount})); } /*! @@ -596,9 +617,11 @@ namespace Nz } } - layer.otherDrawables.clear(); layer.depthSortedMeshes.clear(); layer.depthSortedMeshData.clear(); + layer.depthSortedSpriteData.clear(); + layer.depthSortedSprites.clear(); + layer.otherDrawables.clear(); ++it; } } @@ -717,7 +740,15 @@ namespace Nz const Spheref& sphere1 = layer.depthSortedMeshData[index1].obbSphere; const Spheref& sphere2 = layer.depthSortedMeshData[index2].obbSphere; - return nearPlane.Distance(sphere1.GetPosition()) > nearPlane.Distance(sphere2.GetPosition()); + return nearPlane.Distance(sphere1.GetPosition()) < nearPlane.Distance(sphere2.GetPosition()); + }); + + std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &nearPlane] (std::size_t index1, std::size_t index2) + { + const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position; + const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position; + + return nearPlane.Distance(pos1) < nearPlane.Distance(pos2); }); SortBillboards(layer, nearPlane); @@ -741,6 +772,14 @@ namespace Nz return viewerPos.SquaredDistance(sphere1.GetPosition()) > viewerPos.SquaredDistance(sphere2.GetPosition()); }); + std::sort(layer.depthSortedSprites.begin(), layer.depthSortedSprites.end(), [&layer, &viewerPos] (std::size_t index1, std::size_t index2) + { + const Vector3f& pos1 = layer.depthSortedSpriteData[index1].vertices[0].position; + const Vector3f& pos2 = layer.depthSortedSpriteData[index2].vertices[0].position; + + return viewerPos.SquaredDistance(pos1) > viewerPos.SquaredDistance(pos2); + }); + SortBillboards(layer, nearPlane); } } diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index e450c06a4..1d202c837 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -107,6 +107,9 @@ namespace Nz if (!layer.opaqueSprites.empty()) DrawBasicSprites(sceneData, layer); + if (!layer.depthSortedSprites.empty()) + DrawOrderedSprites(sceneData, layer); + if (!layer.billboards.empty()) DrawBillboards(sceneData, layer); @@ -301,6 +304,9 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + for (auto& pipelinePair : layer.opaqueSprites) { const MaterialPipeline* pipeline = pipelinePair.first; @@ -323,6 +329,9 @@ namespace Nz // Position of the camera shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + lastShader = shader; } @@ -335,10 +344,6 @@ namespace Nz { material->Apply(pipelineInstance); - unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - - shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); - Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); auto& overlayMap = matEntry.overlayMap; @@ -362,7 +367,6 @@ namespace Nz VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); std::size_t spriteCount = 0; - std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); do { @@ -777,6 +781,142 @@ namespace Nz } } + void ForwardRenderTechnique::DrawOrderedSprites(const SceneData & sceneData, ForwardRenderQueue::Layer & layer) const + { + NazaraAssert(sceneData.viewer, "Invalid viewer"); + + Renderer::SetIndexBuffer(&s_quadIndexBuffer); + Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); + Renderer::SetVertexBuffer(&m_spriteBuffer); + + const Material* lastMaterial = nullptr; + const MaterialPipeline* lastPipeline = nullptr; + const Shader* lastShader = nullptr; + const Texture* lastOverlay = nullptr; + const MaterialPipeline::Instance* pipelineInstance = nullptr; + + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); + + bool updateVertexBuffer = true; + const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); + + std::size_t alreadyDrawnCount = 0; + std::size_t spriteIndex = 0; + std::size_t spriteChainOffset = 0; + auto splitChainIt = layer.depthSortedSprites.end(); + + for (auto it = layer.depthSortedSprites.begin(); it != layer.depthSortedSprites.end();) + { + if (updateVertexBuffer) + { + // We open the buffer in writing mode + BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); + VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); + + std::size_t availableSpriteSpace = maxSpriteCount; + bool split = false; + for (auto it2 = it; it2 != layer.depthSortedSprites.end(); ++it2) + { + const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[*it2]; + + std::size_t count = std::min(availableSpriteSpace, spriteData.spriteCount - spriteChainOffset); + + std::memcpy(vertices, spriteData.vertices + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += count * 4; + + availableSpriteSpace -= count; + + // Have we treated the entire chain ? + if (count != spriteData.spriteCount) + { + // Oops, not enough space to store current chain + spriteChainOffset += count; + splitChainIt = it2; + split = true; + break; + } + + // Switch to next sprite chain, if any + spriteChainOffset = 0; + } + + spriteIndex = 0; + updateVertexBuffer = false; + + if (!split) + splitChainIt = layer.depthSortedSprites.end(); + } + + std::size_t index = *it; + + const ForwardRenderQueue::UnbatchedSpriteData& spriteData = layer.depthSortedSpriteData[index]; + + const Material* material = spriteData.material; + if (material != lastMaterial) + { + const MaterialPipeline* pipeline = material->GetPipeline(); + if (pipeline != lastPipeline) + { + pipelineInstance = &pipeline->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + + // Uniforms are conserved in our program, there's no point to send them back until they change + if (shader != lastShader) + { + // Index of uniforms in the shader + const ShaderUniforms* shaderUniforms = GetShaderUniforms(shader); + + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + + lastShader = shader; + } + + lastPipeline = pipeline; + } + + material->Apply(*pipelineInstance); + + Renderer::SetTextureSampler(overlayTextureUnit, material->GetDiffuseSampler()); + + lastMaterial = material; + } + + const Texture* overlay = (spriteData.overlay) ? spriteData.overlay : &m_whiteTexture; + if (overlay != lastOverlay) + { + Renderer::SetTexture(overlayTextureUnit, overlay); + lastOverlay = overlay; + } + + std::size_t spriteCount; + if (it != splitChainIt) + { + spriteCount = spriteData.spriteCount - alreadyDrawnCount; + alreadyDrawnCount = 0; + + ++it; + } + else + { + spriteCount = spriteChainOffset; + + alreadyDrawnCount = spriteCount; + updateVertexBuffer = true; + + // Restart at current iterator next time + } + + Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, spriteIndex * 6, spriteCount * 6); + spriteIndex += spriteCount; + } + } + /*! * \brief Draws transparent models * @@ -796,9 +936,9 @@ namespace Nz const ShaderUniforms* shaderUniforms = nullptr; unsigned int lightCount = 0; - for (unsigned int index : layer.depthSortedMeshes) + for (std::size_t index : layer.depthSortedMeshes) { - const ForwardRenderQueue::TransparentModelData& modelData = layer.depthSortedMeshData[index]; + const ForwardRenderQueue::UnbatchedModelData& modelData = layer.depthSortedMeshData[index]; // Material const Material* material = modelData.material;