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 138 0.0 %
Date: 2021-09-24 14:46:47 Functions: 0 5 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2021 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             : #include <vector>
      40             : 
      41             : // TODO: Currently each decal is a separate CDecalRData. We might want to use
      42             : // lots of decals for special effects like shadows, footprints, etc, in which
      43             : // case we should probably redesign this to batch them all together for more
      44             : // efficient rendering.
      45             : 
      46             : namespace
      47             : {
      48             : 
      49           0 : struct SDecalBatch
      50             : {
      51             :     CDecalRData* decal;
      52             :     CShaderTechniquePtr shaderTech;
      53             :     CVertexBuffer::VBChunk* vertices;
      54             :     CVertexBuffer::VBChunk* indices;
      55             : };
      56             : 
      57             : struct SDecalBatchComparator
      58             : {
      59           0 :     bool operator()(const SDecalBatch& lhs, const SDecalBatch& rhs) const
      60             :     {
      61           0 :         if (lhs.shaderTech != rhs.shaderTech)
      62           0 :             return lhs.shaderTech < rhs.shaderTech;
      63           0 :         if (lhs.vertices->m_Owner != rhs.vertices->m_Owner)
      64           0 :             return lhs.vertices->m_Owner < rhs.vertices->m_Owner;
      65           0 :         if (lhs.indices->m_Owner != rhs.indices->m_Owner)
      66           0 :             return lhs.indices->m_Owner < rhs.indices->m_Owner;
      67           0 :         return lhs.decal < rhs.decal;
      68             :     }
      69             : };
      70             : 
      71             : } // anonymous namespace
      72             : 
      73           0 : CDecalRData::CDecalRData(CModelDecal* decal, CSimulation2* simulation)
      74           0 :     : m_Decal(decal), m_Simulation(simulation)
      75             : {
      76           0 :     BuildVertexData();
      77           0 : }
      78             : 
      79             : CDecalRData::~CDecalRData() = default;
      80             : 
      81           0 : void CDecalRData::Update(CSimulation2* simulation)
      82             : {
      83           0 :     m_Simulation = simulation;
      84           0 :     if (m_UpdateFlags != 0)
      85             :     {
      86           0 :         BuildVertexData();
      87           0 :         m_UpdateFlags = 0;
      88             :     }
      89           0 : }
      90             : 
      91           0 : void CDecalRData::RenderDecals(
      92             :     const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow)
      93             : {
      94           0 :     PROFILE3("render terrain decals");
      95             : 
      96           0 :     using Arena = Allocators::DynamicArena<256 * KiB>;
      97             : 
      98           0 :     Arena arena;
      99             : 
     100           0 :     using Batches = std::vector<SDecalBatch, ProxyAllocator<SDecalBatch, Arena>>;
     101           0 :     Batches batches((Batches::allocator_type(arena)));
     102           0 :     batches.reserve(decals.size());
     103             : 
     104           0 :     CShaderDefines contextDecal = context;
     105           0 :     contextDecal.Add(str_DECAL, str_1);
     106             : 
     107           0 :     for (CDecalRData* decal : decals)
     108             :     {
     109           0 :         CMaterial &material = decal->m_Decal->m_Decal.m_Material;
     110             : 
     111           0 :         if (material.GetShaderEffect().empty())
     112             :         {
     113           0 :             LOGERROR("Terrain renderer failed to load shader effect.\n");
     114           0 :             continue;
     115             :         }
     116             : 
     117           0 :         CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
     118           0 :             material.GetShaderEffect(), contextDecal, material.GetShaderDefines(0));
     119           0 :         if (!techBase)
     120             :         {
     121           0 :             LOGERROR("Terrain renderer failed to load shader effect (%s)\n",
     122             :                     material.GetShaderEffect().string().c_str());
     123           0 :             continue;
     124             :         }
     125             : 
     126           0 :         if (material.GetSamplers().empty() || !decal->m_VBDecals || !decal->m_VBDecalsIndices)
     127             :             continue;
     128             : 
     129           0 :         SDecalBatch batch;
     130           0 :         batch.decal = decal;
     131           0 :         batch.shaderTech = techBase;
     132           0 :         batch.vertices = decal->m_VBDecals.Get();
     133           0 :         batch.indices = decal->m_VBDecalsIndices.Get();
     134             : 
     135           0 :         batches.emplace_back(std::move(batch));
     136             :     }
     137             : 
     138           0 :     if (batches.empty())
     139           0 :         return;
     140             : 
     141           0 :     std::sort(batches.begin(), batches.end(), SDecalBatchComparator());
     142             : 
     143           0 :     glEnable(GL_BLEND);
     144           0 :     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     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 :             techBase->BeginPass(pass);
     158           0 :             const CShaderProgramPtr& shader = techBase->GetShader(pass);
     159           0 :             TerrainRenderer::PrepareShader(shader, shadow);
     160             : 
     161             :             CVertexBuffer* lastVB = nullptr;
     162           0 :             for (auto itDecal = itTechBegin; itDecal != itTechEnd; ++itDecal)
     163             :             {
     164           0 :                 SDecalBatch& batch = *itDecal;
     165           0 :                 CDecalRData* decal = batch.decal;
     166           0 :                 CMaterial& material = decal->m_Decal->m_Decal.m_Material;
     167             : 
     168           0 :                 const CMaterial::SamplersVector& samplers = material.GetSamplers();
     169           0 :                 for (const CMaterial::TextureSampler& sampler : samplers)
     170           0 :                     shader->BindTexture(sampler.Name, sampler.Sampler);
     171             : 
     172           0 :                 material.GetStaticUniforms().BindUniforms(shader);
     173             : 
     174             :                 // TODO: Need to handle floating decals correctly. In particular, we need
     175             :                 // to render non-floating before water and floating after water (to get
     176             :                 // the blending right), and we also need to apply the correct lighting in
     177             :                 // each case, which doesn't really seem possible with the current
     178             :                 // TerrainRenderer.
     179             :                 // Also, need to mark the decals as dirty when water height changes.
     180             : 
     181             :                 //  glDisable(GL_TEXTURE_2D);
     182             :                 //  m_Decal->GetBounds().Render();
     183             :                 //  glEnable(GL_TEXTURE_2D);
     184             : 
     185           0 :                 shader->Uniform(str_shadingColor, decal->m_Decal->GetShadingColor());
     186             : 
     187           0 :                 if (lastVB != batch.vertices->m_Owner)
     188             :                 {
     189           0 :                     lastVB = batch.vertices->m_Owner;
     190           0 :                     const GLsizei stride = sizeof(SDecalVertex);
     191           0 :                     SDecalVertex* base = (SDecalVertex*)batch.vertices->m_Owner->Bind();
     192             : 
     193           0 :                     shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
     194           0 :                     shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
     195           0 :                     shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, &base->m_UV[0]);
     196             :                 }
     197             : 
     198           0 :                 shader->AssertPointersBound();
     199             : 
     200           0 :                 if (lastIB != batch.indices->m_Owner)
     201             :                 {
     202           0 :                     lastIB = batch.indices->m_Owner;
     203           0 :                     batch.indices->m_Owner->Bind();
     204             :                 }
     205             : 
     206           0 :                 u8* indexBase = nullptr;
     207           0 :                 if (!g_Renderer.m_SkipSubmit)
     208           0 :                     glDrawElements(GL_TRIANGLES, batch.indices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16) * (batch.indices->m_Index));
     209             : 
     210             :                 // bump stats
     211           0 :                 g_Renderer.m_Stats.m_DrawCalls++;
     212           0 :                 g_Renderer.m_Stats.m_TerrainTris += batch.indices->m_Count / 3;
     213             :             }
     214             : 
     215           0 :             techBase->EndPass();
     216             :         }
     217             :     }
     218             : 
     219           0 :     CVertexBuffer::Unbind();
     220             : 
     221           0 :     glDisable(GL_BLEND);
     222             : }
     223             : 
     224           0 : void CDecalRData::BuildVertexData()
     225             : {
     226           0 :     PROFILE("decal build");
     227             : 
     228           0 :     const SDecal& decal = m_Decal->m_Decal;
     229             : 
     230             :     // TODO: Currently this constructs an axis-aligned bounding rectangle around
     231             :     // the decal. It would be more efficient for rendering if we excluded tiles
     232             :     // that are outside the (non-axis-aligned) decal rectangle.
     233             : 
     234           0 :     ssize_t i0, j0, i1, j1;
     235           0 :     m_Decal->CalcVertexExtents(i0, j0, i1, j1);
     236             :     // Currently CalcVertexExtents might return empty rectangle, that means
     237             :     // we can't render it.
     238           0 :     if (i1 <= i0 && j1 <= j0)
     239             :     {
     240             :         // We have nothing to render.
     241           0 :         m_VBDecals.Reset();
     242           0 :         m_VBDecalsIndices.Reset();
     243           0 :         return;
     244             :     }
     245             : 
     246           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
     247             : 
     248           0 :     std::vector<SDecalVertex> vertices((i1 - i0 + 1) * (j1 - j0 + 1));
     249             : 
     250           0 :     for (ssize_t j = j0, idx = 0; j <= j1; ++j)
     251             :     {
     252           0 :         for (ssize_t i = i0; i <= i1; ++i, ++idx)
     253             :         {
     254           0 :             SDecalVertex& vertex = vertices[idx];
     255           0 :             m_Decal->m_Terrain->CalcPosition(i, j, vertex.m_Position);
     256             : 
     257           0 :             if (decal.m_Floating && cmpWaterManager)
     258             :             {
     259           0 :                 vertex.m_Position.Y = std::max(
     260           0 :                     vertex.m_Position.Y,
     261           0 :                     cmpWaterManager->GetExactWaterLevel(vertex.m_Position.X, vertex.m_Position.Z));
     262             :             }
     263             : 
     264           0 :             m_Decal->m_Terrain->CalcNormal(i, j, vertex.m_Normal);
     265             : 
     266             :             // Map from world space back into decal texture space.
     267           0 :             CVector3D inv = m_Decal->GetInvTransform().Transform(vertex.m_Position);
     268           0 :             vertex.m_UV.X = 0.5f + (inv.X - decal.m_OffsetX) / decal.m_SizeX;
     269             :             // Flip V to match our texture convention.
     270           0 :             vertex.m_UV.Y = 0.5f - (inv.Z - decal.m_OffsetZ) / decal.m_SizeZ;
     271             :         }
     272             :     }
     273             : 
     274           0 :     if (!m_VBDecals || m_VBDecals->m_Count != vertices.size())
     275           0 :         m_VBDecals = g_VBMan.AllocateChunk(sizeof(SDecalVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
     276           0 :     m_VBDecals->m_Owner->UpdateChunkVertices(m_VBDecals.Get(), vertices.data());
     277             : 
     278           0 :     std::vector<u16> indices((i1 - i0) * (j1 - j0) * 6);
     279             : 
     280           0 :     const ssize_t w = i1 - i0 + 1;
     281           0 :     auto itIdx = indices.begin();
     282           0 :     const size_t base = m_VBDecals->m_Index;
     283           0 :     for (ssize_t dj = 0; dj < j1 - j0; ++dj)
     284             :     {
     285           0 :         for (ssize_t di = 0; di < i1 - i0; ++di)
     286             :         {
     287           0 :             const bool dir = m_Decal->m_Terrain->GetTriangulationDir(i0 + di, j0 + dj);
     288           0 :             if (dir)
     289             :             {
     290           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     291           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     292           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     293             : 
     294           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     295           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     296           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     297             :             }
     298             :             else
     299             :             {
     300           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     301           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     302           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     303             : 
     304           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     305           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     306           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     307             :             }
     308             :         }
     309             :     }
     310             : 
     311             :     // Construct vertex buffer.
     312           0 :     if (!m_VBDecalsIndices || m_VBDecalsIndices->m_Count != indices.size())
     313           0 :         m_VBDecalsIndices = g_VBMan.AllocateChunk(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
     314           0 :     m_VBDecalsIndices->m_Owner->UpdateChunkVertices(m_VBDecalsIndices.Get(), indices.data());
     315           0 : }

Generated by: LCOV version 1.13