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"
25 #include "maths/MathUtil.h"
27 #include "ps/Profile.h"
28 #include "ps/Shapes.h"
30 #include "renderer/Renderer.h"
31 #include "renderer/TerrainRenderer.h"
32 #include "simulation2/system/SimContext.h"
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
;
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())
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())
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()
111 return; // should never happen, but let's play it safe
114 #warning TODO: implement TerrainOverlay::RenderOverlays for GLES
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
);
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
);
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
);
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)
172 glDisable(GL_DEPTH_TEST
);
173 glDisable(GL_CULL_FACE
);
177 glEnable(GL_DEPTH_TEST
);
178 glEnable(GL_CULL_FACE
);
182 #warning TODO: implement TerrainOverlay::RenderTile for GLES
185 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
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());
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());
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
)
224 glDisable(GL_DEPTH_TEST
);
225 glDisable(GL_CULL_FACE
);
229 glEnable(GL_DEPTH_TEST
);
230 glEnable(GL_CULL_FACE
);
234 #warning TODO: implement TerrainOverlay::RenderTileOutline for GLES
237 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
240 glLineWidth((float)line_width
);
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());
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
);
304 matrix
._11
= m_TexelsPerTile
/ (m_TextureW
* TERRAIN_TILE_SIZE
);
305 matrix
._23
= m_TexelsPerTile
/ (m_TextureH
* TERRAIN_TILE_SIZE
);
308 g_Renderer
.GetTerrainRenderer().RenderTerrainOverlayTexture(cullGroup
, matrix
);
311 SColor4ub
TerrainTextureOverlay::GetColor(size_t idx
, u8 alpha
) const
313 static u8 colors
[][3] = {
345 size_t c
= idx
% ARRAY_SIZE(colors
);
346 return SColor4ub(colors
[c
][0], colors
[c
][1], colors
[c
][2], alpha
);