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, &parallaxHeightTex);
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 = &centerNormalsSynced[0];
420 		  float3* ucn = &centerNormalsUnsynced[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