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 "TerrainOverlay.h"
21 :
22 : #include "graphics/Color.h"
23 : #include "graphics/ShaderManager.h"
24 : #include "graphics/ShaderProgram.h"
25 : #include "graphics/Terrain.h"
26 : #include "lib/bits.h"
27 : #include "lib/ogl.h"
28 : #include "maths/MathUtil.h"
29 : #include "ps/CStrInternStatic.h"
30 : #include "ps/Game.h"
31 : #include "ps/Profile.h"
32 : #include "ps/World.h"
33 : #include "renderer/backend/gl/Device.h"
34 : #include "renderer/Renderer.h"
35 : #include "renderer/SceneRenderer.h"
36 : #include "renderer/TerrainRenderer.h"
37 : #include "simulation2/system/SimContext.h"
38 :
39 : #include <algorithm>
40 :
41 : // Global overlay list management:
42 :
43 : static std::vector<std::pair<ITerrainOverlay*, int> > g_TerrainOverlayList;
44 :
45 0 : ITerrainOverlay::ITerrainOverlay(int priority)
46 : {
47 : // Add to global list of overlays
48 0 : g_TerrainOverlayList.emplace_back(this, priority);
49 : // Sort by overlays by priority. Do stable sort so that adding/removing
50 : // overlays doesn't randomly disturb all the existing ones (which would
51 : // be noticeable if they have the same priority and overlap).
52 0 : std::stable_sort(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(),
53 0 : [](const std::pair<ITerrainOverlay*, int>& a, const std::pair<ITerrainOverlay*, int>& b) {
54 0 : return a.second < b.second;
55 : });
56 0 : }
57 :
58 0 : ITerrainOverlay::~ITerrainOverlay()
59 : {
60 0 : std::vector<std::pair<ITerrainOverlay*, int> >::iterator newEnd =
61 : std::remove_if(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(),
62 0 : [this](const std::pair<ITerrainOverlay*, int>& a) { return a.first == this; });
63 0 : g_TerrainOverlayList.erase(newEnd, g_TerrainOverlayList.end());
64 0 : }
65 0 :
66 :
67 : void ITerrainOverlay::RenderOverlaysBeforeWater(
68 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext)
69 : {
70 : if (g_TerrainOverlayList.empty())
71 0 : return;
72 0 :
73 : PROFILE3_GPU("terrain overlays (before)");
74 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain overlays before water");
75 :
76 0 : for (size_t i = 0; i < g_TerrainOverlayList.size(); ++i)
77 0 : g_TerrainOverlayList[i].first->RenderBeforeWater(deviceCommandContext);
78 0 : }
79 :
80 : void ITerrainOverlay::RenderOverlaysAfterWater(
81 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, int cullGroup)
82 : {
83 : if (g_TerrainOverlayList.empty())
84 0 : return;
85 0 :
86 : PROFILE3_GPU("terrain overlays (after)");
87 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain overlays after water");
88 0 :
89 : for (size_t i = 0; i < g_TerrainOverlayList.size(); ++i)
90 0 : g_TerrainOverlayList[i].first->RenderAfterWater(deviceCommandContext, cullGroup);
91 0 : }
92 :
93 : //////////////////////////////////////////////////////////////////////////
94 0 :
95 : TerrainOverlay::TerrainOverlay(const CSimContext& simContext, int priority /* = 100 */)
96 : : ITerrainOverlay(priority), m_Terrain(&simContext.GetTerrain())
97 0 : {
98 0 : }
99 :
100 0 : void TerrainOverlay::StartRender()
101 0 : {
102 : }
103 0 :
104 0 : void TerrainOverlay::EndRender()
105 : {
106 : }
107 :
108 : void TerrainOverlay::GetTileExtents(
109 0 : ssize_t& min_i_inclusive, ssize_t& min_j_inclusive,
110 0 : ssize_t& max_i_inclusive, ssize_t& max_j_inclusive)
111 : {
112 0 : // Default to whole map
113 : min_i_inclusive = min_j_inclusive = 0;
114 0 : max_i_inclusive = max_j_inclusive = m_Terrain->GetTilesPerSide()-1;
115 : }
116 0 :
117 : void TerrainOverlay::RenderBeforeWater(
118 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext)
119 : {
120 0 : if (!m_Terrain)
121 : return; // should never happen, but let's play it safe
122 0 :
123 : #if CONFIG2_GLES
124 : UNUSED2(deviceCommandContext);
125 : #warning TODO: implement TerrainOverlay::RenderOverlays for GLES
126 : #else
127 0 : StartRender();
128 0 :
129 0 : ssize_t min_i, min_j, max_i, max_j;
130 : GetTileExtents(min_i, min_j, max_i, max_j);
131 0 : // Clamp the min to 0, but the max to -1 - so tile -1 can never be rendered,
132 : // but if unclamped_max<0 then no tiles at all will be rendered. And the same
133 : // for the upper limit.
134 0 : min_i = Clamp<ssize_t>(min_i, 0, m_Terrain->GetTilesPerSide());
135 0 : min_j = Clamp<ssize_t>(min_j, 0, m_Terrain->GetTilesPerSide());
136 : max_i = Clamp<ssize_t>(max_i, -1, m_Terrain->GetTilesPerSide()-1);
137 : max_j = Clamp<ssize_t>(max_j, -1, m_Terrain->GetTilesPerSide()-1);
138 :
139 : for (m_j = min_j; m_j <= max_j; ++m_j)
140 : for (m_i = min_i; m_i <= max_i; ++m_i)
141 0 : ProcessTile(deviceCommandContext, m_i, m_j);
142 :
143 0 : EndRender();
144 0 : #endif
145 : }
146 :
147 : void TerrainOverlay::RenderTile(
148 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
149 0 : const CColor& color, bool drawHidden)
150 0 : {
151 0 : RenderTile(deviceCommandContext, color, drawHidden, m_i, m_j);
152 : }
153 0 :
154 0 : void TerrainOverlay::RenderTile(
155 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
156 : const CColor& color, bool drawHidden, ssize_t i, ssize_t j)
157 0 : {
158 : // TODO: unnecessary computation calls has been removed but we should use
159 : // a vertex buffer or a vertex shader with a texture.
160 : // Not sure if it's possible on old OpenGL.
161 0 :
162 : #if CONFIG2_GLES
163 : UNUSED2(deviceCommandContext);
164 : UNUSED2(color);
165 0 : UNUSED2(drawHidden);
166 0 : UNUSED2(i);
167 : UNUSED2(j);
168 0 : #warning TODO: implement TerrainOverlay::RenderTile for GLES
169 : #else
170 :
171 : CVector3D pos[2][2];
172 : for (int di = 0; di < 2; ++di)
173 : for (int dj = 0; dj < 2; ++dj)
174 : m_Terrain->CalcPosition(i + di, j + dj, pos[di][dj]);
175 :
176 : std::vector<float> vertices;
177 : #define ADD(position) \
178 : vertices.emplace_back((position).X); \
179 : vertices.emplace_back((position).Y); \
180 : vertices.emplace_back((position).Z);
181 :
182 : if (m_Terrain->GetTriangulationDir(i, j))
183 : {
184 : ADD(pos[0][0]);
185 0 : ADD(pos[1][0]);
186 0 : ADD(pos[0][1]);
187 0 :
188 0 : ADD(pos[1][0]);
189 : ADD(pos[1][1]);
190 0 : ADD(pos[0][1]);
191 : }
192 : else
193 : {
194 : ADD(pos[0][0]);
195 : ADD(pos[1][0]);
196 0 : ADD(pos[1][1]);
197 :
198 0 : ADD(pos[1][1]);
199 0 : ADD(pos[0][1]);
200 0 : ADD(pos[0][0]);
201 : }
202 0 : #undef ADD
203 0 :
204 0 : CShaderTechniquePtr overlayTech =
205 : g_Renderer.GetShaderManager().LoadEffect(str_debug_line);
206 : Renderer::Backend::GraphicsPipelineStateDesc pipelineStateDesc =
207 : overlayTech->GetGraphicsPipelineStateDesc();
208 0 : pipelineStateDesc.depthStencilState.depthTestEnabled = !drawHidden;
209 0 : pipelineStateDesc.blendState.enabled = true;
210 0 : pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
211 : Renderer::Backend::BlendFactor::SRC_ALPHA;
212 0 : pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
213 0 : Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
214 0 : pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
215 : Renderer::Backend::BlendOp::ADD;
216 : pipelineStateDesc.rasterizationState.cullMode =
217 : drawHidden ? Renderer::Backend::CullMode::NONE : Renderer::Backend::CullMode::BACK;
218 0 : // To ensure that outlines are drawn on top of the terrain correctly (and
219 0 : // don't Z-fight and flicker nastily), use detph bias to pull them towards
220 0 : // the camera.
221 0 : pipelineStateDesc.rasterizationState.depthBiasEnabled = true;
222 0 : pipelineStateDesc.rasterizationState.depthBiasConstantFactor = -1.0f;
223 0 : pipelineStateDesc.rasterizationState.depthBiasSlopeFactor = -1.0f;
224 0 : overlayTech->BeginPass();
225 : deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
226 0 :
227 : CShaderProgramPtr overlayShader = overlayTech->GetShader();
228 0 :
229 : overlayShader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection());
230 0 : overlayShader->Uniform(str_color, color);
231 0 :
232 : overlayShader->VertexPointer(
233 : Renderer::Backend::Format::R32G32B32_SFLOAT, 0, vertices.data());
234 : overlayShader->AssertPointersBound();
235 0 :
236 0 : deviceCommandContext->Draw(0, vertices.size() / 3);
237 0 :
238 0 : overlayTech->EndPass();
239 0 : #endif
240 : }
241 0 :
242 : void TerrainOverlay::RenderTileOutline(
243 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
244 0 : const CColor& color, bool drawHidden)
245 : {
246 0 : RenderTileOutline(deviceCommandContext, color, drawHidden, m_i, m_j);
247 0 : }
248 0 :
249 : void TerrainOverlay::RenderTileOutline(
250 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
251 : const CColor& color, bool drawHidden, ssize_t i, ssize_t j)
252 0 : {
253 : #if CONFIG2_GLES
254 0 : UNUSED2(deviceCommandContext);
255 : UNUSED2(color);
256 0 : UNUSED2(drawHidden);
257 : UNUSED2(i);
258 : UNUSED2(j);
259 : #warning TODO: implement TerrainOverlay::RenderTileOutline for GLES
260 0 : #else
261 0 :
262 : std::vector<float> vertices;
263 0 : #define ADD(i, j) \
264 : m_Terrain->CalcPosition(i, j, position); \
265 : vertices.emplace_back(position.X); \
266 : vertices.emplace_back(position.Y); \
267 : vertices.emplace_back(position.Z);
268 :
269 : CVector3D position;
270 : ADD(i, j);
271 : ADD(i + 1, j);
272 : ADD(i + 1, j + 1);
273 : ADD(i, j);
274 : ADD(i + 1, j + 1);
275 : ADD(i, j + 1);
276 0 : #undef ADD
277 :
278 : CShaderTechniquePtr overlayTech =
279 : g_Renderer.GetShaderManager().LoadEffect(str_debug_line);
280 : Renderer::Backend::GraphicsPipelineStateDesc pipelineStateDesc =
281 : overlayTech->GetGraphicsPipelineStateDesc();
282 : pipelineStateDesc.depthStencilState.depthTestEnabled = !drawHidden;
283 0 : pipelineStateDesc.blendState.enabled = true;
284 0 : pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
285 0 : Renderer::Backend::BlendFactor::SRC_ALPHA;
286 0 : pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
287 0 : Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
288 0 : pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
289 0 : Renderer::Backend::BlendOp::ADD;
290 : pipelineStateDesc.rasterizationState.cullMode =
291 : drawHidden ? Renderer::Backend::CullMode::NONE : Renderer::Backend::CullMode::BACK;
292 0 : pipelineStateDesc.rasterizationState.polygonMode = Renderer::Backend::PolygonMode::LINE;
293 0 : overlayTech->BeginPass();
294 0 : deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
295 0 :
296 0 : const CShaderProgramPtr& overlayShader = overlayTech->GetShader();
297 0 :
298 0 : overlayShader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection());
299 : overlayShader->Uniform(str_color, color);
300 0 :
301 : overlayShader->VertexPointer(
302 0 : Renderer::Backend::Format::R32G32B32_SFLOAT, 0, vertices.data());
303 : overlayShader->AssertPointersBound();
304 0 :
305 0 : deviceCommandContext->Draw(0, vertices.size() / 3);
306 0 :
307 0 : overlayTech->EndPass();
308 0 : #endif
309 : }
310 0 :
311 : //////////////////////////////////////////////////////////////////////////
312 0 :
313 0 : TerrainTextureOverlay::TerrainTextureOverlay(float texelsPerTile, int priority) :
314 : ITerrainOverlay(priority), m_TexelsPerTile(texelsPerTile)
315 0 : {
316 0 : }
317 0 :
318 : TerrainTextureOverlay::~TerrainTextureOverlay() = default;
319 0 :
320 : void TerrainTextureOverlay::RenderAfterWater(
321 0 : Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, int cullGroup)
322 : {
323 0 : CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
324 :
325 : ssize_t w = (ssize_t)(terrain->GetTilesPerSide() * m_TexelsPerTile);
326 : ssize_t h = (ssize_t)(terrain->GetTilesPerSide() * m_TexelsPerTile);
327 0 :
328 0 : const uint32_t requiredWidth = round_up_to_pow2(w);
329 : const uint32_t requiredHeight = round_up_to_pow2(h);
330 0 :
331 : // Recreate the texture with new size if necessary
332 : if (!m_Texture || m_Texture->GetWidth() != requiredWidth || m_Texture->GetHeight() != requiredHeight)
333 : {
334 0 : m_Texture = deviceCommandContext->GetDevice()->CreateTexture2D("TerrainOverlayTexture",
335 : Renderer::Backend::Format::R8G8B8A8_UNORM, requiredWidth, requiredHeight,
336 : Renderer::Backend::Sampler::MakeDefaultSampler(
337 0 : Renderer::Backend::Sampler::Filter::NEAREST,
338 : Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
339 0 : }
340 0 :
341 : u8* data = (u8*)calloc(w * h, 4);
342 0 : BuildTextureRGBA(data, w, h);
343 0 :
344 : deviceCommandContext->UploadTextureRegion(
345 : m_Texture.get(), Renderer::Backend::Format::R8G8B8A8_UNORM, data, w * h * 4, 0, 0, w, h);
346 0 :
347 : free(data);
348 0 :
349 : CMatrix3D matrix;
350 0 : matrix.SetZero();
351 : matrix._11 = m_TexelsPerTile / (m_Texture->GetWidth() * TERRAIN_TILE_SIZE);
352 0 : matrix._23 = m_TexelsPerTile / (m_Texture->GetHeight() * TERRAIN_TILE_SIZE);
353 : matrix._44 = 1;
354 :
355 0 : g_Renderer.GetSceneRenderer().GetTerrainRenderer().RenderTerrainOverlayTexture(
356 0 : deviceCommandContext, cullGroup, matrix, m_Texture.get());
357 : }
358 0 :
359 0 : SColor4ub TerrainTextureOverlay::GetColor(size_t idx, u8 alpha) const
360 : {
361 0 : static u8 colors[][3] =
362 : {
363 0 : { 255, 0, 0 },
364 0 : { 0, 255, 0 },
365 0 : { 0, 0, 255 },
366 0 : { 255, 255, 0 },
367 0 : { 255, 0, 255 },
368 : { 0, 255, 255 },
369 0 : { 255, 255, 255 },
370 :
371 0 : { 127, 0, 0 },
372 : { 0, 127, 0 },
373 0 : { 0, 0, 127 },
374 : { 127, 127, 0 },
375 0 : { 127, 0, 127 },
376 : { 0, 127, 127 },
377 : { 127, 127, 127},
378 :
379 : { 255, 127, 0 },
380 : { 127, 255, 0 },
381 : { 255, 0, 127 },
382 : { 127, 0, 255},
383 : { 0, 255, 127 },
384 : { 0, 127, 255},
385 : { 255, 127, 127},
386 : { 127, 255, 127},
387 : { 127, 127, 255},
388 :
389 : { 127, 255, 255 },
390 : { 255, 127, 255 },
391 : { 255, 255, 127 },
392 : };
393 :
394 : size_t c = idx % ARRAY_SIZE(colors);
395 : return SColor4ub(colors[c][0], colors[c][1], colors[c][2], alpha);
396 : }
|