1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "TerritoryTexture.h"
22 #include "graphics/Color.h"
23 #include "graphics/Terrain.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"
37 // TODO: There's a lot of duplication with CLOSTexture - might be nice to refactor a bit
39 CTerritoryTexture::CTerritoryTexture(CSimulation2
& simulation
) :
40 m_Simulation(simulation
), m_DirtyID(0), m_MapSize(0)
44 CTerritoryTexture::~CTerritoryTexture()
49 void CTerritoryTexture::DeleteTexture()
54 bool CTerritoryTexture::UpdateDirty()
56 CmpPtr
<ICmpTerritoryManager
> cmpTerritoryManager(m_Simulation
, SYSTEM_ENTITY
);
57 return cmpTerritoryManager
&& cmpTerritoryManager
->NeedUpdateTexture(&m_DirtyID
);
60 Renderer::Backend::ITexture
* CTerritoryTexture::GetTexture()
62 ENSURE(!UpdateDirty());
63 return m_Texture
.get();
66 const CMatrix3D
& CTerritoryTexture::GetTextureMatrix()
68 ENSURE(!UpdateDirty());
69 return m_TextureMatrix
;
72 const CMatrix3D
& CTerritoryTexture::GetMinimapTextureMatrix()
74 ENSURE(!UpdateDirty());
75 return m_MinimapTextureMatrix
;
78 void CTerritoryTexture::ConstructTexture(Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
)
80 CmpPtr
<ICmpTerrain
> cmpTerrain(m_Simulation
, SYSTEM_ENTITY
);
84 // Convert size from terrain tiles to territory tiles
85 m_MapSize
= cmpTerrain
->GetMapSize() * Pathfinding::NAVCELL_SIZE_INT
/ ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE
;
87 const uint32_t textureSize
= round_up_to_pow2(static_cast<uint32_t>(m_MapSize
));
89 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 Renderer::Backend::Sampler::MakeDefaultSampler(
94 Renderer::Backend::Sampler::Filter::LINEAR
,
95 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE
));
97 // Initialise texture with transparency, for the areas we don't
98 // overwrite with uploading later.
99 std::unique_ptr
<u8
[]> texData
= std::make_unique
<u8
[]>(textureSize
* textureSize
* 4);
100 memset(texData
.get(), 0x00, textureSize
* textureSize
* 4);
101 deviceCommandContext
->UploadTexture(
102 m_Texture
.get(), Renderer::Backend::Format::R8G8B8A8_UNORM
,
103 texData
.get(), textureSize
* textureSize
* 4);
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)
113 float s
= 1.f
/ static_cast<float>(textureSize
* TERRAIN_TILE_SIZE
);
115 m_TextureMatrix
.SetZero();
116 m_TextureMatrix
._11
= s
;
117 m_TextureMatrix
._23
= s
;
118 m_TextureMatrix
._14
= t
;
119 m_TextureMatrix
._24
= t
;
120 m_TextureMatrix
._44
= 1;
124 // Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)
126 float s
= m_MapSize
/ static_cast<float>(textureSize
);
127 m_MinimapTextureMatrix
.SetZero();
128 m_MinimapTextureMatrix
._11
= s
;
129 m_MinimapTextureMatrix
._22
= s
;
130 m_MinimapTextureMatrix
._44
= 1;
134 void CTerritoryTexture::RecomputeTexture(Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
)
136 // If the map was resized, delete and regenerate the texture
139 CmpPtr
<ICmpTerrain
> cmpTerrain(m_Simulation
, SYSTEM_ENTITY
);
140 if (cmpTerrain
&& m_MapSize
!= (ssize_t
)cmpTerrain
->GetVerticesPerSide())
145 ConstructTexture(deviceCommandContext
);
147 PROFILE("recompute territory texture");
149 CmpPtr
<ICmpTerritoryManager
> cmpTerritoryManager(m_Simulation
, SYSTEM_ENTITY
);
150 if (!cmpTerritoryManager
)
153 std::unique_ptr
<u8
[]> bitmap
= std::make_unique
<u8
[]>(m_MapSize
* m_MapSize
* 4);
154 GenerateBitmap(cmpTerritoryManager
->GetTerritoryGrid(), bitmap
.get(), m_MapSize
, m_MapSize
);
156 deviceCommandContext
->UploadTextureRegion(
157 m_Texture
.get(), Renderer::Backend::Format::R8G8B8A8_UNORM
, bitmap
.get(), m_MapSize
* m_MapSize
* 4,
158 0, 0, m_MapSize
, m_MapSize
);
161 void CTerritoryTexture::GenerateBitmap(const Grid
<u8
>& territories
, u8
* bitmap
, ssize_t w
, ssize_t h
)
164 int alphaFalloff
= 0x20;
166 CmpPtr
<ICmpPlayerManager
> cmpPlayerManager(m_Simulation
, SYSTEM_ENTITY
);
168 std::vector
<CColor
> colors
;
169 i32 numPlayers
= cmpPlayerManager
->GetNumPlayers();
170 for (i32 p
= 0; p
< numPlayers
; ++p
)
172 CColor
color(1, 0, 1, 1);
173 CmpPtr
<ICmpPlayer
> cmpPlayer(m_Simulation
, cmpPlayerManager
->GetPlayerByID(p
));
175 color
= cmpPlayer
->GetDisplayedColor();
176 colors
.push_back(color
);
180 for (ssize_t j
= 0; j
< h
; ++j
)
181 for (ssize_t i
= 0; i
< w
; ++i
)
183 u8 val
= territories
.get(i
, j
) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK
;
185 CColor
color(1, 0, 1, 1);
186 if (val
< colors
.size())
189 *p
++ = (int)(color
.r
* 255.f
);
190 *p
++ = (int)(color
.g
* 255.f
);
191 *p
++ = (int)(color
.b
* 255.f
);
193 // Use alphaMax for borders and gaia territory; these tiles will be deleted later
195 (i
> 0 && (territories
.get(i
-1, j
) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK
) != val
) ||
196 (i
< w
-1 && (territories
.get(i
+1, j
) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK
) != val
) ||
197 (j
> 0 && (territories
.get(i
, j
-1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK
) != val
) ||
198 (j
< h
-1 && (territories
.get(i
, j
+1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK
) != val
))
204 // Do a low-quality cheap blur effect
206 for (ssize_t j
= 0; j
< h
; ++j
)
211 for (ssize_t i
= 0; i
< w
; ++i
)
213 a
= std::max(a
- alphaFalloff
, (int)bitmap
[(j
*w
+i
)*4 + 3]);
214 bitmap
[(j
*w
+i
)*4 + 3] = a
;
218 for (ssize_t i
= w
-1; i
>= 0; --i
)
220 a
= std::max(a
- alphaFalloff
, (int)bitmap
[(j
*w
+i
)*4 + 3]);
221 bitmap
[(j
*w
+i
)*4 + 3] = a
;
225 for (ssize_t i
= 0; i
< w
; ++i
)
230 for (ssize_t j
= 0; j
< w
; ++j
)
232 a
= std::max(a
- alphaFalloff
, (int)bitmap
[(j
*w
+i
)*4 + 3]);
233 bitmap
[(j
*w
+i
)*4 + 3] = a
;
237 for (ssize_t j
= w
-1; j
>= 0; --j
)
239 a
= std::max(a
- alphaFalloff
, (int)bitmap
[(j
*w
+i
)*4 + 3]);
240 bitmap
[(j
*w
+i
)*4 + 3] = a
;
244 // Add a gap between the boundaries, by deleting the max-alpha tiles
245 for (ssize_t j
= 0; j
< h
; ++j
)
246 for (ssize_t i
= 0; i
< w
; ++i
)
247 if (bitmap
[(j
*w
+i
)*4 + 3] == alphaMax
)
248 bitmap
[(j
*w
+i
)*4 + 3] = 0;
251 void CTerritoryTexture::UpdateIfNeeded(Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
)
254 RecomputeTexture(deviceCommandContext
);