LCOV - code coverage report
Current view: top level - source/renderer - DecalRData.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 151 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 5 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "DecalRData.h"
      21             : 
      22             : #include "graphics/Decal.h"
      23             : #include "graphics/Model.h"
      24             : #include "graphics/ShaderManager.h"
      25             : #include "graphics/Terrain.h"
      26             : #include "graphics/TextureManager.h"
      27             : #include "lib/allocators/DynamicArena.h"
      28             : #include "lib/allocators/STLAllocators.h"
      29             : #include "ps/CLogger.h"
      30             : #include "ps/CStrInternStatic.h"
      31             : #include "ps/Game.h"
      32             : #include "ps/Profile.h"
      33             : #include "renderer/Renderer.h"
      34             : #include "renderer/TerrainRenderer.h"
      35             : #include "simulation2/components/ICmpWaterManager.h"
      36             : #include "simulation2/Simulation2.h"
      37             : 
      38             : #include <algorithm>
      39             : 
      40             : // TODO: Currently each decal is a separate CDecalRData. We might want to use
      41             : // lots of decals for special effects like shadows, footprints, etc, in which
      42             : // case we should probably redesign this to batch them all together for more
      43             : // efficient rendering.
      44             : 
      45             : namespace
      46             : {
      47             : 
      48           0 : struct SDecalBatch
      49             : {
      50             :     CDecalRData* decal;
      51             :     CShaderTechniquePtr shaderTech;
      52             :     CVertexBuffer::VBChunk* vertices;
      53             :     CVertexBuffer::VBChunk* indices;
      54             : };
      55             : 
      56             : struct SDecalBatchComparator
      57             : {
      58           0 :     bool operator()(const SDecalBatch& lhs, const SDecalBatch& rhs) const
      59             :     {
      60           0 :         if (lhs.shaderTech != rhs.shaderTech)
      61           0 :             return lhs.shaderTech < rhs.shaderTech;
      62           0 :         if (lhs.vertices->m_Owner != rhs.vertices->m_Owner)
      63           0 :             return lhs.vertices->m_Owner < rhs.vertices->m_Owner;
      64           0 :         if (lhs.indices->m_Owner != rhs.indices->m_Owner)
      65           0 :             return lhs.indices->m_Owner < rhs.indices->m_Owner;
      66           0 :         return lhs.decal < rhs.decal;
      67             :     }
      68             : };
      69             : 
      70             : } // anonymous namespace
      71             : 
      72           0 : CDecalRData::CDecalRData(CModelDecal* decal, CSimulation2* simulation)
      73           0 :     : m_Decal(decal), m_Simulation(simulation)
      74             : {
      75           0 :     BuildVertexData();
      76           0 : }
      77             : 
      78             : CDecalRData::~CDecalRData() = default;
      79             : 
      80           0 : void CDecalRData::Update(CSimulation2* simulation)
      81             : {
      82           0 :     m_Simulation = simulation;
      83           0 :     if (m_UpdateFlags != 0)
      84             :     {
      85           0 :         BuildVertexData();
      86           0 :         m_UpdateFlags = 0;
      87             :     }
      88           0 : }
      89             : 
      90           0 : void CDecalRData::RenderDecals(
      91             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
      92             :     const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow)
      93             : {
      94           0 :     PROFILE3("render terrain decals");
      95           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain decals");
      96             : 
      97           0 :     using Arena = Allocators::DynamicArena<256 * KiB>;
      98             : 
      99           0 :     Arena arena;
     100             : 
     101           0 :     using Batches = std::vector<SDecalBatch, ProxyAllocator<SDecalBatch, Arena>>;
     102           0 :     Batches batches((Batches::allocator_type(arena)));
     103           0 :     batches.reserve(decals.size());
     104             : 
     105           0 :     CShaderDefines contextDecal = context;
     106           0 :     contextDecal.Add(str_DECAL, str_1);
     107             : 
     108           0 :     for (CDecalRData* decal : decals)
     109             :     {
     110           0 :         CMaterial &material = decal->m_Decal->m_Decal.m_Material;
     111             : 
     112           0 :         if (material.GetShaderEffect().empty())
     113             :         {
     114           0 :             LOGERROR("Terrain renderer failed to load shader effect.\n");
     115           0 :             continue;
     116             :         }
     117             : 
     118           0 :         CShaderDefines defines = contextDecal;
     119           0 :         defines.SetMany(material.GetShaderDefines(0));
     120           0 :         CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
     121           0 :             material.GetShaderEffect(), defines);
     122           0 :         if (!techBase)
     123             :         {
     124           0 :             LOGERROR("Terrain renderer failed to load shader effect (%s)\n",
     125             :                     material.GetShaderEffect().string().c_str());
     126           0 :             continue;
     127             :         }
     128             : 
     129           0 :         if (material.GetSamplers().empty() || !decal->m_VBDecals || !decal->m_VBDecalsIndices)
     130             :             continue;
     131             : 
     132           0 :         SDecalBatch batch;
     133           0 :         batch.decal = decal;
     134           0 :         batch.shaderTech = techBase;
     135           0 :         batch.vertices = decal->m_VBDecals.Get();
     136           0 :         batch.indices = decal->m_VBDecalsIndices.Get();
     137             : 
     138           0 :         batches.emplace_back(std::move(batch));
     139             :     }
     140             : 
     141           0 :     if (batches.empty())
     142           0 :         return;
     143             : 
     144           0 :     std::sort(batches.begin(), batches.end(), SDecalBatchComparator());
     145             : 
     146           0 :     CVertexBuffer* lastIB = nullptr;
     147           0 :     for (auto itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd)
     148             :     {
     149           0 :         while (itTechEnd != batches.end() && itTechBegin->shaderTech == itTechEnd->shaderTech)
     150           0 :             ++itTechEnd;
     151             : 
     152           0 :         const CShaderTechniquePtr& techBase = itTechBegin->shaderTech;
     153           0 :         const int numPasses = techBase->GetNumPasses();
     154             : 
     155           0 :         for (int pass = 0; pass < numPasses; ++pass)
     156             :         {
     157           0 :             Renderer::Backend::GraphicsPipelineStateDesc pipelineStateDesc =
     158           0 :                 techBase->GetGraphicsPipelineStateDesc(pass);
     159           0 :             pipelineStateDesc.blendState.enabled = true;
     160           0 :             pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
     161             :                 Renderer::Backend::BlendFactor::SRC_ALPHA;
     162           0 :             pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
     163             :                 Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
     164           0 :             pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
     165             :                 Renderer::Backend::BlendOp::ADD;
     166           0 :             techBase->BeginPass(pass);
     167           0 :             deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
     168             : 
     169           0 :             const CShaderProgramPtr& shader = techBase->GetShader(pass);
     170           0 :             TerrainRenderer::PrepareShader(shader, shadow);
     171             : 
     172             :             CVertexBuffer* lastVB = nullptr;
     173           0 :             for (auto itDecal = itTechBegin; itDecal != itTechEnd; ++itDecal)
     174             :             {
     175           0 :                 SDecalBatch& batch = *itDecal;
     176           0 :                 CDecalRData* decal = batch.decal;
     177           0 :                 CMaterial& material = decal->m_Decal->m_Decal.m_Material;
     178             : 
     179           0 :                 const CMaterial::SamplersVector& samplers = material.GetSamplers();
     180           0 :                 for (const CMaterial::TextureSampler& sampler : samplers)
     181           0 :                     sampler.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
     182           0 :                 for (const CMaterial::TextureSampler& sampler : samplers)
     183           0 :                     shader->BindTexture(sampler.Name, sampler.Sampler->GetBackendTexture());
     184             : 
     185           0 :                 material.GetStaticUniforms().BindUniforms(shader);
     186             : 
     187             :                 // TODO: Need to handle floating decals correctly. In particular, we need
     188             :                 // to render non-floating before water and floating after water (to get
     189             :                 // the blending right), and we also need to apply the correct lighting in
     190             :                 // each case, which doesn't really seem possible with the current
     191             :                 // TerrainRenderer.
     192             :                 // Also, need to mark the decals as dirty when water height changes.
     193             : 
     194             :                 //  m_Decal->GetBounds().Render();
     195             : 
     196           0 :                 shader->Uniform(str_shadingColor, decal->m_Decal->GetShadingColor());
     197             : 
     198           0 :                 if (lastVB != batch.vertices->m_Owner)
     199             :                 {
     200           0 :                     lastVB = batch.vertices->m_Owner;
     201           0 :                     const GLsizei stride = sizeof(SDecalVertex);
     202           0 :                     SDecalVertex* base = (SDecalVertex*)batch.vertices->m_Owner->Bind(deviceCommandContext);
     203             : 
     204           0 :                     shader->VertexPointer(
     205           0 :                         Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Position[0]);
     206           0 :                     shader->NormalPointer(
     207           0 :                         Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &base->m_Normal[0]);
     208           0 :                     shader->TexCoordPointer(
     209           0 :                         GL_TEXTURE0, Renderer::Backend::Format::R32G32_SFLOAT, stride, &base->m_UV[0]);
     210             :                 }
     211             : 
     212           0 :                 shader->AssertPointersBound();
     213             : 
     214           0 :                 if (lastIB != batch.indices->m_Owner)
     215             :                 {
     216           0 :                     lastIB = batch.indices->m_Owner;
     217           0 :                     batch.indices->m_Owner->UploadIfNeeded(deviceCommandContext);
     218           0 :                     deviceCommandContext->SetIndexBuffer(batch.indices->m_Owner->GetBuffer());
     219             :                 }
     220             : 
     221           0 :                 deviceCommandContext->DrawIndexed(batch.indices->m_Index, batch.indices->m_Count, 0);
     222             : 
     223             :                 // bump stats
     224           0 :                 g_Renderer.m_Stats.m_DrawCalls++;
     225           0 :                 g_Renderer.m_Stats.m_TerrainTris += batch.indices->m_Count / 3;
     226             :             }
     227             : 
     228           0 :             techBase->EndPass();
     229             :         }
     230             :     }
     231             : 
     232           0 :     CVertexBuffer::Unbind(deviceCommandContext);
     233             : }
     234             : 
     235           0 : void CDecalRData::BuildVertexData()
     236             : {
     237           0 :     PROFILE("decal build");
     238             : 
     239           0 :     const SDecal& decal = m_Decal->m_Decal;
     240             : 
     241             :     // TODO: Currently this constructs an axis-aligned bounding rectangle around
     242             :     // the decal. It would be more efficient for rendering if we excluded tiles
     243             :     // that are outside the (non-axis-aligned) decal rectangle.
     244             : 
     245           0 :     ssize_t i0, j0, i1, j1;
     246           0 :     m_Decal->CalcVertexExtents(i0, j0, i1, j1);
     247             :     // Currently CalcVertexExtents might return empty rectangle, that means
     248             :     // we can't render it.
     249           0 :     if (i1 <= i0 || j1 <= j0)
     250             :     {
     251             :         // We have nothing to render.
     252           0 :         m_VBDecals.Reset();
     253           0 :         m_VBDecalsIndices.Reset();
     254           0 :         return;
     255             :     }
     256             : 
     257           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
     258             : 
     259           0 :     std::vector<SDecalVertex> vertices((i1 - i0 + 1) * (j1 - j0 + 1));
     260             : 
     261           0 :     for (ssize_t j = j0, idx = 0; j <= j1; ++j)
     262             :     {
     263           0 :         for (ssize_t i = i0; i <= i1; ++i, ++idx)
     264             :         {
     265           0 :             SDecalVertex& vertex = vertices[idx];
     266           0 :             m_Decal->m_Terrain->CalcPosition(i, j, vertex.m_Position);
     267             : 
     268           0 :             if (decal.m_Floating && cmpWaterManager)
     269             :             {
     270           0 :                 vertex.m_Position.Y = std::max(
     271           0 :                     vertex.m_Position.Y,
     272           0 :                     cmpWaterManager->GetExactWaterLevel(vertex.m_Position.X, vertex.m_Position.Z));
     273             :             }
     274             : 
     275           0 :             m_Decal->m_Terrain->CalcNormal(i, j, vertex.m_Normal);
     276             : 
     277             :             // Map from world space back into decal texture space.
     278           0 :             CVector3D inv = m_Decal->GetInvTransform().Transform(vertex.m_Position);
     279           0 :             vertex.m_UV.X = 0.5f + (inv.X - decal.m_OffsetX) / decal.m_SizeX;
     280             :             // Flip V to match our texture convention.
     281           0 :             vertex.m_UV.Y = 0.5f - (inv.Z - decal.m_OffsetZ) / decal.m_SizeZ;
     282             :         }
     283             :     }
     284             : 
     285           0 :     if (!m_VBDecals || m_VBDecals->m_Count != vertices.size())
     286             :     {
     287           0 :         m_VBDecals = g_VBMan.AllocateChunk(
     288             :             sizeof(SDecalVertex), vertices.size(),
     289           0 :             Renderer::Backend::GL::CBuffer::Type::VERTEX, false);
     290             :     }
     291           0 :     m_VBDecals->m_Owner->UpdateChunkVertices(m_VBDecals.Get(), vertices.data());
     292             : 
     293           0 :     std::vector<u16> indices((i1 - i0) * (j1 - j0) * 6);
     294             : 
     295           0 :     const ssize_t w = i1 - i0 + 1;
     296           0 :     auto itIdx = indices.begin();
     297           0 :     const size_t base = m_VBDecals->m_Index;
     298           0 :     for (ssize_t dj = 0; dj < j1 - j0; ++dj)
     299             :     {
     300           0 :         for (ssize_t di = 0; di < i1 - i0; ++di)
     301             :         {
     302           0 :             const bool dir = m_Decal->m_Terrain->GetTriangulationDir(i0 + di, j0 + dj);
     303           0 :             if (dir)
     304             :             {
     305           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     306           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     307           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     308             : 
     309           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     310           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     311           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     312             :             }
     313             :             else
     314             :             {
     315           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     316           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     317           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     318             : 
     319           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     320           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     321           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     322             :             }
     323             :         }
     324             :     }
     325             : 
     326             :     // Construct vertex buffer.
     327           0 :     if (!m_VBDecalsIndices || m_VBDecalsIndices->m_Count != indices.size())
     328             :     {
     329           0 :         m_VBDecalsIndices = g_VBMan.AllocateChunk(
     330             :             sizeof(u16), indices.size(),
     331           0 :             Renderer::Backend::GL::CBuffer::Type::INDEX, false);
     332             :     }
     333           0 :     m_VBDecalsIndices->m_Owner->UpdateChunkVertices(m_VBDecalsIndices.Get(), indices.data());
     334           0 : }

Generated by: LCOV version 1.13