Merge 'remotes/trunk'
[0ad.git] / source / renderer / TerrainOverlay.cpp
blobf25cdfe88461e7edf0db41b134fdb4d9458e5543
1 /* Copyright (C) 2015 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 "TerrainOverlay.h"
22 #include "graphics/Terrain.h"
23 #include "lib/bits.h"
24 #include "lib/ogl.h"
25 #include "maths/MathUtil.h"
26 #include "ps/Game.h"
27 #include "ps/Profile.h"
28 #include "ps/Shapes.h"
29 #include "ps/World.h"
30 #include "renderer/Renderer.h"
31 #include "renderer/TerrainRenderer.h"
32 #include "simulation2/system/SimContext.h"
34 #include <algorithm>
36 // Global overlay list management:
38 static std::vector<std::pair<ITerrainOverlay*, int> > g_TerrainOverlayList;
40 ITerrainOverlay::ITerrainOverlay(int priority)
42 // Add to global list of overlays
43 g_TerrainOverlayList.emplace_back(this, priority);
44 // Sort by overlays by priority. Do stable sort so that adding/removing
45 // overlays doesn't randomly disturb all the existing ones (which would
46 // be noticeable if they have the same priority and overlap).
47 std::stable_sort(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(),
48 [](const std::pair<ITerrainOverlay*, int>& a, const std::pair<ITerrainOverlay*, int>& b) {
49 return a.second < b.second;
50 });
53 ITerrainOverlay::~ITerrainOverlay()
55 std::vector<std::pair<ITerrainOverlay*, int> >::iterator newEnd =
56 std::remove_if(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(),
57 [this](const std::pair<ITerrainOverlay*, int>& a) { return a.first == this; });
58 g_TerrainOverlayList.erase(newEnd, g_TerrainOverlayList.end());
62 void ITerrainOverlay::RenderOverlaysBeforeWater()
64 if (g_TerrainOverlayList.empty())
65 return;
67 PROFILE3_GPU("terrain overlays (before)");
69 for (size_t i = 0; i < g_TerrainOverlayList.size(); ++i)
70 g_TerrainOverlayList[i].first->RenderBeforeWater();
73 void ITerrainOverlay::RenderOverlaysAfterWater(int cullGroup)
75 if (g_TerrainOverlayList.empty())
76 return;
78 PROFILE3_GPU("terrain overlays (after)");
80 for (size_t i = 0; i < g_TerrainOverlayList.size(); ++i)
81 g_TerrainOverlayList[i].first->RenderAfterWater(cullGroup);
84 //////////////////////////////////////////////////////////////////////////
86 TerrainOverlay::TerrainOverlay(const CSimContext& simContext, int priority /* = 100 */)
87 : ITerrainOverlay(priority), m_Terrain(&simContext.GetTerrain())
91 void TerrainOverlay::StartRender()
95 void TerrainOverlay::EndRender()
99 void TerrainOverlay::GetTileExtents(
100 ssize_t& min_i_inclusive, ssize_t& min_j_inclusive,
101 ssize_t& max_i_inclusive, ssize_t& max_j_inclusive)
103 // Default to whole map
104 min_i_inclusive = min_j_inclusive = 0;
105 max_i_inclusive = max_j_inclusive = m_Terrain->GetTilesPerSide()-1;
108 void TerrainOverlay::RenderBeforeWater()
110 if (!m_Terrain)
111 return; // should never happen, but let's play it safe
113 #if CONFIG2_GLES
114 #warning TODO: implement TerrainOverlay::RenderOverlays for GLES
115 #else
116 glEnable(GL_BLEND);
117 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
118 glDepthMask(GL_FALSE);
119 // To ensure that outlines are drawn on top of the terrain correctly (and
120 // don't Z-fight and flicker nastily), draw them as QUADS with the LINE
121 // PolygonMode, and use PolygonOffset to pull them towards the camera.
122 // (See e.g. http://www.opengl.org/resources/faq/technical/polygonoffset.htm)
123 glPolygonOffset(-1.f, -1.f);
124 //glEnable(GL_POLYGON_OFFSET_LINE);
125 glEnable(GL_POLYGON_OFFSET_FILL);
127 pglActiveTextureARB(GL_TEXTURE0);
128 glDisable(GL_TEXTURE_2D);
130 StartRender();
132 ssize_t min_i, min_j, max_i, max_j;
133 GetTileExtents(min_i, min_j, max_i, max_j);
134 // Clamp the min to 0, but the max to -1 - so tile -1 can never be rendered,
135 // but if unclamped_max<0 then no tiles at all will be rendered. And the same
136 // for the upper limit.
137 min_i = clamp(min_i, ssize_t(0), m_Terrain->GetTilesPerSide());
138 min_j = clamp(min_j, ssize_t(0), m_Terrain->GetTilesPerSide());
139 max_i = clamp(max_i, ssize_t(-1), m_Terrain->GetTilesPerSide()-1);
140 max_j = clamp(max_j, ssize_t(-1), m_Terrain->GetTilesPerSide()-1);
142 for (m_j = min_j; m_j <= max_j; ++m_j)
143 for (m_i = min_i; m_i <= max_i; ++m_i)
144 ProcessTile(m_i, m_j);
146 EndRender();
148 // Clean up state changes
149 glEnable(GL_CULL_FACE);
150 glEnable(GL_DEPTH_TEST);
151 //glDisable(GL_POLYGON_OFFSET_LINE);
152 glDisable(GL_POLYGON_OFFSET_FILL);
153 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
154 glDepthMask(GL_TRUE);
155 glDisable(GL_BLEND);
156 #endif
159 void TerrainOverlay::RenderTile(const CColor& color, bool draw_hidden)
161 RenderTile(color, draw_hidden, m_i, m_j);
164 void TerrainOverlay::RenderTile(const CColor& color, bool draw_hidden, ssize_t i, ssize_t j)
166 // TODO: if this is unpleasantly slow, make it much more efficient
167 // (e.g. buffering data and making a single draw call? or at least
168 // far fewer calls than it makes now)
170 if (draw_hidden)
172 glDisable(GL_DEPTH_TEST);
173 glDisable(GL_CULL_FACE);
175 else
177 glEnable(GL_DEPTH_TEST);
178 glEnable(GL_CULL_FACE);
181 #if CONFIG2_GLES
182 #warning TODO: implement TerrainOverlay::RenderTile for GLES
183 #else
185 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
187 CVector3D pos;
188 glBegin(GL_TRIANGLES);
189 glColor4fv(color.FloatArray());
190 if (m_Terrain->GetTriangulationDir(i, j))
192 m_Terrain->CalcPosition(i, j, pos); glVertex3fv(pos.GetFloatArray());
193 m_Terrain->CalcPosition(i+1, j, pos); glVertex3fv(pos.GetFloatArray());
194 m_Terrain->CalcPosition(i, j+1, pos); glVertex3fv(pos.GetFloatArray());
196 m_Terrain->CalcPosition(i+1, j, pos); glVertex3fv(pos.GetFloatArray());
197 m_Terrain->CalcPosition(i+1, j+1, pos); glVertex3fv(pos.GetFloatArray());
198 m_Terrain->CalcPosition(i, j+1, pos); glVertex3fv(pos.GetFloatArray());
200 else
202 m_Terrain->CalcPosition(i, j, pos); glVertex3fv(pos.GetFloatArray());
203 m_Terrain->CalcPosition(i+1, j, pos); glVertex3fv(pos.GetFloatArray());
204 m_Terrain->CalcPosition(i+1, j+1, pos); glVertex3fv(pos.GetFloatArray());
206 m_Terrain->CalcPosition(i+1, j+1, pos); glVertex3fv(pos.GetFloatArray());
207 m_Terrain->CalcPosition(i, j+1, pos); glVertex3fv(pos.GetFloatArray());
208 m_Terrain->CalcPosition(i, j, pos); glVertex3fv(pos.GetFloatArray());
210 glEnd();
212 #endif
215 void TerrainOverlay::RenderTileOutline(const CColor& color, int line_width, bool draw_hidden)
217 RenderTileOutline(color, line_width, draw_hidden, m_i, m_j);
220 void TerrainOverlay::RenderTileOutline(const CColor& color, int line_width, bool draw_hidden, ssize_t i, ssize_t j)
222 if (draw_hidden)
224 glDisable(GL_DEPTH_TEST);
225 glDisable(GL_CULL_FACE);
227 else
229 glEnable(GL_DEPTH_TEST);
230 glEnable(GL_CULL_FACE);
233 #if CONFIG2_GLES
234 #warning TODO: implement TerrainOverlay::RenderTileOutline for GLES
235 #else
237 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
239 if (line_width != 1)
240 glLineWidth((float)line_width);
242 CVector3D pos;
243 glBegin(GL_QUADS);
244 glColor4fv(color.FloatArray());
245 m_Terrain->CalcPosition(i, j, pos); glVertex3fv(pos.GetFloatArray());
246 m_Terrain->CalcPosition(i+1, j, pos); glVertex3fv(pos.GetFloatArray());
247 m_Terrain->CalcPosition(i+1, j+1, pos); glVertex3fv(pos.GetFloatArray());
248 m_Terrain->CalcPosition(i, j+1, pos); glVertex3fv(pos.GetFloatArray());
249 glEnd();
251 if (line_width != 1)
252 glLineWidth(1.0f);
254 #endif
257 //////////////////////////////////////////////////////////////////////////
259 TerrainTextureOverlay::TerrainTextureOverlay(float texelsPerTile, int priority) :
260 ITerrainOverlay(priority), m_TexelsPerTile(texelsPerTile), m_Texture(0), m_TextureW(0), m_TextureH(0)
262 glGenTextures(1, &m_Texture);
265 TerrainTextureOverlay::~TerrainTextureOverlay()
267 glDeleteTextures(1, &m_Texture);
270 void TerrainTextureOverlay::RenderAfterWater(int cullGroup)
272 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
274 ssize_t w = (ssize_t)(terrain->GetTilesPerSide() * m_TexelsPerTile);
275 ssize_t h = (ssize_t)(terrain->GetTilesPerSide() * m_TexelsPerTile);
277 pglActiveTextureARB(GL_TEXTURE0);
279 // Recreate the texture with new size if necessary
280 if (round_up_to_pow2(w) != m_TextureW || round_up_to_pow2(h) != m_TextureH)
282 m_TextureW = round_up_to_pow2(w);
283 m_TextureH = round_up_to_pow2(h);
285 glBindTexture(GL_TEXTURE_2D, m_Texture);
286 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureW, m_TextureH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
288 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
289 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
290 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
291 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
294 u8* data = (u8*)calloc(w * h, 4);
295 BuildTextureRGBA(data, w, h);
297 glBindTexture(GL_TEXTURE_2D, m_Texture);
298 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
300 free(data);
302 CMatrix3D matrix;
303 matrix.SetZero();
304 matrix._11 = m_TexelsPerTile / (m_TextureW * TERRAIN_TILE_SIZE);
305 matrix._23 = m_TexelsPerTile / (m_TextureH * TERRAIN_TILE_SIZE);
306 matrix._44 = 1;
308 g_Renderer.GetTerrainRenderer().RenderTerrainOverlayTexture(cullGroup, matrix);
311 SColor4ub TerrainTextureOverlay::GetColor(size_t idx, u8 alpha) const
313 static u8 colors[][3] = {
314 { 255, 0, 0 },
315 { 0, 255, 0 },
316 { 0, 0, 255 },
317 { 255, 255, 0 },
318 { 255, 0, 255 },
319 { 0, 255, 255 },
320 { 255, 255, 255 },
322 { 127, 0, 0 },
323 { 0, 127, 0 },
324 { 0, 0, 127 },
325 { 127, 127, 0 },
326 { 127, 0, 127 },
327 { 0, 127, 127 },
328 { 127, 127, 127},
330 { 255, 127, 0 },
331 { 127, 255, 0 },
332 { 255, 0, 127 },
333 { 127, 0, 255},
334 { 0, 255, 127 },
335 { 0, 127, 255},
336 { 255, 127, 127},
337 { 127, 255, 127},
338 { 127, 127, 255},
340 { 127, 255, 255 },
341 { 255, 127, 255 },
342 { 255, 255, 127 },
345 size_t c = idx % ARRAY_SIZE(colors);
346 return SColor4ub(colors[c][0], colors[c][1], colors[c][2], alpha);