1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 /**
22  * @file terrain.c
23  * Draws the terrain.
24  * It uses the ground property of every MAPTILE to divide the terrain into different layers.
25  * For every layer the GROUND_TYPE (from psGroundTypes) determines the texture and size.
26  * The technique used it called "texture splatting".
27  * Every layer only draws the spots where that terrain is, and additive blending is used to make transitions smooth.
28  * Decals are a kind of hack now, as for some tiles (where decal == true) the old tile is just drawn.
29  * The water is drawn using the hardcoded page-80 and page-81 textures.
30  */
31 
32 #include <string.h>
33 
34 #include "lib/framework/frame.h"
35 #include "lib/framework/opengl.h"
36 #include "lib/ivis_opengl/ivisdef.h"
37 #include "lib/ivis_opengl/imd.h"
38 #include "lib/ivis_opengl/piefunc.h"
39 #include "lib/ivis_opengl/tex.h"
40 #include "lib/ivis_opengl/pietypes.h"
41 #include "lib/ivis_opengl/pieclip.h"
42 #include "lib/ivis_opengl/piestate.h"
43 #include "lib/ivis_opengl/screen.h"
44 #include "lib/ivis_opengl/piematrix.h"
45 #include <glm/mat4x4.hpp>
46 #ifndef GLM_ENABLE_EXPERIMENTAL
47 	#define GLM_ENABLE_EXPERIMENTAL
48 #endif
49 #include <glm/gtx/transform.hpp>
50 
51 #include "terrain.h"
52 #include "map.h"
53 #include "texture.h"
54 #include "display3d.h"
55 #include "hci.h"
56 #include "loop.h"
57 
58 /**
59  * A sector contains all information to draw a square piece of the map.
60  * The actual geometry and texture data is not stored in here but in large VBO's.
61  * The sector only stores the index and length of the pieces it's going to use.
62  */
63 struct Sector
64 {
65 	int geometryOffset;      ///< The point in the geometry VBO where our geometry starts
66 	int geometrySize;        ///< The size of our geometry
67 	int geometryIndexOffset; ///< The point in the index VBO where our triangles start
68 	int geometryIndexSize;   ///< The size of our indices
69 	int waterOffset;         ///< The point in the water VBO where the water geometry starts
70 	int waterSize;           ///< The size of the water geometry
71 	int waterIndexOffset;    ///< The point in the water index VBO where the water triangles start
72 	int waterIndexSize;      ///< The size of our water triangles
73 	int *textureOffset;      ///< An array containing the offsets into the texture VBO for each terrain layer
74 	int *textureSize;        ///< The size of the geometry for this layer for each layer
75 	int *textureIndexOffset; ///< The offset into the index VBO for the texture for each layer
76 	int *textureIndexSize;   ///< The size of the indices for each layer
77 	int decalOffset;         ///< Index into the decal VBO
78 	int decalSize;           ///< Size of the part of the decal VBO we are going to use
79 	bool draw;               ///< Do we draw this sector this frame?
80 	bool dirty;              ///< Do we need to update the geometry for this sector?
81 };
82 
83 using RenderVertex = Vector3f;
84 
85 /// A vertex with a position and texture coordinates
86 struct DecalVertex
87 {
88 	Vector3f pos = Vector3f(0.f, 0.f, 0.f);
89 	Vector2f uv = Vector2f(0.f, 0.f);
90 };
91 
92 /// The lightmap texture
93 static gfx_api::texture* lightmap_tex_num = nullptr;
94 /// When are we going to update the lightmap next?
95 static unsigned int lightmapLastUpdate;
96 /// How big is the lightmap?
97 static size_t lightmapWidth;
98 static size_t lightmapHeight;
99 /// Lightmap image
100 static gfx_api::gfxUByte *lightmapPixmap;
101 /// Ticks per lightmap refresh
102 static const unsigned int LIGHTMAP_REFRESH = 80;
103 
104 /// VBOs
105 static gfx_api::buffer *geometryVBO = nullptr, *geometryIndexVBO = nullptr, *textureVBO = nullptr, *textureIndexVBO = nullptr, *decalVBO = nullptr;
106 /// VBOs
107 static gfx_api::buffer *waterVBO = nullptr, *waterIndexVBO = nullptr;
108 /// The amount we shift the water textures so the waves appear to be moving
109 static float waterOffset;
110 
111 /// These are properties of your videocard and hardware
112 static int32_t GLmaxElementsVertices, GLmaxElementsIndices;
113 
114 /// The sectors are stored here
115 static Sector *sectors;
116 /// The default sector size (a sector is sectorSize x sectorSize)
117 static int sectorSize = 15;
118 /// What is the distance we can see
119 static int terrainDistance;
120 /// How many sectors have we actually got?
121 static int xSectors, ySectors;
122 
123 /// Did we initialise the terrain renderer yet?
124 static bool terrainInitialised = false;
125 
126 /// Helper to specify the offset in a VBO
127 #define BUFFER_OFFSET(i) (reinterpret_cast<char *>(i))
128 
129 /// Helper variables for the DrawRangeElements functions
130 GLuint dreStart, dreEnd, dreOffset;
131 GLsizei dreCount;
132 /// Are we actually drawing something using the DrawRangeElements functions?
133 bool drawRangeElementsStarted = false;
134 
135 #define MIN_TERRAIN_TEXTURE_SIZE 512
136 
137 /// Pass all remaining triangles to OpenGL
138 template<typename PSO>
finishDrawRangeElements()139 static void finishDrawRangeElements()
140 {
141 	if (drawRangeElementsStarted && dreCount > 0)
142 	{
143 		ASSERT(dreEnd - dreStart + 1 <= GLmaxElementsVertices, "too many vertices (%i)", (int)(dreEnd - dreStart + 1));
144 		ASSERT(dreCount <= GLmaxElementsIndices, "too many indices (%i)", (int)dreCount);
145 		PSO::get().draw_elements(dreCount, sizeof(GLuint)*dreOffset);
146 	}
147 	drawRangeElementsStarted = false;
148 }
149 
150 /**
151  * Either draw the elements or batch them to be sent to OpenGL later
152  * This improves performance by reducing the amount of OpenGL calls.
153  */
154 template<typename PSO>
addDrawRangeElements(GLuint start,GLuint end,GLsizei count,GLuint offset)155 static void addDrawRangeElements(GLuint start,
156                                  GLuint end,
157                                  GLsizei count,
158                                  GLuint offset)
159 {
160 
161 	if (end - start + 1 > GLmaxElementsVertices)
162 	{
163 		debug(LOG_WARNING, "A single call provided too much vertices, will operate at reduced performance or crash. Decrease the sector size to fix this.");
164 	}
165 	if (count > GLmaxElementsIndices)
166 	{
167 		debug(LOG_WARNING, "A single call provided too much indices, will operate at reduced performance or crash. Decrease the sector size to fix this.");
168 	}
169 
170 	if (!drawRangeElementsStarted)
171 	{
172 		dreStart  = start;
173 		dreEnd    = end;
174 		dreCount  = count;
175 		dreOffset = offset;
176 		drawRangeElementsStarted = true;
177 		return;
178 	}
179 
180 	// check if we can append theoretically and
181 	// check if this will not go over the bounds advised by the opengl implementation
182 	if (dreOffset + dreCount != offset ||
183 	    dreCount + count > GLmaxElementsIndices ||
184 	    end - dreStart + 1 > GLmaxElementsVertices)
185 	{
186 		finishDrawRangeElements<PSO>();
187 		// start anew
188 		addDrawRangeElements<PSO>(start, end, count, offset);
189 	}
190 	else
191 	{
192 		// OK to append
193 		dreCount += count;
194 		dreEnd = end;
195 	}
196 	// make sure we did everything right
197 	ASSERT(dreEnd - dreStart + 1 <= GLmaxElementsVertices, "too many vertices (%i)", (int)(dreEnd - dreStart + 1));
198 	ASSERT(dreCount <= GLmaxElementsIndices, "too many indices (%i)", (int)(dreCount));
199 }
200 
201 /// Get the colour of the terrain tile at the specified position
getTileColour(int x,int y)202 PIELIGHT getTileColour(int x, int y)
203 {
204 	return mapTile(x, y)->colour;
205 }
206 
207 /// Set the colour of the tile at the specified position
setTileColour(int x,int y,PIELIGHT colour)208 void setTileColour(int x, int y, PIELIGHT colour)
209 {
210 	MAPTILE *psTile = mapTile(x, y);
211 
212 	psTile->colour = colour;
213 }
214 
215 // NOTE:  The current (max) texture size of a tile is 128x128.  We allow up to a user defined texture size
216 // of 2048.  This will cause ugly seams for the decals, if user picks a texture size bigger than the tile!
217 #define MAX_TILE_TEXTURE_SIZE 128.0f
218 /// Set up the texture coordinates for a tile
getTileTexCoords(Vector2f * uv,unsigned int tileNumber)219 static Vector2f getTileTexCoords(Vector2f *uv, unsigned int tileNumber)
220 {
221 	/* unmask proper values from compressed data */
222 	const unsigned short texture = TileNumber_texture(tileNumber);
223 	const unsigned short tile = TileNumber_tile(tileNumber);
224 
225 	/* Used to calculate texture coordinates */
226 	const float xMult = 1.0f / TILES_IN_PAGE_COLUMN;
227 	const float yMult = 1.0f / TILES_IN_PAGE_ROW;
228 	float texsize = (float)getTextureSize();
229 
230 	// the decals are 128x128 (at this time), we should not go above this value.  See note above
231 	if (texsize > MAX_TILE_TEXTURE_SIZE)
232 	{
233 		texsize = MAX_TILE_TEXTURE_SIZE;
234 	}
235 	const float centertile = 0.5f / texsize;	// compute center of tile
236 	const float shiftamount = (texsize - 1.0) / texsize;	// 1 pixel border
237 	// bump the texture coords, for 1 pixel border, so our range is [.5,(texsize - .5)]
238 	const float one = 1.0f / (TILES_IN_PAGE_COLUMN * texsize) + centertile * shiftamount;
239 
240 	/*
241 	 * Points for flipping the texture around if the tile is flipped or rotated
242 	 * Store the source rect as four points
243 	 */
244 	Vector2f sP1 { one, one };
245 	Vector2f sP2 { xMult - one, one };
246 	Vector2f sP3 { xMult - one, yMult - one };
247 	Vector2f sP4 { one, yMult - one };
248 
249 	if (texture & TILE_XFLIP)
250 	{
251 		std::swap(sP1, sP2);
252 		std::swap(sP3, sP4);
253 	}
254 	if (texture & TILE_YFLIP)
255 	{
256 		std::swap(sP1, sP4);
257 		std::swap(sP2, sP3);
258 	}
259 
260 	Vector2f sPTemp;
261 	switch ((texture & TILE_ROTMASK) >> TILE_ROTSHIFT)
262 	{
263 	case 1:
264 		sPTemp = sP1;
265 		sP1 = sP4;
266 		sP4 = sP3;
267 		sP3 = sP2;
268 		sP2 = sPTemp;
269 		break;
270 	case 2:
271 		sPTemp = sP1;
272 		sP1 = sP3;
273 		sP3 = sPTemp;
274 		sPTemp = sP4;
275 		sP4 = sP2;
276 		sP2 = sPTemp;
277 		break;
278 	case 3:
279 		sPTemp = sP1;
280 		sP1 = sP2;
281 		sP2 = sP3;
282 		sP3 = sP4;
283 		sP4 = sPTemp;
284 		break;
285 	}
286 	const Vector2f offset { tileTexInfo[tile].uOffset, tileTexInfo[tile].vOffset };
287 
288 	uv[0 + 0] = offset + sP1;
289 	uv[0 + 2] = offset + sP2;
290 	uv[1 + 2] = offset + sP3;
291 	uv[1 + 0] = offset + sP4;
292 
293 	/// Calculate the average texture coordinates of 4 points
294 	return Vector2f { (uv[0].x + uv[1].x + uv[2].x + uv[3].x) / 4, (uv[0].y + uv[1].y + uv[2].y + uv[3].y) / 4 };
295 }
296 
297 /// Average the four positions to get the center
averagePos(Vector3i * center,Vector3i * a,Vector3i * b,Vector3i * c,Vector3i * d)298 static void averagePos(Vector3i *center, Vector3i *a, Vector3i *b, Vector3i *c, Vector3i *d)
299 {
300 	center->x = (a->x + b->x + c->x + d->x) / 4;
301 	center->y = (a->y + b->y + c->y + d->y) / 4;
302 	center->z = (a->z + b->z + c->z + d->z) / 4;
303 }
304 
305 /// Is this position next to a water tile?
isWater(int x,int y)306 static bool isWater(int x, int y)
307 {
308 	bool result = false;
309 	result = result || (tileOnMap(x  , y) && terrainType(mapTile(x  , y)) == TER_WATER);
310 	result = result || (tileOnMap(x - 1, y) && terrainType(mapTile(x - 1, y)) == TER_WATER);
311 	result = result || (tileOnMap(x  , y - 1) && terrainType(mapTile(x  , y - 1)) == TER_WATER);
312 	result = result || (tileOnMap(x - 1, y - 1) && terrainType(mapTile(x - 1, y - 1)) == TER_WATER);
313 	return result;
314 }
315 
316 /// Get the position of a grid point
getGridPos(Vector3i * result,int x,int y,bool center,bool water)317 static void getGridPos(Vector3i *result, int x, int y, bool center, bool water)
318 {
319 	if (center)
320 	{
321 		Vector3i a, b, c, d;
322 		getGridPos(&a, x  , y  , false, water);
323 		getGridPos(&b, x + 1, y  , false, water);
324 		getGridPos(&c, x  , y + 1, false, water);
325 		getGridPos(&d, x + 1, y + 1, false, water);
326 		averagePos(result, &a, &b, &c, &d);
327 		return;
328 	}
329 	result->x = world_coord(x);
330 	result->z = world_coord(-y);
331 
332 	if (x <= 0 || y <= 0 || x >= mapWidth || y >= mapHeight)
333 	{
334 		result->y = 0;
335 	}
336 	else
337 	{
338 		result->y = map_TileHeight(x, y);
339 		if (water)
340 		{
341 			result->y = map_WaterHeight(x, y);
342 		}
343 	}
344 }
345 
346 /// Calculate the average colour of 4 points
averageColour(PIELIGHT * average,PIELIGHT a,PIELIGHT b,PIELIGHT c,PIELIGHT d)347 static inline void averageColour(PIELIGHT *average, PIELIGHT a, PIELIGHT b,
348                                  PIELIGHT c, PIELIGHT d)
349 {
350 	average->byte.a = (a.byte.a + b.byte.a + c.byte.a + d.byte.a) / 4;
351 	average->byte.r = (a.byte.r + b.byte.r + c.byte.r + d.byte.r) / 4;
352 	average->byte.g = (a.byte.g + b.byte.g + c.byte.g + d.byte.g) / 4;
353 	average->byte.b = (a.byte.b + b.byte.b + c.byte.b + d.byte.b) / 4;
354 }
355 
356 /**
357  * Set the terrain and water geometry for the specified sector
358  */
setSectorGeometry(int x,int y,RenderVertex * geometry,RenderVertex * water,int * geometrySize,int * waterSize)359 static void setSectorGeometry(int x, int y,
360                               RenderVertex *geometry, RenderVertex *water,
361                               int *geometrySize, int *waterSize)
362 {
363 	Vector3i pos;
364 	int i, j;
365 	for (i = 0; i < sectorSize + 1; i++)
366 	{
367 		for (j = 0; j < sectorSize + 1; j++)
368 		{
369 			// set up geometry
370 			getGridPos(&pos, i + x * sectorSize, j + y * sectorSize, false, false);
371 			geometry[*geometrySize].x = pos.x;
372 			geometry[*geometrySize].y = pos.y;
373 			geometry[*geometrySize].z = pos.z;
374 			(*geometrySize)++;
375 
376 			getGridPos(&pos, i + x * sectorSize, j + y * sectorSize, true, false);
377 			geometry[*geometrySize].x = pos.x;
378 			geometry[*geometrySize].y = pos.y;
379 			geometry[*geometrySize].z = pos.z;
380 			(*geometrySize)++;
381 
382 			getGridPos(&pos, i + x * sectorSize, j + y * sectorSize, false, true);
383 			water[*waterSize].x = pos.x;
384 			water[*waterSize].y = pos.y;
385 			water[*waterSize].z = pos.z;
386 			(*waterSize)++;
387 
388 			getGridPos(&pos, i + x * sectorSize, j + y * sectorSize, true, true);
389 			water[*waterSize].x = pos.x;
390 			water[*waterSize].y = pos.y;
391 			water[*waterSize].z = pos.z;
392 			(*waterSize)++;
393 		}
394 	}
395 }
396 
397 /**
398  * Set the decals for a sector. This takes care of both the geometry and the texture part.
399  */
setSectorDecals(int x,int y,DecalVertex * decaldata,int * decalSize)400 static void setSectorDecals(int x, int y, DecalVertex *decaldata, int *decalSize)
401 {
402 	Vector3i pos;
403 	Vector2f uv[2][2], center;
404 	int a, b;
405 	int i, j;
406 
407 	for (i = x * sectorSize; i < x * sectorSize + sectorSize; i++)
408 	{
409 		for (j = y * sectorSize; j < y * sectorSize + sectorSize; j++)
410 		{
411 			if (i < 0 || j < 0 || i >= mapWidth || j >= mapHeight)
412 			{
413 				continue;
414 			}
415 			if (TILE_HAS_DECAL(mapTile(i, j)))
416 			{
417 				center = getTileTexCoords(*uv, mapTile(i, j)->texture);
418 
419 				getGridPos(&pos, i, j, true, false);
420 				decaldata[*decalSize].pos = pos;
421 				decaldata[*decalSize].uv = center;
422 				(*decalSize)++;
423 				a = 0; b = 1;
424 				getGridPos(&pos, i + a, j + b, false, false);
425 				decaldata[*decalSize].pos = pos;
426 				decaldata[*decalSize].uv = uv[a][b];
427 				(*decalSize)++;
428 				a = 0; b = 0;
429 				getGridPos(&pos, i + a, j + b, false, false);
430 				decaldata[*decalSize].pos = pos;
431 				decaldata[*decalSize].uv = uv[a][b];
432 				(*decalSize)++;
433 
434 				getGridPos(&pos, i, j, true, false);
435 				decaldata[*decalSize].pos = pos;
436 				decaldata[*decalSize].uv = center;
437 				(*decalSize)++;
438 				a = 1; b = 1;
439 				getGridPos(&pos, i + a, j + b, false, false);
440 				decaldata[*decalSize].pos = pos;
441 				decaldata[*decalSize].uv = uv[a][b];
442 				(*decalSize)++;
443 				a = 0; b = 1;
444 				getGridPos(&pos, i + a, j + b, false, false);
445 				decaldata[*decalSize].pos = pos;
446 				decaldata[*decalSize].uv = uv[a][b];
447 				(*decalSize)++;
448 
449 				getGridPos(&pos, i, j, true, false);
450 				decaldata[*decalSize].pos = pos;
451 				decaldata[*decalSize].uv = center;
452 				(*decalSize)++;
453 				a = 1; b = 0;
454 				getGridPos(&pos, i + a, j + b, false, false);
455 				decaldata[*decalSize].pos = pos;
456 				decaldata[*decalSize].uv = uv[a][b];
457 				(*decalSize)++;
458 				a = 1; b = 1;
459 				getGridPos(&pos, i + a, j + b, false, false);
460 				decaldata[*decalSize].pos = pos;
461 				decaldata[*decalSize].uv = uv[a][b];
462 				(*decalSize)++;
463 
464 				getGridPos(&pos, i, j, true, false);
465 				decaldata[*decalSize].pos = pos;
466 				decaldata[*decalSize].uv = center;
467 				(*decalSize)++;
468 				a = 0; b = 0;
469 				getGridPos(&pos, i + a, j + b, false, false);
470 				decaldata[*decalSize].pos = pos;
471 				decaldata[*decalSize].uv = uv[a][b];
472 				(*decalSize)++;
473 				a = 1; b = 0;
474 				getGridPos(&pos, i + a, j + b, false, false);
475 				decaldata[*decalSize].pos = pos;
476 				decaldata[*decalSize].uv = uv[a][b];
477 				(*decalSize)++;
478 			}
479 		}
480 	}
481 }
482 
483 /**
484  * Update the sector for when the terrain is changed.
485  */
updateSectorGeometry(int x,int y)486 static void updateSectorGeometry(int x, int y)
487 {
488 	RenderVertex *geometry;
489 	RenderVertex *water;
490 	DecalVertex *decaldata;
491 	int geometrySize = 0;
492 	int waterSize = 0;
493 	int decalSize = 0;
494 
495 	geometry  = (RenderVertex *)malloc(sizeof(RenderVertex) * sectors[x * ySectors + y].geometrySize);
496 	water     = (RenderVertex *)malloc(sizeof(RenderVertex) * sectors[x * ySectors + y].waterSize);
497 
498 	setSectorGeometry(x, y, geometry, water, &geometrySize, &waterSize);
499 	ASSERT(geometrySize == sectors[x * ySectors + y].geometrySize, "something went seriously wrong updating the terrain");
500 	ASSERT(waterSize    == sectors[x * ySectors + y].waterSize   , "something went seriously wrong updating the terrain");
501 
502 	geometryVBO->update(sizeof(RenderVertex)*sectors[x * ySectors + y].geometryOffset,
503 	                    sizeof(RenderVertex)*sectors[x * ySectors + y].geometrySize, geometry,
504 						gfx_api::buffer::update_flag::non_overlapping_updates_promise);
505 	waterVBO->update(sizeof(RenderVertex)*sectors[x * ySectors + y].waterOffset,
506 	                 sizeof(RenderVertex)*sectors[x * ySectors + y].waterSize, water,
507 					 gfx_api::buffer::update_flag::non_overlapping_updates_promise);
508 
509 	free(geometry);
510 	free(water);
511 
512 	if (sectors[x * ySectors + y].decalSize <= 0)
513 	{
514 		// Nothing to do here, and glBufferSubData(GL_ARRAY_BUFFER, 0, 0, *) crashes in my graphics driver. Probably shouldn't crash...
515 		return;
516 	}
517 
518 	decaldata = (DecalVertex *)malloc(sizeof(DecalVertex) * sectors[x * ySectors + y].decalSize);
519 	setSectorDecals(x, y, decaldata, &decalSize);
520 	ASSERT(decalSize == sectors[x * ySectors + y].decalSize   , "the amount of decals has changed");
521 
522 	decalVBO->update(sizeof(DecalVertex)*sectors[x * ySectors + y].decalOffset,
523 	                 sizeof(DecalVertex)*sectors[x * ySectors + y].decalSize, decaldata,
524 					 gfx_api::buffer::update_flag::non_overlapping_updates_promise);
525 
526 	free(decaldata);
527 }
528 
529 /**
530  * Mark all tiles that are influenced by this grid point as dirty.
531  * Dirty sectors will later get updated by updateSectorGeometry.
532  */
markTileDirty(int i,int j)533 void markTileDirty(int i, int j)
534 {
535 	int x, y;
536 
537 	if (!terrainInitialised)
538 	{
539 		return; // will be updated anyway
540 	}
541 
542 	x = i / sectorSize;
543 	y = j / sectorSize;
544 	if (x < xSectors && y < ySectors) // could be on the lower or left edge of the map
545 	{
546 		sectors[x * ySectors + y].dirty = true;
547 	}
548 
549 	// it could be on an edge, so update for all sectors it is in
550 	if (x * sectorSize == i && x > 0)
551 	{
552 		if (x - 1 < xSectors && y < ySectors)
553 		{
554 			sectors[(x - 1)*ySectors + y].dirty = true;
555 		}
556 	}
557 	if (y * sectorSize == j && y > 0)
558 	{
559 		if (x < xSectors && y - 1 < ySectors)
560 		{
561 			sectors[x * ySectors + (y - 1)].dirty = true;
562 		}
563 	}
564 	if (x * sectorSize == i && x > 0 && y * sectorSize == j && y > 0)
565 	{
566 		if (x - 1 < xSectors && y - 1 < ySectors)
567 		{
568 			sectors[(x - 1)*ySectors + (y - 1)].dirty = true;
569 		}
570 	}
571 }
572 
loadTerrainTextures()573 void loadTerrainTextures()
574 {
575 	ASSERT_OR_RETURN(, psGroundTypes, "Ground type was not set, no textures will be seen.");
576 
577 	int32_t maxGfxTextureSize = gfx_api::context::get().get_context_value(gfx_api::context::context_value::MAX_TEXTURE_SIZE);
578 	int maxTerrainTextureSize = std::max(std::min({getTextureSize(), maxGfxTextureSize}), MIN_TERRAIN_TEXTURE_SIZE);
579 
580 	// for each terrain layer
581 	for (int layer = 0; layer < numGroundTypes; layer++)
582 	{
583 		// pre-load the texture
584 		optional<size_t> texPage = iV_GetTexture(psGroundTypes[layer].textureName, true, maxTerrainTextureSize, maxTerrainTextureSize);
585 		ASSERT(texPage.has_value(), "Failed to pre-load terrain texture: %s", psGroundTypes[layer].textureName);
586 	}
587 }
588 
589 /**
590  * Check what the videocard + drivers support and divide the loaded map into sectors that can be drawn.
591  * It also determines the lightmap size.
592  */
initTerrain()593 bool initTerrain()
594 {
595 	int i, j, x, y, a, b, absX, absY;
596 	PIELIGHT colour[2][2], centerColour;
597 	int layer = 0;
598 
599 	RenderVertex *geometry;
600 	RenderVertex *water;
601 	DecalVertex *decaldata;
602 	int geometrySize, geometryIndexSize;
603 	int waterSize, waterIndexSize;
604 	int textureSize, textureIndexSize;
605 	GLuint *geometryIndex;
606 	GLuint *waterIndex;
607 	GLuint *textureIndex;
608 	PIELIGHT *texture;
609 	int decalSize;
610 	int maxSectorSizeIndices, maxSectorSizeVertices;
611 	bool decreasedSize = false;
612 
613 	// this information is useful to prevent crashes with buggy opengl implementations
614 	GLmaxElementsVertices = gfx_api::context::get().get_context_value(gfx_api::context::context_value::MAX_ELEMENTS_VERTICES);
615 	GLmaxElementsIndices = gfx_api::context::get().get_context_value(gfx_api::context::context_value::MAX_ELEMENTS_INDICES);
616 
617 	// testing for crappy cards
618 	debug(LOG_TERRAIN, "GL_MAX_ELEMENTS_VERTICES: %i", (int)GLmaxElementsVertices);
619 	debug(LOG_TERRAIN, "GL_MAX_ELEMENTS_INDICES:  %i", (int)GLmaxElementsIndices);
620 
621 	// now we know these values, determine the maximum sector size achievable
622 	maxSectorSizeVertices = iSqrt(GLmaxElementsVertices / 2) - 1;
623 	maxSectorSizeIndices = iSqrt(GLmaxElementsIndices / 12);
624 
625 	debug(LOG_TERRAIN, "preferred sector size: %i", sectorSize);
626 	debug(LOG_TERRAIN, "maximum sector size due to vertices: %i", maxSectorSizeVertices);
627 	debug(LOG_TERRAIN, "maximum sector size due to indices: %i", maxSectorSizeIndices);
628 
629 	if (sectorSize > maxSectorSizeVertices)
630 	{
631 		sectorSize = maxSectorSizeVertices;
632 		decreasedSize = true;
633 	}
634 	if (sectorSize > maxSectorSizeIndices)
635 	{
636 		sectorSize = maxSectorSizeIndices;
637 		decreasedSize = true;
638 	}
639 	if (decreasedSize)
640 	{
641 		if (sectorSize < 1)
642 		{
643 			debug(LOG_WARNING, "GL_MAX_ELEMENTS_VERTICES: %i", (int)GLmaxElementsVertices);
644 			debug(LOG_WARNING, "GL_MAX_ELEMENTS_INDICES:  %i", (int)GLmaxElementsIndices);
645 			debug(LOG_WARNING, "maximum sector size due to vertices: %i", maxSectorSizeVertices);
646 			debug(LOG_WARNING, "maximum sector size due to indices: %i", maxSectorSizeIndices);
647 			debug(LOG_ERROR, "Your graphics card and/or drivers do not seem to support glDrawRangeElements, needed for the terrain renderer.");
648 			debug(LOG_ERROR, "- Do other 3D games work?");
649 			debug(LOG_ERROR, "- Did you install the latest drivers correctly?");
650 			debug(LOG_ERROR, "- Do you have a 3D window manager (Aero/Compiz) running?");
651 			return false;
652 		}
653 		debug(LOG_WARNING, "decreasing sector size to %i to fit graphics card constraints", sectorSize);
654 	}
655 
656 	// +4 = +1 for iHypot rounding, +1 for sector size rounding, +2 for edge of visibility
657 	terrainDistance = iHypot(visibleTiles.x / 2, visibleTiles.y / 2) + 4 + sectorSize / 2;
658 	debug(LOG_TERRAIN, "visible tiles x:%i y: %i", visibleTiles.x, visibleTiles.y);
659 	debug(LOG_TERRAIN, "terrain view distance: %i", terrainDistance);
660 
661 	/////////////////////
662 	// Create the sectors
663 	xSectors = (mapWidth + sectorSize - 1) / sectorSize;
664 	ySectors = (mapHeight + sectorSize - 1) / sectorSize;
665 	sectors = (Sector *)malloc(sizeof(Sector) * xSectors * ySectors);
666 
667 	////////////////////
668 	// fill the geometry part of the sectors
669 	geometry = (RenderVertex *)malloc(sizeof(RenderVertex) * xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2);
670 	geometryIndex = (GLuint *)malloc(sizeof(GLuint) * xSectors * ySectors * sectorSize * sectorSize * 12);
671 	geometrySize = 0;
672 	geometryIndexSize = 0;
673 
674 	water = (RenderVertex *)malloc(sizeof(RenderVertex) * xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2);
675 	waterIndex = (GLuint *)malloc(sizeof(GLuint) * xSectors * ySectors * sectorSize * sectorSize * 12);
676 	waterSize = 0;
677 	waterIndexSize = 0;
678 	for (x = 0; x < xSectors; x++)
679 	{
680 		for (y = 0; y < ySectors; y++)
681 		{
682 			sectors[x * ySectors + y].dirty = false;
683 			sectors[x * ySectors + y].geometryOffset = geometrySize;
684 			sectors[x * ySectors + y].geometrySize = 0;
685 			sectors[x * ySectors + y].waterOffset = waterSize;
686 			sectors[x * ySectors + y].waterSize = 0;
687 
688 			setSectorGeometry(x, y, geometry, water, &geometrySize, &waterSize);
689 
690 			sectors[x * ySectors + y].geometrySize = geometrySize - sectors[x * ySectors + y].geometryOffset;
691 			sectors[x * ySectors + y].waterSize = waterSize - sectors[x * ySectors + y].waterOffset;
692 			// and do the index buffers
693 			sectors[x * ySectors + y].geometryIndexOffset = geometryIndexSize;
694 			sectors[x * ySectors + y].geometryIndexSize = 0;
695 			sectors[x * ySectors + y].waterIndexOffset = waterIndexSize;
696 			sectors[x * ySectors + y].waterIndexSize = 0;
697 
698 			for (i = 0; i < sectorSize; i++)
699 			{
700 				for (j = 0; j < sectorSize; j++)
701 				{
702 					if (x * sectorSize + i >= mapWidth || y * sectorSize + j >= mapHeight)
703 					{
704 						continue; // off map, so skip
705 					}
706 
707 					/* One tile is composed of 4 triangles,
708 					 * we need _2_ vertices per tile (1)
709 					 * 		e.g. center and bottom left
710 					 * 	the other 3 vertices are from the adjacent tiles
711 					 * 	on their top and right.
712 					 * (1) The top row and right column of tiles need 4 vertices per tile
713 					 * 	because they do not have adjacent tiles on their top and right,
714 					 * 	that is why we add _1_ row and _1_ column to provide the geometry
715 					 * 	for these tiles.
716 					 * This is the source of the '*2' and '+1' in the index math below.
717 					 */
718 #define q(i,j,center) ((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + ((i)*(sectorSize+1)+(j))*2+(center))
719 					// First triangle
720 					geometryIndex[geometryIndexSize + 0]  = q(i  , j  , 1);	// Center vertex
721 					geometryIndex[geometryIndexSize + 1]  = q(i  , j  , 0);	// Bottom left
722 					geometryIndex[geometryIndexSize + 2]  = q(i + 1, j  , 0);	// Bottom right
723 					// Second triangle
724 					geometryIndex[geometryIndexSize + 3]  = q(i  , j  , 1);	// Center vertex
725 					geometryIndex[geometryIndexSize + 4]  = q(i  , j + 1, 0);	// Top left
726 					geometryIndex[geometryIndexSize + 5]  = q(i  , j  , 0);	// Bottom left
727 					// Third triangle
728 					geometryIndex[geometryIndexSize + 6]  = q(i  , j  , 1);	// Center vertex
729 					geometryIndex[geometryIndexSize + 7]  = q(i + 1, j + 1, 0);	// Top right
730 					geometryIndex[geometryIndexSize + 8]  = q(i  , j + 1, 0);	// Top left
731 					// Fourth triangle
732 					geometryIndex[geometryIndexSize + 9]  = q(i  , j  , 1);	// Center vertex
733 					geometryIndex[geometryIndexSize + 10] = q(i + 1, j  , 0);	// Bottom right
734 					geometryIndex[geometryIndexSize + 11] = q(i + 1, j + 1, 0);	// Top right
735 					geometryIndexSize += 12;
736 					if (isWater(i + x * sectorSize, j + y * sectorSize))
737 					{
738 						waterIndex[waterIndexSize + 0]  = q(i  , j  , 1);
739 						waterIndex[waterIndexSize + 1]  = q(i  , j  , 0);
740 						waterIndex[waterIndexSize + 2]  = q(i + 1, j  , 0);
741 
742 						waterIndex[waterIndexSize + 3]  = q(i  , j  , 1);
743 						waterIndex[waterIndexSize + 4]  = q(i  , j + 1, 0);
744 						waterIndex[waterIndexSize + 5]  = q(i  , j  , 0);
745 
746 						waterIndex[waterIndexSize + 6]  = q(i  , j  , 1);
747 						waterIndex[waterIndexSize + 7]  = q(i + 1, j + 1, 0);
748 						waterIndex[waterIndexSize + 8]  = q(i  , j + 1, 0);
749 
750 						waterIndex[waterIndexSize + 9]  = q(i  , j  , 1);
751 						waterIndex[waterIndexSize + 10] = q(i + 1, j  , 0);
752 						waterIndex[waterIndexSize + 11] = q(i + 1, j + 1, 0);
753 						waterIndexSize += 12;
754 					}
755 				}
756 			}
757 			sectors[x * ySectors + y].geometryIndexSize = geometryIndexSize - sectors[x * ySectors + y].geometryIndexOffset;
758 			sectors[x * ySectors + y].waterIndexSize = waterIndexSize - sectors[x * ySectors + y].waterIndexOffset;
759 		}
760 	}
761 	if (geometryVBO)
762 		delete geometryVBO;
763 	geometryVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer, gfx_api::context::buffer_storage_hint::dynamic_draw);
764 	geometryVBO->upload(sizeof(RenderVertex)*geometrySize, geometry);
765 	free(geometry);
766 
767 	if (geometryIndexVBO)
768 		delete geometryIndexVBO;
769 	geometryIndexVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::index_buffer);
770 	geometryIndexVBO->upload(sizeof(GLuint)*geometryIndexSize, geometryIndex);
771 	free(geometryIndex);
772 
773 	if (waterVBO)
774 		delete waterVBO;
775 	waterVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer, gfx_api::context::buffer_storage_hint::dynamic_draw);
776 	waterVBO->upload(sizeof(RenderVertex)*waterSize, water);
777 	free(water);
778 
779 	if (waterIndexVBO)
780 		delete waterIndexVBO;
781 	if (waterIndexSize > 0)
782 	{
783 		waterIndexVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::index_buffer);
784 		waterIndexVBO->upload(sizeof(GLuint)*waterIndexSize, waterIndex);
785 	}
786 	else
787 	{
788 		waterIndexVBO = nullptr;
789 	}
790 	free(waterIndex);
791 
792 
793 	////////////////////
794 	// fill the texture part of the sectors
795 	texture = (PIELIGHT *)malloc(sizeof(PIELIGHT) * xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2 * numGroundTypes);
796 	textureIndex = (GLuint *)malloc(sizeof(GLuint) * xSectors * ySectors * sectorSize * sectorSize * 12 * numGroundTypes);
797 	textureSize = 0;
798 	textureIndexSize = 0;
799 	for (layer = 0; layer < numGroundTypes; layer++)
800 	{
801 		for (x = 0; x < xSectors; x++)
802 		{
803 			for (y = 0; y < ySectors; y++)
804 			{
805 				if (layer == 0)
806 				{
807 					sectors[x * ySectors + y].textureOffset = (int *)malloc(sizeof(int) * numGroundTypes);
808 					sectors[x * ySectors + y].textureSize = (int *)malloc(sizeof(int) * numGroundTypes);
809 					sectors[x * ySectors + y].textureIndexOffset = (int *)malloc(sizeof(int) * numGroundTypes);
810 					sectors[x * ySectors + y].textureIndexSize = (int *)malloc(sizeof(int) * numGroundTypes);
811 				}
812 
813 				sectors[x * ySectors + y].textureOffset[layer] = textureSize;
814 				sectors[x * ySectors + y].textureSize[layer] = 0;
815 				sectors[x * ySectors + y].textureIndexOffset[layer] = textureIndexSize;
816 				sectors[x * ySectors + y].textureIndexSize[layer] = 0;
817 				//debug(LOG_WARNING, "offset when filling %i: %i", layer, xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer);
818 				for (i = 0; i < sectorSize + 1; i++)
819 				{
820 					for (j = 0; j < sectorSize + 1; j++)
821 					{
822 						bool draw = false;
823 						bool off_map;
824 
825 						// set transparency
826 						for (a = 0; a < 2; a++)
827 						{
828 							for (b = 0; b < 2; b++)
829 							{
830 								absX = x * sectorSize + i + a;
831 								absY = y * sectorSize + j + b;
832 								colour[a][b].rgba = 0x00FFFFFF; // transparent
833 
834 								// extend the terrain type for the bottom and left edges of the map
835 								off_map = false;
836 								if (absX == mapWidth)
837 								{
838 									off_map = true;
839 									absX--;
840 								}
841 								if (absY == mapHeight)
842 								{
843 									off_map = true;
844 									absY--;
845 								}
846 
847 								if (absX < 0 || absY < 0 || absX >= mapWidth || absY >= mapHeight)
848 								{
849 									// not on the map, so don't draw
850 									continue;
851 								}
852 								if (mapTile(absX, absY)->ground == layer)
853 								{
854 									colour[a][b].rgba = 0xFFFFFFFF;
855 									if (!off_map)
856 									{
857 										// if this point lies on the edge is may not force this tile to be drawn
858 										// otherwise this will give a bright line when fog is enabled
859 										draw = true;
860 									}
861 								}
862 							}
863 						}
864 						texture[xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2 * layer + ((x * ySectors + y) * (sectorSize + 1) * (sectorSize + 1) * 2 + (i * (sectorSize + 1) + j) * 2)].rgba = colour[0][0].rgba;
865 						averageColour(&centerColour, colour[0][0], colour[0][1], colour[1][0], colour[1][1]);
866 						texture[xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2 * layer + ((x * ySectors + y) * (sectorSize + 1) * (sectorSize + 1) * 2 + (i * (sectorSize + 1) + j) * 2 + 1)].rgba = centerColour.rgba;
867 						textureSize += 2;
868 						if ((draw) && i < sectorSize && j < sectorSize)
869 						{
870 							textureIndex[textureIndexSize + 0]  = q(i  , j  , 1);
871 							textureIndex[textureIndexSize + 1]  = q(i  , j  , 0);
872 							textureIndex[textureIndexSize + 2]  = q(i + 1, j  , 0);
873 
874 							textureIndex[textureIndexSize + 3]  = q(i  , j  , 1);
875 							textureIndex[textureIndexSize + 4]  = q(i  , j + 1, 0);
876 							textureIndex[textureIndexSize + 5]  = q(i  , j  , 0);
877 
878 							textureIndex[textureIndexSize + 6]  = q(i  , j  , 1);
879 							textureIndex[textureIndexSize + 7]  = q(i + 1, j + 1, 0);
880 							textureIndex[textureIndexSize + 8]  = q(i  , j + 1, 0);
881 
882 							textureIndex[textureIndexSize + 9]  = q(i  , j  , 1);
883 							textureIndex[textureIndexSize + 10] = q(i + 1, j  , 0);
884 							textureIndex[textureIndexSize + 11] = q(i + 1, j + 1, 0);
885 							textureIndexSize += 12;
886 						}
887 
888 					}
889 				}
890 				sectors[x * ySectors + y].textureSize[layer] = textureSize - sectors[x * ySectors + y].textureOffset[layer];
891 				sectors[x * ySectors + y].textureIndexSize[layer] = textureIndexSize - sectors[x * ySectors + y].textureIndexOffset[layer];
892 			}
893 		}
894 	}
895 	if (textureVBO)
896 		delete textureVBO;
897 	textureVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer);
898 	textureVBO->upload(sizeof(PIELIGHT)*xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2 * numGroundTypes, texture);
899 	free(texture);
900 
901 	if (textureIndexVBO)
902 		delete textureIndexVBO;
903 	textureIndexVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::index_buffer);
904 	textureIndexVBO->upload(sizeof(GLuint)*textureIndexSize, textureIndex);
905 	free(textureIndex);
906 
907 	// and finally the decals
908 	decaldata = (DecalVertex *)malloc(sizeof(DecalVertex) * mapWidth * mapHeight * 12);
909 	decalSize = 0;
910 	for (x = 0; x < xSectors; x++)
911 	{
912 		for (y = 0; y < ySectors; y++)
913 		{
914 			sectors[x * ySectors + y].decalOffset = decalSize;
915 			sectors[x * ySectors + y].decalSize = 0;
916 			setSectorDecals(x, y, decaldata, &decalSize);
917 			sectors[x * ySectors + y].decalSize = decalSize - sectors[x * ySectors + y].decalOffset;
918 		}
919 	}
920 	debug(LOG_TERRAIN, "%i decals found", decalSize / 12);
921 	if (decalVBO)
922 		delete decalVBO;
923 	decalVBO = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer, gfx_api::context::buffer_storage_hint::dynamic_draw);
924 	decalVBO->upload(sizeof(DecalVertex)*decalSize, decaldata);
925 	free(decaldata);
926 
927 	lightmap_tex_num = 0;
928 	lightmapLastUpdate = 0;
929 	lightmapWidth = 1;
930 	lightmapHeight = 1;
931 	// determine the smallest power-of-two size we can use for the lightmap
932 	while (mapWidth > (lightmapWidth <<= 1)) {}
933 	while (mapHeight > (lightmapHeight <<= 1)) {}
934 	debug(LOG_TERRAIN, "the size of the map is %ix%i", mapWidth, mapHeight);
935 	debug(LOG_TERRAIN, "lightmap texture size is %zu x %zu", lightmapWidth, lightmapHeight);
936 
937 	// Prepare the lightmap pixmap and texture
938 	lightmapPixmap = (gfx_api::gfxUByte *)calloc(lightmapWidth * lightmapHeight, 3 * sizeof(gfx_api::gfxUByte));
939 	if (lightmapPixmap == nullptr)
940 	{
941 		debug(LOG_FATAL, "Out of memory!");
942 		abort();
943 		return false;
944 	}
945 	if (lightmap_tex_num)
946 		delete lightmap_tex_num;
947 	lightmap_tex_num = gfx_api::context::get().create_texture(1, lightmapWidth, lightmapHeight, gfx_api::pixel_format::FORMAT_RGB8_UNORM_PACK8);
948 
949 	lightmap_tex_num->upload(0, 0, 0, lightmapWidth, lightmapHeight, gfx_api::pixel_format::FORMAT_RGB8_UNORM_PACK8, lightmapPixmap);
950 
951 	terrainInitialised = true;
952 
953 	return true;
954 }
955 
956 /// free all memory and opengl buffers used by the terrain renderer
shutdownTerrain()957 void shutdownTerrain()
958 {
959 	if (!sectors)
960 	{
961 		// This happens in some cases when loading a savegame from level init
962 		debug(LOG_ERROR, "Trying to shutdown terrain when we did not need to!");
963 		return;
964 	}
965 	delete geometryVBO;
966 	geometryVBO = nullptr;
967 	delete geometryIndexVBO;
968 	geometryIndexVBO = nullptr;
969 	delete waterVBO;
970 	waterVBO = nullptr;
971 	delete waterIndexVBO;
972 	waterIndexVBO = nullptr;
973 	delete textureVBO;
974 	textureVBO = nullptr;
975 	delete textureIndexVBO;
976 	textureIndexVBO = nullptr;
977 	delete decalVBO;
978 	decalVBO = nullptr;
979 
980 	for (int x = 0; x < xSectors; x++)
981 	{
982 		for (int y = 0; y < ySectors; y++)
983 		{
984 			free(sectors[x * ySectors + y].textureOffset);
985 			free(sectors[x * ySectors + y].textureSize);
986 			free(sectors[x * ySectors + y].textureIndexOffset);
987 			free(sectors[x * ySectors + y].textureIndexSize);
988 		}
989 	}
990 	free(sectors);
991 	sectors = nullptr;
992 	delete lightmap_tex_num;
993 	lightmap_tex_num = nullptr;
994 	free(lightmapPixmap);
995 	lightmapPixmap = nullptr;
996 
997 	terrainInitialised = false;
998 }
999 
updateLightMap()1000 static void updateLightMap()
1001 {
1002 	for (int j = 0; j < mapHeight; ++j)
1003 	{
1004 		for (int i = 0; i < mapWidth; ++i)
1005 		{
1006 			MAPTILE *psTile = mapTile(i, j);
1007 			PIELIGHT colour = psTile->colour;
1008 
1009 			if (psTile->tileInfoBits & BITS_GATEWAY && showGateways)
1010 			{
1011 				colour.byte.g = 255;
1012 			}
1013 			if (psTile->tileInfoBits & BITS_MARKED)
1014 			{
1015 				int m = getModularScaledGraphicsTime(2048, 255);
1016 				colour.byte.r = MAX(m, 255 - m);
1017 			}
1018 
1019 			lightmapPixmap[(i + j * lightmapWidth) * 3 + 0] = colour.byte.r;
1020 			lightmapPixmap[(i + j * lightmapWidth) * 3 + 1] = colour.byte.g;
1021 			lightmapPixmap[(i + j * lightmapWidth) * 3 + 2] = colour.byte.b;
1022 
1023 			if (!pie_GetFogStatus())
1024 			{
1025 				// fade to black at the edges of the visible terrain area
1026 				const float playerX = map_coordf(playerPos.p.x);
1027 				const float playerY = map_coordf(playerPos.p.z);
1028 
1029 				const float distA = i - (playerX - visibleTiles.x / 2);
1030 				const float distB = (playerX + visibleTiles.x / 2) - i;
1031 				const float distC = j - (playerY - visibleTiles.y / 2);
1032 				const float distD = (playerY + visibleTiles.y / 2) - j;
1033 				float darken, distToEdge;
1034 
1035 				// calculate the distance to the closest edge of the visible map
1036 				// determine the smallest distance
1037 				distToEdge = distA;
1038 				if (distB < distToEdge)
1039 				{
1040 					distToEdge = distB;
1041 				}
1042 				if (distC < distToEdge)
1043 				{
1044 					distToEdge = distC;
1045 				}
1046 				if (distD < distToEdge)
1047 				{
1048 					distToEdge = distD;
1049 				}
1050 
1051 				darken = (distToEdge) / 2.0f;
1052 				if (darken <= 0)
1053 				{
1054 					lightmapPixmap[(i + j * lightmapWidth) * 3 + 0] = 0;
1055 					lightmapPixmap[(i + j * lightmapWidth) * 3 + 1] = 0;
1056 					lightmapPixmap[(i + j * lightmapWidth) * 3 + 2] = 0;
1057 				}
1058 				else if (darken < 1)
1059 				{
1060 					lightmapPixmap[(i + j * lightmapWidth) * 3 + 0] *= darken;
1061 					lightmapPixmap[(i + j * lightmapWidth) * 3 + 1] *= darken;
1062 					lightmapPixmap[(i + j * lightmapWidth) * 3 + 2] *= darken;
1063 				}
1064 			}
1065 		}
1066 	}
1067 }
1068 
cullTerrain()1069 static void cullTerrain()
1070 {
1071 	for (int x = 0; x < xSectors; x++)
1072 	{
1073 		for (int y = 0; y < ySectors; y++)
1074 		{
1075 			float xPos = world_coord(x * sectorSize + sectorSize / 2);
1076 			float yPos = world_coord(y * sectorSize + sectorSize / 2);
1077 			float distance = pow(playerPos.p.x - xPos, 2) + pow(playerPos.p.z - yPos, 2);
1078 
1079 			if (distance > pow((double)world_coord(terrainDistance), 2))
1080 			{
1081 				sectors[x * ySectors + y].draw = false;
1082 			}
1083 			else
1084 			{
1085 				sectors[x * ySectors + y].draw = true;
1086 				if (sectors[x * ySectors + y].dirty)
1087 				{
1088 					updateSectorGeometry(x, y);
1089 					sectors[x * ySectors + y].dirty = false;
1090 				}
1091 			}
1092 		}
1093 	}
1094 }
1095 
drawDepthOnly(const glm::mat4 & ModelViewProjection,const glm::vec4 & paramsXLight,const glm::vec4 & paramsYLight)1096 static void drawDepthOnly(const glm::mat4 &ModelViewProjection, const glm::vec4 &paramsXLight, const glm::vec4 &paramsYLight)
1097 {
1098 	// bind the vertex buffer
1099 	gfx_api::TerrainDepth::get().bind();
1100 	gfx_api::TerrainDepth::get().bind_vertex_buffers(geometryVBO);
1101 	gfx_api::TerrainDepth::get().bind_constants({ ModelViewProjection, paramsXLight, paramsYLight, glm::vec4(0.f), glm::vec4(0.f), glm::mat4(1.f), glm::mat4(1.f), glm::vec4(0.f), 0, 0.f, 0.f, 0, 0 });
1102 	gfx_api::context::get().bind_index_buffer(*geometryIndexVBO, gfx_api::index_type::u32);
1103 
1104 	// draw slightly higher distance than it actually is so it will not
1105 	// by accident obscure the actual terrain
1106 	gfx_api::context::get().set_polygon_offset(0.1f, 1.f);
1107 
1108 	for (int x = 0; x < xSectors; x++)
1109 	{
1110 		for (int y = 0; y < ySectors; y++)
1111 		{
1112 			if (sectors[x * ySectors + y].draw)
1113 			{
1114 				addDrawRangeElements<gfx_api::TerrainDepth>(
1115 					sectors[x * ySectors + y].geometryOffset,
1116 					sectors[x * ySectors + y].geometryOffset + sectors[x * ySectors + y].geometrySize,
1117 					sectors[x * ySectors + y].geometryIndexSize,
1118 					sectors[x * ySectors + y].geometryIndexOffset);
1119 			}
1120 		}
1121 	}
1122 	finishDrawRangeElements<gfx_api::TerrainDepth>();
1123 	gfx_api::context::get().set_polygon_offset(0.f, 0.f);
1124 	gfx_api::TerrainDepth::get().unbind_vertex_buffers(geometryVBO);
1125 	gfx_api::context::get().unbind_index_buffer(*geometryIndexVBO);
1126 }
1127 
drawTerrainLayers(const glm::mat4 & ModelViewProjection,const glm::vec4 & paramsXLight,const glm::vec4 & paramsYLight,const glm::mat4 & textureMatrix)1128 static void drawTerrainLayers(const glm::mat4 &ModelViewProjection, const glm::vec4 &paramsXLight, const glm::vec4 &paramsYLight, const glm::mat4 &textureMatrix)
1129 {
1130 	const auto &renderState = getCurrentRenderState();
1131 	const glm::vec4 fogColor(
1132 		renderState.fogColour.vector[0] / 255.f,
1133 		renderState.fogColour.vector[1] / 255.f,
1134 		renderState.fogColour.vector[2] / 255.f,
1135 		renderState.fogColour.vector[3] / 255.f
1136 	);
1137 
1138 	// load the vertex (geometry) buffer
1139 	gfx_api::TerrainLayer::get().bind();
1140 	gfx_api::TerrainLayer::get().bind_vertex_buffers(geometryVBO, textureVBO);
1141 	gfx_api::context::get().bind_index_buffer(*textureIndexVBO, gfx_api::index_type::u32);
1142 	ASSERT_OR_RETURN(, psGroundTypes, "Ground type was not set, no textures will be seen.");
1143 
1144 	int32_t maxGfxTextureSize = gfx_api::context::get().get_context_value(gfx_api::context::context_value::MAX_TEXTURE_SIZE);
1145 	int maxTerrainTextureSize = std::max(std::min({getTextureSize(), maxGfxTextureSize}), MIN_TERRAIN_TEXTURE_SIZE);
1146 
1147 	// draw each layer separately
1148 	for (int layer = 0; layer < numGroundTypes; layer++)
1149 	{
1150 		const glm::vec4 paramsX(0, 0, -1.0f / world_coord(psGroundTypes[layer].textureSize), 0 );
1151 		const glm::vec4 paramsY(1.0f / world_coord(psGroundTypes[layer].textureSize), 0, 0, 0 );
1152 		gfx_api::TerrainLayer::get().bind_constants({ ModelViewProjection, paramsX, paramsY, paramsXLight, paramsYLight, glm::mat4(1.f), textureMatrix,
1153 			fogColor, renderState.fogEnabled, renderState.fogBegin, renderState.fogEnd, 0, 1 });
1154 
1155 		// load the texture
1156 		optional<size_t> texPage = iV_GetTexture(psGroundTypes[layer].textureName, true, maxTerrainTextureSize, maxTerrainTextureSize);
1157 		ASSERT_OR_RETURN(, texPage.has_value(), "Failed to retrieve terrain texture: %s", psGroundTypes[layer].textureName);
1158 		gfx_api::TerrainLayer::get().bind_textures(&pie_Texture(texPage.value()), lightmap_tex_num);
1159 
1160 		// load the color buffer
1161 		gfx_api::context::get().bind_vertex_buffers(1, { std::make_tuple(textureVBO, static_cast<size_t>(sizeof(PIELIGHT)*xSectors * ySectors * (sectorSize + 1) * (sectorSize + 1) * 2 * layer)) });
1162 
1163 		for (int x = 0; x < xSectors; x++)
1164 		{
1165 			for (int y = 0; y < ySectors; y++)
1166 			{
1167 				if (sectors[x * ySectors + y].draw)
1168 				{
1169 					addDrawRangeElements<gfx_api::TerrainLayer>(
1170 						sectors[x * ySectors + y].geometryOffset,
1171 						sectors[x * ySectors + y].geometryOffset + sectors[x * ySectors + y].geometrySize,
1172 						sectors[x * ySectors + y].textureIndexSize[layer],
1173 						sectors[x * ySectors + y].textureIndexOffset[layer]);
1174 				}
1175 			}
1176 		}
1177 		finishDrawRangeElements<gfx_api::TerrainLayer>();
1178 	}
1179 	gfx_api::TerrainLayer::get().unbind_vertex_buffers(geometryVBO, textureVBO);
1180 	gfx_api::context::get().unbind_index_buffer(*textureIndexVBO);
1181 }
1182 
drawDecals(const glm::mat4 & ModelViewProjection,const glm::vec4 & paramsXLight,const glm::vec4 & paramsYLight,const glm::mat4 & textureMatrix)1183 static void drawDecals(const glm::mat4 &ModelViewProjection, const glm::vec4 &paramsXLight, const glm::vec4 &paramsYLight, const glm::mat4 &textureMatrix)
1184 {
1185 	const auto &renderState = getCurrentRenderState();
1186 	const glm::vec4 fogColor(
1187 		renderState.fogColour.vector[0] / 255.f,
1188 		renderState.fogColour.vector[1] / 255.f,
1189 		renderState.fogColour.vector[2] / 255.f,
1190 		renderState.fogColour.vector[3] / 255.f
1191 	);
1192 	gfx_api::TerrainDecals::get().bind();
1193 	gfx_api::TerrainDecals::get().bind_textures(&pie_Texture(terrainPage), lightmap_tex_num);
1194 	gfx_api::TerrainDecals::get().bind_vertex_buffers(decalVBO);
1195 	gfx_api::TerrainDecals::get().bind_constants({ ModelViewProjection, textureMatrix, paramsXLight, paramsYLight,
1196 		fogColor, renderState.fogEnabled, renderState.fogBegin, renderState.fogEnd, 0, 1 });
1197 
1198 	int size = 0;
1199 	int offset = 0;
1200 	for (int x = 0; x < xSectors; x++)
1201 	{
1202 		for (int y = 0; y < ySectors + 1; y++)
1203 		{
1204 			if (y < ySectors && offset + size == sectors[x * ySectors + y].decalOffset && sectors[x * ySectors + y].draw)
1205 			{
1206 				// append
1207 				size += sectors[x * ySectors + y].decalSize;
1208 				continue;
1209 			}
1210 			// can't append, so draw what we have and start anew
1211 			if (size > 0)
1212 			{
1213 				gfx_api::TerrainDecals::get().draw(size, offset);
1214 			}
1215 			size = 0;
1216 			if (y < ySectors && sectors[x * ySectors + y].draw)
1217 			{
1218 				offset = sectors[x * ySectors + y].decalOffset;
1219 				size = sectors[x * ySectors + y].decalSize;
1220 			}
1221 		}
1222 	}
1223 	gfx_api::TerrainDecals::get().unbind_vertex_buffers(decalVBO);
1224 }
1225 
1226 
1227 /**
1228  * Update the lightmap and draw the terrain and decals.
1229  * This function first draws the terrain in black, and then uses additive blending to put the terrain layers
1230  * on it one by one. Finally the decals are drawn.
1231  */
drawTerrain(const glm::mat4 & mvp)1232 void drawTerrain(const glm::mat4 &mvp)
1233 {
1234 	const glm::vec4 paramsXLight(1.0f / world_coord(mapWidth) *((float)mapWidth / (float)lightmapWidth), 0, 0, 0);
1235 	const glm::vec4 paramsYLight(0, 0, -1.0f / world_coord(mapHeight) *((float)mapHeight / (float)lightmapHeight), 0);
1236 
1237 	///////////////////////////////////
1238 	// set up the lightmap texture
1239 
1240 	// we limit the framerate of the lightmap, because updating a texture is an expensive operation
1241 	if (realTime - lightmapLastUpdate >= LIGHTMAP_REFRESH)
1242 	{
1243 		lightmapLastUpdate = realTime;
1244 		updateLightMap();
1245 
1246 		lightmap_tex_num->upload(0, 0, 0, lightmapWidth, lightmapHeight, gfx_api::pixel_format::FORMAT_RGB8_UNORM_PACK8, lightmapPixmap);
1247 	}
1248 
1249 	///////////////////////////////////
1250 	// terrain culling
1251 	cullTerrain();
1252 
1253 	// shift the lightmap half a tile as lights are supposed to be placed at the center of a tile
1254 	const glm::mat4 lightMatrix = glm::translate(glm::vec3(1.f / (float)lightmapWidth / 2, 1.f / (float)lightmapHeight / 2, 0.f));
1255 
1256 	//////////////////////////////////////
1257 	// canvas to draw on
1258 	drawDepthOnly(mvp, paramsXLight, paramsYLight);
1259 
1260 	///////////////////////////////////
1261 	// terrain
1262 	drawTerrainLayers(mvp, paramsXLight, paramsYLight, lightMatrix);
1263 
1264 	//////////////////////////////////
1265 	// decals
1266 	drawDecals(mvp, paramsXLight, paramsYLight, lightMatrix);
1267 }
1268 
1269 /**
1270  * Draw the water.
1271  */
drawWater(const glm::mat4 & viewMatrix)1272 void drawWater(const glm::mat4 &viewMatrix)
1273 {
1274 	if (!waterIndexVBO)
1275 	{
1276 		return; // no water
1277 	}
1278 
1279 	int x, y;
1280 	const glm::vec4 paramsX(0, 0, -1.0f / world_coord(4), 0);
1281 	const glm::vec4 paramsY(1.0f / world_coord(4), 0, 0, 0);
1282 	const glm::vec4 paramsX2(0, 0, -1.0f / world_coord(5), 0);
1283 	const glm::vec4 paramsY2(1.0f / world_coord(5), 0, 0, 0);
1284 	const auto &renderState = getCurrentRenderState();
1285 
1286 	int32_t maxGfxTextureSize = gfx_api::context::get().get_context_value(gfx_api::context::context_value::MAX_TEXTURE_SIZE);
1287 	int maxTerrainTextureSize = std::max(std::min({getTextureSize(), maxGfxTextureSize}), MIN_TERRAIN_TEXTURE_SIZE);
1288 
1289 	optional<size_t> water1_texPage = iV_GetTexture("page-80-water-1.png", true, maxTerrainTextureSize, maxTerrainTextureSize);
1290 	optional<size_t> water2_texPage = iV_GetTexture("page-81-water-2.png", true, maxTerrainTextureSize, maxTerrainTextureSize);
1291 	ASSERT_OR_RETURN(, water1_texPage.has_value() && water2_texPage.has_value(), "Failed to load water texture");
1292 	gfx_api::WaterPSO::get().bind();
1293 	gfx_api::WaterPSO::get().bind_textures(&pie_Texture(water1_texPage.value()), &pie_Texture(water2_texPage.value()));
1294 	gfx_api::WaterPSO::get().bind_vertex_buffers(waterVBO);
1295 	gfx_api::WaterPSO::get().bind_constants({ viewMatrix, paramsX, paramsY, paramsX2, paramsY2,
1296 		glm::translate(glm::vec3(waterOffset, 0.f, 0.f)), glm::mat4(1.f), glm::vec4(0.f), renderState.fogEnabled, renderState.fogBegin, renderState.fogEnd, 0, 1
1297 	});
1298 	gfx_api::context::get().bind_index_buffer(*waterIndexVBO, gfx_api::index_type::u32);
1299 
1300 	for (x = 0; x < xSectors; x++)
1301 	{
1302 		for (y = 0; y < ySectors; y++)
1303 		{
1304 			if (sectors[x * ySectors + y].draw)
1305 			{
1306 				addDrawRangeElements<gfx_api::WaterPSO>(
1307 				                     sectors[x * ySectors + y].geometryOffset,
1308 				                     sectors[x * ySectors + y].geometryOffset + sectors[x * ySectors + y].geometrySize,
1309 				                     sectors[x * ySectors + y].waterIndexSize,
1310 				                     sectors[x * ySectors + y].waterIndexOffset);
1311 			}
1312 		}
1313 	}
1314 	finishDrawRangeElements<gfx_api::WaterPSO>();
1315 	gfx_api::WaterPSO::get().unbind_vertex_buffers(waterVBO);
1316 	gfx_api::context::get().unbind_index_buffer(*waterIndexVBO);
1317 
1318 	// move the water
1319 	if (!gamePaused())
1320 	{
1321 		waterOffset += graphicsTimeAdjustedIncrement(0.1f);
1322 	}
1323 }
1324