1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include "SMFReadMap.h"
5 #include "SMFGroundTextures.h"
6 #include "SMFGroundDrawer.h"
7 #include "SMFFormat.h"
8 #include "Map/MapInfo.h"
9 #include "Game/Camera.h"
10 #include "Game/LoadScreen.h"
11 #include "Rendering/GlobalRendering.h"
12 #include "Rendering/Env/ISky.h"
13 #include "Rendering/GL/myGL.h"
14 #include "Rendering/Textures/Bitmap.h"
15 #include "System/bitops.h"
16 #include "System/Config/ConfigHandler.h"
17 #include "System/EventHandler.h"
18 #include "System/Exceptions.h"
19 #include "System/FileSystem/FileHandler.h"
20 #include "System/ThreadPool.h"
21 #include "System/myMath.h"
22 #include "System/Util.h"
23
24 #define SSMF_UNCOMPRESSED_NORMALS 0
25
26 using std::max;
27
28 CONFIG(bool, GroundNormalTextureHighPrecision).defaultValue(false);
29 CONFIG(float, SMFTexAniso).defaultValue(0.0f);
30
31 CR_BIND_DERIVED(CSMFReadMap, CReadMap, (""))
32
33
CSMFReadMap(std::string mapname)34 CSMFReadMap::CSMFReadMap(std::string mapname)
35 : CEventClient("[CSMFReadMap]", 271950, false)
36 , file(mapname)
37 , detailTex(0)
38 , specularTex(0)
39 , shadingTex(0)
40 , normalsTex(0)
41 , minimapTex(0)
42 , splatDetailTex(0)
43 , splatDistrTex(0)
44 , skyReflectModTex(0)
45 , detailNormalTex(0)
46 , lightEmissionTex(0)
47 , parallaxHeightTex(0)
48 , groundDrawer(NULL)
49 {
50 loadscreen->SetLoadMessage("Loading SMF");
51 eventHandler.AddClient(this);
52
53 haveSpecularTexture = !(mapInfo->smf.specularTexName.empty());
54 haveSplatTexture = (!mapInfo->smf.splatDetailTexName.empty() && !mapInfo->smf.splatDistrTexName.empty());
55
56 ParseHeader();
57 LoadHeightMap();
58 CReadMap::Initialize();
59
60 LoadMinimap();
61
62 ConfigureAnisotropy();
63 InitializeWaterHeightColors();
64
65 CreateSpecularTex();
66 CreateSplatDetailTextures();
67 CreateGrassTex();
68 CreateDetailTex();
69 CreateShadingTex();
70 CreateNormalTex();
71
72 file.ReadFeatureInfo();
73 }
74
75
~CSMFReadMap()76 CSMFReadMap::~CSMFReadMap()
77 {
78 delete groundDrawer;
79
80 glDeleteTextures(1, &detailTex );
81 glDeleteTextures(1, &specularTex );
82 glDeleteTextures(1, &minimapTex );
83 glDeleteTextures(1, &shadingTex );
84 glDeleteTextures(1, &normalsTex );
85 glDeleteTextures(1, &splatDetailTex );
86 glDeleteTextures(1, &splatDistrTex );
87 glDeleteTextures(1, &grassShadingTex );
88 glDeleteTextures(1, &skyReflectModTex );
89 glDeleteTextures(1, &detailNormalTex );
90 glDeleteTextures(1, &lightEmissionTex );
91 glDeleteTextures(1, ¶llaxHeightTex);
92 }
93
94
ParseHeader()95 void CSMFReadMap::ParseHeader()
96 {
97 const SMFHeader& header = file.GetHeader();
98
99 gs->mapx = header.mapx;
100 gs->mapy = header.mapy;
101
102 numBigTexX = (header.mapx / bigSquareSize);
103 numBigTexY = (header.mapy / bigSquareSize);
104 bigTexSize = (SQUARE_SIZE * bigSquareSize);
105 tileMapSizeX = (header.mapx / tileScale);
106 tileMapSizeY = (header.mapy / tileScale);
107 tileCount = (header.mapx * header.mapy) / (tileScale * tileScale);
108 mapSizeX = (header.mapx * SQUARE_SIZE);
109 mapSizeZ = (header.mapy * SQUARE_SIZE);
110 maxHeightMapIdx = ((header.mapx + 1) * (header.mapy + 1)) - 1;
111 heightMapSizeX = (header.mapx + 1);
112 }
113
114
LoadHeightMap()115 void CSMFReadMap::LoadHeightMap()
116 {
117 const SMFHeader& header = file.GetHeader();
118
119 cornerHeightMapSynced.resize((gs->mapx + 1) * (gs->mapy + 1));
120 #ifdef USE_UNSYNCED_HEIGHTMAP
121 cornerHeightMapUnsynced.resize((gs->mapx + 1) * (gs->mapy + 1));
122 #endif
123
124 heightMapSyncedPtr = &cornerHeightMapSynced;
125 heightMapUnsyncedPtr = &cornerHeightMapUnsynced;
126
127 const float minHgt = mapInfo->smf.minHeightOverride ? mapInfo->smf.minHeight : header.minHeight;
128 const float maxHgt = mapInfo->smf.maxHeightOverride ? mapInfo->smf.maxHeight : header.maxHeight;
129 float* cornerHeightMapSyncedData = (cornerHeightMapSynced.empty())? NULL: &cornerHeightMapSynced[0];
130 float* cornerHeightMapUnsyncedData = (cornerHeightMapUnsynced.empty())? NULL: &cornerHeightMapUnsynced[0];
131
132 // FIXME:
133 // callchain CReadMap::Initialize --> CReadMap::UpdateHeightMapSynced(0, 0, gs->mapx, gs->mapy) -->
134 // PushVisibleHeightMapUpdate --> (next UpdateDraw) UpdateHeightMapUnsynced(0, 0, gs->mapx, gs->mapy)
135 // initializes the UHM a second time
136 // merge them some way so UHM & shadingtex is available from the time readMap got created
137 file.ReadHeightmap(cornerHeightMapSyncedData, cornerHeightMapUnsyncedData, minHgt, (maxHgt - minHgt) / 65536.0f);
138 }
139
140
LoadMinimap()141 void CSMFReadMap::LoadMinimap()
142 {
143 // the minimap is a static texture
144 std::vector<unsigned char> minimapTexBuf(MINIMAP_SIZE, 0);
145 file.ReadMinimap(&minimapTexBuf[0]);
146
147 glGenTextures(1, &minimapTex);
148 glBindTexture(GL_TEXTURE_2D, minimapTex);
149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
150 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
151 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, MINIMAP_NUM_MIPMAP - 1);
152 int offset = 0;
153 for (unsigned int i = 0; i < MINIMAP_NUM_MIPMAP; i++) {
154 const int mipsize = 1024 >> i;
155 const int size = ((mipsize + 3) / 4) * ((mipsize + 3) / 4) * 8;
156 glCompressedTexImage2DARB(GL_TEXTURE_2D, i, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, mipsize, mipsize, 0, size, &minimapTexBuf[0] + offset);
157 offset += size;
158 }
159 }
160
161
InitializeWaterHeightColors()162 void CSMFReadMap::InitializeWaterHeightColors()
163 {
164 for (int a = 0; a < 1024; ++a) {
165 for (int b = 0; b < 3; ++b) {
166 const float absorbColor = mapInfo->water.baseColor[b] - mapInfo->water.absorb[b] * a;
167 const float clampedColor = std::max(mapInfo->water.minColor[b], absorbColor);
168 waterHeightColors[a * 4 + b] = std::min(255.0f, clampedColor * 255.0f);
169 }
170 waterHeightColors[a * 4 + 3] = 1;
171 }
172 }
173
174
CreateSpecularTex()175 void CSMFReadMap::CreateSpecularTex()
176 {
177 if (!haveSpecularTexture) {
178 return;
179 }
180
181 CBitmap specularTexBM;
182 CBitmap skyReflectModTexBM;
183 CBitmap detailNormalTexBM;
184 CBitmap lightEmissionTexBM;
185 CBitmap parallaxHeightTexBM;
186
187 if (!specularTexBM.Load(mapInfo->smf.specularTexName)) {
188 // maps wants specular lighting, but no moderation
189 specularTexBM.channels = 4;
190 specularTexBM.AllocDummy(SColor(255,255,255,255));
191 }
192
193 specularTex = specularTexBM.CreateTexture(false);
194
195 // no default 1x1 textures for these
196 if (skyReflectModTexBM.Load(mapInfo->smf.skyReflectModTexName)) {
197 skyReflectModTex = skyReflectModTexBM.CreateTexture(false);
198 }
199
200 if (detailNormalTexBM.Load(mapInfo->smf.detailNormalTexName)) {
201 detailNormalTex = detailNormalTexBM.CreateTexture(false);
202 }
203
204 if (lightEmissionTexBM.Load(mapInfo->smf.lightEmissionTexName)) {
205 lightEmissionTex = lightEmissionTexBM.CreateTexture(false);
206 }
207
208 if (parallaxHeightTexBM.Load(mapInfo->smf.parallaxHeightTexName)) {
209 parallaxHeightTex = parallaxHeightTexBM.CreateTexture(false);
210 }
211 }
212
CreateSplatDetailTextures()213 void CSMFReadMap::CreateSplatDetailTextures()
214 {
215 if (!haveSplatTexture) {
216 return;
217 }
218
219 CBitmap splatDistrTexBM;
220 CBitmap splatDetailTexBM;
221
222 // if the map supplies an intensity- AND a distribution-texture for
223 // detail-splat blending, the regular detail-texture is not used
224 if (!splatDetailTexBM.Load(mapInfo->smf.splatDetailTexName)) {
225 // default detail-texture should be all-grey
226 splatDetailTexBM.channels = 4;
227 splatDetailTexBM.AllocDummy(SColor(127,127,127,127));
228 }
229
230 if (!splatDistrTexBM.Load(mapInfo->smf.splatDistrTexName)) {
231 splatDistrTexBM.channels = 4;
232 splatDistrTexBM.AllocDummy(SColor(255,0,0,0));
233 }
234
235 splatDetailTex = splatDetailTexBM.CreateTexture(true);
236 splatDistrTex = splatDistrTexBM.CreateTexture(true);
237 }
238
239
CreateGrassTex()240 void CSMFReadMap::CreateGrassTex()
241 {
242 grassShadingTex = minimapTex;
243
244 CBitmap grassShadingTexBM;
245 if (grassShadingTexBM.Load(mapInfo->smf.grassShadingTexName)) {
246 grassShadingTex = grassShadingTexBM.CreateTexture(true);
247 }
248 }
249
250
CreateDetailTex()251 void CSMFReadMap::CreateDetailTex()
252 {
253 CBitmap detailTexBM;
254 if (!detailTexBM.Load(mapInfo->smf.detailTexName)) {
255 throw content_error("Could not load detail texture from file " + mapInfo->smf.detailTexName);
256 }
257
258 glGenTextures(1, &detailTex);
259 glBindTexture(GL_TEXTURE_2D, detailTex);
260 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
261 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
262 if (anisotropy != 0.0f) {
263 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
264 }
265 glBuildMipmaps(GL_TEXTURE_2D, GL_RGBA8, detailTexBM.xsize, detailTexBM.ysize, GL_RGBA, GL_UNSIGNED_BYTE, detailTexBM.mem);
266 }
267
268
CreateShadingTex()269 void CSMFReadMap::CreateShadingTex()
270 {
271 // the shading/normal texture buffers must have PO2 dimensions
272 // (excess elements that no vertices map into are left unused)
273 glGenTextures(1, &shadingTex);
274 glBindTexture(GL_TEXTURE_2D, shadingTex);
275 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
276 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
277 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
278 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
279 if (anisotropy != 0.0f) {
280 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
281 }
282
283 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gs->pwr2mapx, gs->pwr2mapy, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
284
285 shadingTexBuffer.resize(gs->mapx * gs->mapy * 4, 0);
286 shadingTexUpdateNeeded = false;
287 shadingTexUpdateProgress = -1;
288 }
289
290
CreateNormalTex()291 void CSMFReadMap::CreateNormalTex()
292 {
293 #if (SSMF_UNCOMPRESSED_NORMALS == 0)
294 GLenum texFormat = GL_LUMINANCE_ALPHA16F_ARB;
295 if (configHandler->GetBool("GroundNormalTextureHighPrecision")) {
296 texFormat = GL_LUMINANCE_ALPHA32F_ARB;
297 }
298 #endif
299
300 normalTexSize.x = gs->mapxp1;
301 normalTexSize.y = gs->mapyp1;
302 if (!globalRendering->supportNPOTs) {
303 normalTexSize.x = next_power_of_2(normalTexSize.x);
304 normalTexSize.y = next_power_of_2(normalTexSize.y);
305 }
306
307 glGenTextures(1, &normalsTex);
308 glBindTexture(GL_TEXTURE_2D, normalsTex);
309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
310 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
311 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
312 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
313 #if (SSMF_UNCOMPRESSED_NORMALS == 1)
314 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, normalTexSize.x, normalTexSize.y, 0, GL_RGBA, GL_FLOAT, NULL);
315 #else
316 glTexImage2D(GL_TEXTURE_2D, 0, texFormat, normalTexSize.x, normalTexSize.y, 0, GL_LUMINANCE_ALPHA, GL_FLOAT, NULL);
317 #endif
318 }
319
NewGroundDrawer()320 void CSMFReadMap::NewGroundDrawer() { groundDrawer = new CSMFGroundDrawer(this); }
GetGroundDrawer()321 CBaseGroundDrawer* CSMFReadMap::GetGroundDrawer() { return groundDrawer; }
322
323
324
UpdateHeightMapUnsynced(const SRectangle & update)325 void CSMFReadMap::UpdateHeightMapUnsynced(const SRectangle& update)
326 {
327 UpdateVertexNormalsUnsynced(update);
328 UpdateFaceNormalsUnsynced(update);
329 UpdateNormalTexture(update);
330 UpdateShadingTexture(update);
331 }
332
333
UpdateVertexNormalsUnsynced(const SRectangle & update)334 void CSMFReadMap::UpdateVertexNormalsUnsynced(const SRectangle& update)
335 {
336 #ifdef USE_UNSYNCED_HEIGHTMAP
337 const float* shm = &cornerHeightMapSynced[0];
338 float* uhm = &cornerHeightMapUnsynced[0];
339 float3* vvn = &visVertexNormals[0];
340
341 const int W = gs->mapxp1;
342 const int H = gs->mapyp1;
343 static const int SS = SQUARE_SIZE;
344
345 // a heightmap update over (x1, y1) - (x2, y2) implies the
346 // normals change over (x1 - 1, y1 - 1) - (x2 + 1, y2 + 1)
347 const int minx = std::max(update.x1 - 1, 0);
348 const int minz = std::max(update.y1 - 1, 0);
349 const int maxx = std::min(update.x2 + 1, W - 1);
350 const int maxz = std::min(update.y2 + 1, H - 1);
351
352 for_mt(minz, maxz+1, [&](const int z) {
353 for (int x = minx; x <= maxx; x++) {
354 const int vIdxTL = (z ) * W + x;
355
356 const int xOffL = (x > 0)? 1: 0;
357 const int xOffR = (x < W - 1)? 1: 0;
358 const int zOffT = (z > 0)? 1: 0;
359 const int zOffB = (z < H - 1)? 1: 0;
360
361 const float sxm1 = (x - 1) * SS;
362 const float sx = x * SS;
363 const float sxp1 = (x + 1) * SS;
364
365 const float szm1 = (z - 1) * SS;
366 const float sz = z * SS;
367 const float szp1 = (z + 1) * SS;
368
369 const int shxm1 = x - xOffL;
370 const int shx = x;
371 const int shxp1 = x + xOffR;
372
373 const int shzm1 = (z - zOffT) * W;
374 const int shz = z * W;
375 const int shzp1 = (z + zOffB) * W;
376
377 // pretend there are 8 incident triangle faces per vertex
378 // for each these triangles, calculate the surface normal,
379 // then average the 8 normals (this stays closest to the
380 // heightmap data)
381 // if edge vertex, don't add virtual neighbor normals to vn
382 const float3 vmm = float3(sx , shm[shz + shx ], sz );
383
384 const float3 vtl = float3(sxm1, shm[shzm1 + shxm1], szm1) - vmm;
385 const float3 vtm = float3(sx , shm[shzm1 + shx ], szm1) - vmm;
386 const float3 vtr = float3(sxp1, shm[shzm1 + shxp1], szm1) - vmm;
387
388 const float3 vml = float3(sxm1, shm[shz + shxm1], sz ) - vmm;
389 const float3 vmr = float3(sxp1, shm[shz + shxp1], sz ) - vmm;
390
391 const float3 vbl = float3(sxm1, shm[shzp1 + shxm1], szp1) - vmm;
392 const float3 vbm = float3(sx , shm[shzp1 + shx ], szp1) - vmm;
393 const float3 vbr = float3(sxp1, shm[shzp1 + shxp1], szp1) - vmm;
394
395 float3 vn(0.0f, 0.0f, 0.0f);
396 vn += vtm.cross(vtl) * (zOffT & xOffL); assert(vtm.cross(vtl).y >= 0.0f);
397 vn += vtr.cross(vtm) * (zOffT ); assert(vtr.cross(vtm).y >= 0.0f);
398 vn += vmr.cross(vtr) * (zOffT & xOffR); assert(vmr.cross(vtr).y >= 0.0f);
399 vn += vbr.cross(vmr) * ( xOffR); assert(vbr.cross(vmr).y >= 0.0f);
400 vn += vtl.cross(vml) * ( xOffL); assert(vtl.cross(vml).y >= 0.0f);
401 vn += vbm.cross(vbr) * (zOffB & xOffR); assert(vbm.cross(vbr).y >= 0.0f);
402 vn += vbl.cross(vbm) * (zOffB ); assert(vbl.cross(vbm).y >= 0.0f);
403 vn += vml.cross(vbl) * (zOffB & xOffL); assert(vml.cross(vbl).y >= 0.0f);
404
405 // update the visible vertex/face height/normal
406 uhm[vIdxTL] = shm[vIdxTL];
407 vvn[vIdxTL] = vn.ANormalize();
408 }
409 });
410 #endif
411 }
412
413
UpdateFaceNormalsUnsynced(const SRectangle & update)414 void CSMFReadMap::UpdateFaceNormalsUnsynced(const SRectangle& update)
415 {
416 #ifdef USE_UNSYNCED_HEIGHTMAP
417 const float3* sfn = &faceNormalsSynced[0];
418 float3* ufn = &faceNormalsUnsynced[0];
419 const float3* scn = ¢erNormalsSynced[0];
420 float3* ucn = ¢erNormalsUnsynced[0];
421
422 // a heightmap update over (x1, y1) - (x2, y2) implies the
423 // normals change over (x1 - 1, y1 - 1) - (x2 + 1, y2 + 1)
424 const int minx = std::max(update.x1 - 1, 0);
425 const int minz = std::max(update.y1 - 1, 0);
426 const int maxx = std::min(update.x2 + 1, gs->mapxm1);
427 const int maxz = std::min(update.y2 + 1, gs->mapym1);
428
429 int idx0, idx1;
430 for (int z = minz; z <= maxz; z++) {
431 idx0 = (z * gs->mapx + minx) * 2 ;
432 idx1 = (z * gs->mapx + maxx) * 2 + 1;
433 memcpy(&ufn[idx0], &sfn[idx0], (idx1 - idx0 + 1) * sizeof(float3));
434
435 idx0 = (z * gs->mapx + minx);
436 idx1 = (z * gs->mapx + maxx);
437 memcpy(&ucn[idx0], &scn[idx0], (idx1 - idx0 + 1) * sizeof(float3));
438 }
439 #endif
440 }
441
442
UpdateNormalTexture(const SRectangle & update)443 void CSMFReadMap::UpdateNormalTexture(const SRectangle& update)
444 {
445 // Update VertexNormalsTexture (not used by ARB shaders)
446 if (globalRendering->haveGLSL) {
447 // texture space is [0 .. gs->mapx] x [0 .. gs->mapy] (NPOT; vertex-aligned)
448
449 float3* vvn = &visVertexNormals[0];
450
451 // a heightmap update over (x1, y1) - (x2, y2) implies the
452 // normals change over (x1 - 1, y1 - 1) - (x2 + 1, y2 + 1)
453 const int minx = std::max(update.x1 - 1, 0);
454 const int minz = std::max(update.y1 - 1, 0);
455 const int maxx = std::min(update.x2 + 1, gs->mapx);
456 const int maxz = std::min(update.y2 + 1, gs->mapy);
457
458 const int xsize = (maxx - minx) + 1;
459 const int zsize = (maxz - minz) + 1;
460
461 // Note, it doesn't make sense to use a PBO here.
462 // Cause the upstreamed float32s need to be transformed to float16s, which seems to happen on the CPU!
463 #if (SSMF_UNCOMPRESSED_NORMALS == 1)
464 std::vector<float> pixels(xsize * zsize * 4, 0.0f);
465 #else
466 std::vector<float> pixels(xsize * zsize * 2, 0.0f);
467 #endif
468
469 for (int z = minz; z <= maxz; z++) {
470 for (int x = minx; x <= maxx; x++) {
471 const float3& vertNormal = vvn[z * gs->mapxp1 + x];
472
473 #if (SSMF_UNCOMPRESSED_NORMALS == 1)
474 pixels[((z - minz) * xsize + (x - minx)) * 4 + 0] = vertNormal.x;
475 pixels[((z - minz) * xsize + (x - minx)) * 4 + 1] = vertNormal.y;
476 pixels[((z - minz) * xsize + (x - minx)) * 4 + 2] = vertNormal.z;
477 pixels[((z - minz) * xsize + (x - minx)) * 4 + 3] = 1.0f;
478 #else
479 // note: y-coord is regenerated in the shader via "sqrt(1 - x*x - z*z)",
480 // this gives us 2 solutions but we know that the y-coord always points
481 // upwards, so we can reconstruct it in the shader.
482 pixels[((z - minz) * xsize + (x - minx)) * 2 + 0] = vertNormal.x;
483 pixels[((z - minz) * xsize + (x - minx)) * 2 + 1] = vertNormal.z;
484 #endif
485 }
486 }
487
488 glBindTexture(GL_TEXTURE_2D, normalsTex);
489 #if (SSMF_UNCOMPRESSED_NORMALS == 1)
490 glTexSubImage2D(GL_TEXTURE_2D, 0, minx, minz, xsize, zsize, GL_RGBA, GL_FLOAT, &pixels[0]);
491 #else
492 glTexSubImage2D(GL_TEXTURE_2D, 0, minx, minz, xsize, zsize, GL_LUMINANCE_ALPHA, GL_FLOAT, &pixels[0]);
493 #endif
494 }
495 }
496
497
UpdateShadingTexture(const SRectangle & update)498 void CSMFReadMap::UpdateShadingTexture(const SRectangle& update)
499 {
500 // update the shading texture (even if the map has specular
501 // lighting, we still need it to modulate the minimap image)
502 // this can be done for diffuse lighting only
503 {
504 // texture space is [0 .. gs->mapxm1] x [0 .. gs->mapym1]
505
506 // enlarge rect by 1pixel in all directions (cause we use center normals and not corner ones)
507 const int x1 = std::max(update.x1 - 1, 0);
508 const int y1 = std::max(update.y1 - 1, 0);
509 const int x2 = std::min(update.x2 + 1, gs->mapxm1);
510 const int y2 = std::min(update.y2 + 1, gs->mapym1);
511
512 const int xsize = (x2 - x1) + 1; // +1 cause we iterate:
513 const int ysize = (y2 - y1) + 1; // x1 <= xi <= x2 (not! x1 <= xi < x2)
514
515 //TODO switch to PBO?
516 std::vector<unsigned char> pixels(xsize * ysize * 4, 0.0f);
517
518 for_mt(0, ysize, [&](const int y) {
519 const int idx1 = (y + y1) * gs->mapx + x1;
520 const int idx2 = (y + y1) * gs->mapx + x2;
521 UpdateShadingTexPart(idx1, idx2, &pixels[y * xsize * 4]);
522 });
523
524 // check if we were in a dynamic sun issued shadingTex update
525 // and our updaterect was already updated (buffered, not send to the GPU yet!)
526 // if so update it in that buffer, too
527 if (shadingTexUpdateProgress > (y1 * gs->mapx + x1)) {
528 for (int y = 0; y < ysize; ++y) {
529 const int idx = (y + y1) * gs->mapx + x1;
530 memcpy(&shadingTexBuffer[idx * 4] , &pixels[y * xsize * 4], xsize);
531 }
532 }
533
534 // redefine the texture subregion
535 glBindTexture(GL_TEXTURE_2D, shadingTex);
536 glTexSubImage2D(GL_TEXTURE_2D, 0, x1, y1, xsize, ysize, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
537 }
538 }
539
540
GetCenterHeightUnsynced(const int x,const int y) const541 const float CSMFReadMap::GetCenterHeightUnsynced(const int x, const int y) const
542 {
543 static const float* hm = GetCornerHeightMapUnsynced();
544
545 float h = hm[(y ) * gs->mapxp1 + (x )] +
546 hm[(y ) * gs->mapxp1 + (x + 1)] +
547 hm[(y + 1) * gs->mapxp1 + (x )] +
548 hm[(y + 1) * gs->mapxp1 + (x + 1)];
549
550 return h * 0.25f;
551 }
552
553
UpdateShadingTexPart(int idx1,int idx2,unsigned char * dst) const554 void CSMFReadMap::UpdateShadingTexPart(int idx1, int idx2, unsigned char* dst) const
555 {
556 for (int idx = idx1; idx <= idx2; ++idx) {
557 const int i = idx - idx1;
558 const int xi = idx % gs->mapx;
559 const int yi = idx / gs->mapx;
560
561 const float height = GetCenterHeightUnsynced(xi, yi);
562
563 if (height < 0.0f) {
564 // Underwater
565 const int clampedHeight = std::min((int)(-height), int(sizeof(waterHeightColors) / 4) - 1);
566 float lightIntensity = std::min((DiffuseSunCoeff(xi, yi) + 0.2f) * 2.0f, 1.0f);
567
568 if (height > -10.0f) {
569 const float wc = -height * 0.1f;
570 const float3 lightColor = GetLightValue(xi, yi) * (1.0f - wc) * 255.0f;
571
572 lightIntensity *= wc;
573
574 dst[i * 4 + 0] = (unsigned char) (waterHeightColors[clampedHeight * 4 + 0] * lightIntensity + lightColor.x);
575 dst[i * 4 + 1] = (unsigned char) (waterHeightColors[clampedHeight * 4 + 1] * lightIntensity + lightColor.y);
576 dst[i * 4 + 2] = (unsigned char) (waterHeightColors[clampedHeight * 4 + 2] * lightIntensity + lightColor.z);
577 } else {
578 dst[i * 4 + 0] = (unsigned char) (waterHeightColors[clampedHeight * 4 + 0] * lightIntensity);
579 dst[i * 4 + 1] = (unsigned char) (waterHeightColors[clampedHeight * 4 + 1] * lightIntensity);
580 dst[i * 4 + 2] = (unsigned char) (waterHeightColors[clampedHeight * 4 + 2] * lightIntensity);
581 }
582 dst[i * 4 + 3] = EncodeHeight(height);
583 } else {
584 // Above water
585 const float3& light = GetLightValue(xi, yi) * 255.0f;
586 dst[i * 4 + 0] = (unsigned char) light.x;
587 dst[i * 4 + 1] = (unsigned char) light.y;
588 dst[i * 4 + 2] = (unsigned char) light.z;
589 dst[i * 4 + 3] = 255;
590 }
591 }
592 }
593
594
DiffuseSunCoeff(const int x,const int y) const595 float CSMFReadMap::DiffuseSunCoeff(const int x, const int y) const
596 {
597 const float3& N = centerNormalsUnsynced[y * gs->mapx + x];
598 const float3& L = sky->GetLight()->GetLightDir();
599 return Clamp(L.dot(N), 0.0f, 1.0f);
600 }
601
602
GetLightValue(const int x,const int y) const603 float3 CSMFReadMap::GetLightValue(const int x, const int y) const
604 {
605 float3 light =
606 mapInfo->light.groundAmbientColor +
607 mapInfo->light.groundSunColor * DiffuseSunCoeff(x, y);
608 light *= CGlobalRendering::SMF_INTENSITY_MULT;
609
610 for (int a = 0; a < 3; ++a) {
611 light[a] = std::min(light[a], 1.0f);
612 }
613
614 return light;
615 }
616
SunChanged(const float3 & sunDir)617 void CSMFReadMap::SunChanged(const float3& sunDir)
618 {
619 if (shadingTexUpdateProgress < 0) {
620 shadingTexUpdateProgress = 0;
621 } else {
622 shadingTexUpdateNeeded = true;
623 }
624 }
625
626
627
UpdateShadingTexture()628 void CSMFReadMap::UpdateShadingTexture()
629 {
630 static const int xsize = gs->mapx;
631 static const int ysize = gs->mapy;
632 static const int pixels = xsize * ysize;
633
634 // with GLSL, the shading texture has very limited use (minimap etc) so we reduce the updaterate
635 //FIXME replace with a real check if glsl is used in terrain rendering!
636 //FIXME make configurable? or even FPS depending?
637 const int update_rate = (globalRendering->haveGLSL ? 64*64 : 64*128);
638
639 if (shadingTexUpdateProgress < 0) {
640 return;
641 }
642
643 if (shadingTexUpdateProgress >= pixels) {
644 if (shadingTexUpdateNeeded) {
645 shadingTexUpdateProgress = 0;
646 shadingTexUpdateNeeded = false;
647 } else {
648 shadingTexUpdateProgress = -1;
649 }
650
651 //FIXME use FBO and blend slowly new and old? (this way update rate could reduced even more -> saves CPU time)
652 glBindTexture(GL_TEXTURE_2D, shadingTex);
653 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, xsize, ysize, GL_RGBA, GL_UNSIGNED_BYTE, &shadingTexBuffer[0]);
654 return;
655 }
656
657 const int idx1 = shadingTexUpdateProgress;
658 const int idx2 = std::min(idx1 + update_rate, pixels - 1);
659
660 for_mt(idx1, idx2+1, 1025, [&](const int idx){
661 const int idx3 = std::min(idx2, idx + 1024);
662 UpdateShadingTexPart(idx, idx3, &shadingTexBuffer[idx * 4]);
663 });
664
665 shadingTexUpdateProgress += update_rate;
666 }
667
668
DrawMinimap() const669 void CSMFReadMap::DrawMinimap() const
670 {
671 glDisable(GL_ALPHA_TEST);
672
673 glActiveTextureARB(GL_TEXTURE0_ARB);
674 glEnable(GL_TEXTURE_2D);
675 glBindTexture(GL_TEXTURE_2D, shadingTex);
676 // glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
677 // glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
678 // glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
679 // glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
680 // glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
681
682 glActiveTextureARB(GL_TEXTURE1_ARB);
683 glEnable(GL_TEXTURE_2D);
684 glBindTexture(GL_TEXTURE_2D, minimapTex);
685 // glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
686
687 if (groundDrawer->DrawExtraTex()) {
688 glActiveTextureARB(GL_TEXTURE2_ARB);
689 glEnable(GL_TEXTURE_2D);
690 glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_ADD_SIGNED_ARB);
691 glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB);
692 glBindTexture(GL_TEXTURE_2D, groundDrawer->GetActiveInfoTexture());
693 glActiveTextureARB(GL_TEXTURE0_ARB);
694 }
695
696 static float isx = gs->mapx / float(gs->pwr2mapx);
697 static float isy = gs->mapy / float(gs->pwr2mapy);
698
699 glColor4f(1, 1, 1, 1);
700 glBegin(GL_QUADS);
701 glTexCoord2f(0, isy);
702 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
703 glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0, isy);
704 glVertex2f(0, 0);
705 glTexCoord2f(0, 0);
706 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
707 glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0, 0);
708 glVertex2f(0, 1);
709 glTexCoord2f(isx, 0);
710 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
711 glMultiTexCoord2fARB(GL_TEXTURE2_ARB, isx, 0);
712 glVertex2f(1, 1);
713 glTexCoord2f(isx, isy);
714 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
715 glMultiTexCoord2fARB(GL_TEXTURE2_ARB, isx, isy);
716 glVertex2f(1, 0);
717 glEnd();
718
719 glActiveTextureARB(GL_TEXTURE1_ARB);
720 glDisable(GL_TEXTURE_2D);
721
722 glActiveTextureARB(GL_TEXTURE2_ARB);
723 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
724 glDisable(GL_TEXTURE_2D);
725
726 glActiveTextureARB(GL_TEXTURE0_ARB);
727 //glTexEnvi(GL_TEXTURE_ENV,GL_RGB_SCALE_ARB,1);
728 //glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
729 glDisable(GL_TEXTURE_2D);
730 }
731
732
GridVisibility(CCamera * cam,int quadSize,float maxdist,CReadMap::IQuadDrawer * qd,int extraSize)733 void CSMFReadMap::GridVisibility(CCamera* cam, int quadSize, float maxdist, CReadMap::IQuadDrawer* qd, int extraSize)
734 {
735 const int cx = cam->GetPos().x / (SQUARE_SIZE * quadSize);
736 const int cy = cam->GetPos().z / (SQUARE_SIZE * quadSize);
737
738 const int drawSquare = int(maxdist / (SQUARE_SIZE * quadSize)) + 1;
739
740 const int drawQuadsX = gs->mapx / quadSize;
741 const int drawQuadsY = gs->mapy / quadSize;
742
743 int sy = Clamp(cy - drawSquare, 0, drawQuadsY - 1);
744 int ey = Clamp(cy + drawSquare, 0, drawQuadsY - 1);
745 int sxi = Clamp(cx - drawSquare, 0, drawQuadsX - 1);
746 int exi = Clamp(cx + drawSquare, 0, drawQuadsX - 1);
747
748 // NOTE:
749 // GridVisibility is only ever passed <camera>, not <cam2>
750 // (but only <cam2> has sides calculated for it at present
751 // by SMFGroundDrawer::UpdateCamRestraints, and older code
752 // iterated over SMFGroundDrawer::{left, right})
753 // UpdateCamRestraints(cam);
754 CCamera* frustumCam = cam2;
755
756 // When called from within Lua for GetVisible{Units, Features}, camera might not be updated
757 if (extraSize == INT_MAX) {
758 extraSize = 0;
759 frustumCam = cam;
760 groundDrawer->UpdateCamRestraints(frustumCam);
761 }
762
763 const std::vector<CCamera::FrustumLine> negSides = frustumCam->GetNegFrustumSides();
764 const std::vector<CCamera::FrustumLine> posSides = frustumCam->GetPosFrustumSides();
765
766 std::vector<CCamera::FrustumLine>::const_iterator fli;
767
768 for (int y = sy; y <= ey; y++) {
769 int sx = sxi;
770 int ex = exi;
771 float xtest, xtest2;
772
773 for (fli = negSides.begin(); fli != negSides.end(); ++fli) {
774 xtest = ((fli->base + fli->dir * ( y * quadSize) ));
775 xtest2 = ((fli->base + fli->dir * ((y * quadSize) + quadSize)));
776
777 if (xtest2 < xtest) //use std::min?
778 xtest = xtest2;
779
780 xtest /= quadSize;
781
782 if (xtest - extraSize > sx)
783 sx = ((int) xtest) - extraSize;
784 }
785 for (fli = posSides.begin(); fli != posSides.end(); ++fli) {
786 xtest = ((fli->base + fli->dir * (y * quadSize) ));
787 xtest2 = ((fli->base + fli->dir * ((y * quadSize) + quadSize)));
788
789 if (xtest2 > xtest)
790 xtest = xtest2;
791
792 xtest /= quadSize;
793
794 if (xtest + extraSize < ex)
795 ex = ((int) xtest) + extraSize;
796 }
797
798 for (int x = sx; x <= ex; x++)
799 qd->DrawQuad(x, y);
800 }
801 }
802
803
GetNumFeatures()804 int CSMFReadMap::GetNumFeatures ()
805 {
806 return file.GetNumFeatures();
807 }
808
809
GetNumFeatureTypes()810 int CSMFReadMap::GetNumFeatureTypes()
811 {
812 return file.GetNumFeatureTypes();
813 }
814
815
GetFeatureInfo(MapFeatureInfo * f)816 void CSMFReadMap::GetFeatureInfo(MapFeatureInfo* f)
817 {
818 file.ReadFeatureInfo(f);
819 }
820
821
GetFeatureTypeName(int typeID)822 const char* CSMFReadMap::GetFeatureTypeName (int typeID)
823 {
824 return file.GetFeatureTypeName(typeID);
825 }
826
827
GetInfoMap(const std::string & name,MapBitmapInfo * bmInfo)828 unsigned char* CSMFReadMap::GetInfoMap(const std::string& name, MapBitmapInfo* bmInfo)
829 {
830 // get size
831 file.GetInfoMapSize(name, bmInfo);
832 if (bmInfo->width <= 0) return NULL;
833
834 // get data
835 unsigned char* data = new unsigned char[bmInfo->width * bmInfo->height];
836 file.ReadInfoMap(name, data);
837 return data;
838 }
839
840
FreeInfoMap(const std::string & name,unsigned char * data)841 void CSMFReadMap::FreeInfoMap(const std::string& name, unsigned char *data)
842 {
843 delete[] data;
844 }
845
846
ConfigureAnisotropy()847 void CSMFReadMap::ConfigureAnisotropy()
848 {
849 if (!GLEW_EXT_texture_filter_anisotropic) {
850 anisotropy = 0.0f;
851 return;
852 }
853
854 anisotropy = configHandler->GetFloat("SMFTexAniso");
855
856 if (anisotropy < 1.0f) {
857 anisotropy = 0.0f; // disabled
858 } else {
859 GLfloat maxAniso;
860 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso);
861 if (anisotropy > maxAniso) {
862 anisotropy = maxAniso;
863 configHandler->Set("SMFTexAniso", anisotropy);
864 }
865 }
866 }
867