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(¢erColour, 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 ¶msXLight, const glm::vec4 ¶msYLight)
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 ¶msXLight, const glm::vec4 ¶msYLight, 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 ¶msXLight, const glm::vec4 ¶msYLight, 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