LCOV - code coverage report
Current view: top level - source/graphics - TerritoryTexture.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 120 0.8 %
Date: 2023-01-19 00:18:29 Functions: 2 13 15.4 %

          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 "TerritoryTexture.h"
      21             : 
      22             : #include "graphics/Color.h"
      23             : #include "graphics/Terrain.h"
      24             : #include "lib/bits.h"
      25             : #include "ps/Profile.h"
      26             : #include "renderer/backend/IDevice.h"
      27             : #include "renderer/backend/IDeviceCommandContext.h"
      28             : #include "renderer/Renderer.h"
      29             : #include "simulation2/Simulation2.h"
      30             : #include "simulation2/helpers/Grid.h"
      31             : #include "simulation2/helpers/Pathfinding.h"
      32             : #include "simulation2/components/ICmpPlayer.h"
      33             : #include "simulation2/components/ICmpPlayerManager.h"
      34             : #include "simulation2/components/ICmpTerrain.h"
      35             : #include "simulation2/components/ICmpTerritoryManager.h"
      36             : 
      37             : // TODO: There's a lot of duplication with CLOSTexture - might be nice to refactor a bit
      38             : 
      39           0 : CTerritoryTexture::CTerritoryTexture(CSimulation2& simulation) :
      40           0 :     m_Simulation(simulation), m_DirtyID(0), m_MapSize(0)
      41             : {
      42           0 : }
      43             : 
      44           0 : CTerritoryTexture::~CTerritoryTexture()
      45             : {
      46           0 :     DeleteTexture();
      47           0 : }
      48             : 
      49           0 : void CTerritoryTexture::DeleteTexture()
      50             : {
      51           0 :     m_Texture.reset();
      52           0 : }
      53             : 
      54           0 : bool CTerritoryTexture::UpdateDirty()
      55             : {
      56           0 :     CmpPtr<ICmpTerritoryManager> cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY);
      57           0 :     return cmpTerritoryManager && cmpTerritoryManager->NeedUpdateTexture(&m_DirtyID);
      58             : }
      59             : 
      60           0 : Renderer::Backend::ITexture* CTerritoryTexture::GetTexture()
      61             : {
      62           0 :     ENSURE(!UpdateDirty());
      63           0 :     return m_Texture.get();
      64             : }
      65             : 
      66           0 : const CMatrix3D& CTerritoryTexture::GetTextureMatrix()
      67             : {
      68           0 :     ENSURE(!UpdateDirty());
      69           0 :     return m_TextureMatrix;
      70             : }
      71             : 
      72           0 : const CMatrix3D& CTerritoryTexture::GetMinimapTextureMatrix()
      73             : {
      74           0 :     ENSURE(!UpdateDirty());
      75           0 :     return m_MinimapTextureMatrix;
      76             : }
      77             : 
      78           0 : void CTerritoryTexture::ConstructTexture(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
      79             : {
      80           0 :     CmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);
      81           0 :     if (!cmpTerrain)
      82           0 :         return;
      83             : 
      84             :     // Convert size from terrain tiles to territory tiles
      85           0 :     m_MapSize = cmpTerrain->GetMapSize() * Pathfinding::NAVCELL_SIZE_INT / ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE;
      86             : 
      87           0 :     const uint32_t textureSize = round_up_to_pow2(static_cast<uint32_t>(m_MapSize));
      88             : 
      89           0 :     m_Texture = deviceCommandContext->GetDevice()->CreateTexture2D("TerritoryTexture",
      90             :         Renderer::Backend::ITexture::Usage::TRANSFER_DST |
      91             :             Renderer::Backend::ITexture::Usage::SAMPLED,
      92             :         Renderer::Backend::Format::R8G8B8A8_UNORM, textureSize, textureSize,
      93           0 :         Renderer::Backend::Sampler::MakeDefaultSampler(
      94             :             Renderer::Backend::Sampler::Filter::LINEAR,
      95           0 :             Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
      96             : 
      97             :     // Initialise texture with transparency, for the areas we don't
      98             :     // overwrite with uploading later.
      99           0 :     std::unique_ptr<u8[]> texData = std::make_unique<u8[]>(textureSize * textureSize * 4);
     100           0 :     memset(texData.get(), 0x00, textureSize * textureSize * 4);
     101           0 :     deviceCommandContext->UploadTexture(
     102             :         m_Texture.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
     103           0 :         texData.get(), textureSize * textureSize * 4);
     104           0 :     texData.reset();
     105             : 
     106             :     {
     107             :         // Texture matrix: We want to map
     108             :         //   world pos (0, y, 0)  (i.e. bottom-left of first tile)
     109             :         //     onto texcoord (0, 0)  (i.e. bottom-left of first texel);
     110             :         //   world pos (mapsize*cellsize, y, mapsize*cellsize)  (i.e. top-right of last tile)
     111             :         //     onto texcoord (mapsize / texsize, mapsize / texsize)  (i.e. top-right of last texel)
     112             : 
     113           0 :         float s = 1.f / static_cast<float>(textureSize * TERRAIN_TILE_SIZE);
     114           0 :         float t = 0.f;
     115           0 :         m_TextureMatrix.SetZero();
     116           0 :         m_TextureMatrix._11 = s;
     117           0 :         m_TextureMatrix._23 = s;
     118           0 :         m_TextureMatrix._14 = t;
     119           0 :         m_TextureMatrix._24 = t;
     120           0 :         m_TextureMatrix._44 = 1;
     121             :     }
     122             : 
     123             :     {
     124             :         // Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)
     125             : 
     126           0 :         float s = m_MapSize / static_cast<float>(textureSize);
     127           0 :         m_MinimapTextureMatrix.SetZero();
     128           0 :         m_MinimapTextureMatrix._11 = s;
     129           0 :         m_MinimapTextureMatrix._22 = s;
     130           0 :         m_MinimapTextureMatrix._44 = 1;
     131             :     }
     132             : }
     133             : 
     134           0 : void CTerritoryTexture::RecomputeTexture(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     135             : {
     136             :     // If the map was resized, delete and regenerate the texture
     137           0 :     if (m_Texture)
     138             :     {
     139           0 :         CmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);
     140           0 :         if (cmpTerrain && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide())
     141           0 :             DeleteTexture();
     142             :     }
     143             : 
     144           0 :     if (!m_Texture)
     145           0 :         ConstructTexture(deviceCommandContext);
     146             : 
     147           0 :     PROFILE("recompute territory texture");
     148             : 
     149           0 :     CmpPtr<ICmpTerritoryManager> cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY);
     150           0 :     if (!cmpTerritoryManager)
     151           0 :         return;
     152             : 
     153           0 :     std::unique_ptr<u8[]> bitmap = std::make_unique<u8[]>(m_MapSize * m_MapSize * 4);
     154           0 :     GenerateBitmap(cmpTerritoryManager->GetTerritoryGrid(), bitmap.get(), m_MapSize, m_MapSize);
     155             : 
     156           0 :     deviceCommandContext->UploadTextureRegion(
     157           0 :         m_Texture.get(), Renderer::Backend::Format::R8G8B8A8_UNORM, bitmap.get(), m_MapSize * m_MapSize * 4,
     158           0 :         0, 0, m_MapSize, m_MapSize);
     159             : }
     160             : 
     161           0 : void CTerritoryTexture::GenerateBitmap(const Grid<u8>& territories, u8* bitmap, ssize_t w, ssize_t h)
     162             : {
     163           0 :     int alphaMax = 0xC0;
     164           0 :     int alphaFalloff = 0x20;
     165             : 
     166           0 :     CmpPtr<ICmpPlayerManager> cmpPlayerManager(m_Simulation, SYSTEM_ENTITY);
     167             : 
     168           0 :     std::vector<CColor> colors;
     169           0 :     i32 numPlayers = cmpPlayerManager->GetNumPlayers();
     170           0 :     for (i32 p = 0; p < numPlayers; ++p)
     171             :     {
     172           0 :         CColor color(1, 0, 1, 1);
     173           0 :         CmpPtr<ICmpPlayer> cmpPlayer(m_Simulation, cmpPlayerManager->GetPlayerByID(p));
     174           0 :         if (cmpPlayer)
     175           0 :             color = cmpPlayer->GetDisplayedColor();
     176           0 :         colors.push_back(color);
     177             :     }
     178             : 
     179           0 :     u8* p = bitmap;
     180           0 :     for (ssize_t j = 0; j < h; ++j)
     181           0 :         for (ssize_t i = 0; i < w; ++i)
     182             :         {
     183           0 :             u8 val = territories.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK;
     184             : 
     185           0 :             CColor color(1, 0, 1, 1);
     186           0 :             if (val < colors.size())
     187           0 :                 color = colors[val];
     188             : 
     189           0 :             *p++ = (int)(color.r * 255.f);
     190           0 :             *p++ = (int)(color.g * 255.f);
     191           0 :             *p++ = (int)(color.b * 255.f);
     192             : 
     193             :             // Use alphaMax for borders and gaia territory; these tiles will be deleted later
     194           0 :             if (val == 0 ||
     195           0 :                (i > 0   && (territories.get(i-1, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val) ||
     196           0 :                (i < w-1 && (territories.get(i+1, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val) ||
     197           0 :                (j > 0   && (territories.get(i, j-1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val) ||
     198           0 :                (j < h-1 && (territories.get(i, j+1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val))
     199           0 :                 *p++ = alphaMax;
     200             :             else
     201           0 :                 *p++ = 0x00;
     202             :         }
     203             : 
     204             :     // Do a low-quality cheap blur effect
     205             : 
     206           0 :     for (ssize_t j = 0; j < h; ++j)
     207             :     {
     208             :         int a;
     209             : 
     210           0 :         a = 0;
     211           0 :         for (ssize_t i = 0; i < w; ++i)
     212             :         {
     213           0 :             a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
     214           0 :             bitmap[(j*w+i)*4 + 3] = a;
     215             :         }
     216             : 
     217           0 :         a = 0;
     218           0 :         for (ssize_t i = w-1; i >= 0; --i)
     219             :         {
     220           0 :             a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
     221           0 :             bitmap[(j*w+i)*4 + 3] = a;
     222             :         }
     223             :     }
     224             : 
     225           0 :     for (ssize_t i = 0; i < w; ++i)
     226             :     {
     227             :         int a;
     228             : 
     229           0 :         a = 0;
     230           0 :         for (ssize_t j = 0; j < w; ++j)
     231             :         {
     232           0 :             a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
     233           0 :             bitmap[(j*w+i)*4 + 3] = a;
     234             :         }
     235             : 
     236           0 :         a = 0;
     237           0 :         for (ssize_t j = w-1; j >= 0; --j)
     238             :         {
     239           0 :             a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]);
     240           0 :             bitmap[(j*w+i)*4 + 3] = a;
     241             :         }
     242             :     }
     243             : 
     244             :     // Add a gap between the boundaries, by deleting the max-alpha tiles
     245           0 :     for (ssize_t j = 0; j < h; ++j)
     246           0 :         for (ssize_t i = 0; i < w; ++i)
     247           0 :             if (bitmap[(j*w+i)*4 + 3] == alphaMax)
     248           0 :                 bitmap[(j*w+i)*4 + 3] = 0;
     249           0 : }
     250             : 
     251           0 : void CTerritoryTexture::UpdateIfNeeded(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     252             : {
     253           0 :     if (UpdateDirty())
     254           0 :         RecomputeTexture(deviceCommandContext);
     255           3 : }

Generated by: LCOV version 1.13