1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <algorithm>
4 #include <cctype>
5 
6 #include "GroundDecalHandler.h"
7 #include "Game/Camera.h"
8 #include "Game/GameSetup.h"
9 #include "Game/GlobalUnsynced.h"
10 #include "Lua/LuaParser.h"
11 #include "Map/BaseGroundDrawer.h"
12 #include "Map/Ground.h"
13 #include "Map/MapInfo.h"
14 #include "Map/ReadMap.h"
15 #include "Rendering/GlobalRendering.h"
16 #include "Rendering/ShadowHandler.h"
17 #include "Rendering/Env/ISky.h"
18 #include "Rendering/GL/myGL.h"
19 #include "Rendering/GL/VertexArray.h"
20 #include "Rendering/Shaders/ShaderHandler.h"
21 #include "Rendering/Shaders/Shader.h"
22 #include "Rendering/Textures/Bitmap.h"
23 #include "Sim/Features/FeatureDef.h"
24 #include "Sim/Units/Unit.h"
25 #include "Sim/Units/UnitDef.h"
26 #include "Sim/Projectiles/ExplosionListener.h"
27 #include "Sim/Weapons/WeaponDef.h"
28 #include "System/Config/ConfigHandler.h"
29 #include "System/EventHandler.h"
30 #include "System/Exceptions.h"
31 #include "System/Log/ILog.h"
32 #include "System/myMath.h"
33 #include "System/Util.h"
34 #include "System/FileSystem/FileSystem.h"
35 
36 using std::list;
37 using std::min;
38 using std::max;
39 
40 CGroundDecalHandler* groundDecals_ = NULL;
41 
42 CONFIG(int, GroundScarAlphaFade).defaultValue(0);
43 
CGroundDecalHandler()44 CGroundDecalHandler::CGroundDecalHandler()
45 	: CEventClient("[CGroundDecalHandler]", 314159, false)
46 {
47 	scarField = NULL;
48 	if (!GetDrawDecals())
49 		return;
50 
51 	eventHandler.AddClient(this);
52 	CExplosionCreator::AddExplosionListener(this);
53 
54 	groundScarAlphaFade = (configHandler->GetInt("GroundScarAlphaFade") != 0);
55 
56 	unsigned char* buf=new unsigned char[512*512*4];
57 	memset(buf,0,512*512*4);
58 
59 	LuaParser resourcesParser("gamedata/resources.lua",
60 	                          SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
61 	if (!resourcesParser.Execute()) {
62 		LOG_L(L_ERROR, "Failed to load resources: %s",
63 				resourcesParser.GetErrorLog().c_str());
64 	}
65 
66 	const LuaTable scarsTable = resourcesParser.GetRoot().SubTable("graphics").SubTable("scars");
67 	LoadScar("bitmaps/" + scarsTable.GetString(2, "scars/scar2.bmp"), buf, 0,   0);
68 	LoadScar("bitmaps/" + scarsTable.GetString(3, "scars/scar3.bmp"), buf, 256, 0);
69 	LoadScar("bitmaps/" + scarsTable.GetString(1, "scars/scar1.bmp"), buf, 0,   256);
70 	LoadScar("bitmaps/" + scarsTable.GetString(4, "scars/scar4.bmp"), buf, 256, 256);
71 
72 	glGenTextures(1, &scarTex);
73 	glBindTexture(GL_TEXTURE_2D, scarTex);
74 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
75 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
76 	glBuildMipmaps(GL_TEXTURE_2D,GL_RGBA8 ,512, 512, GL_RGBA, GL_UNSIGNED_BYTE, buf);
77 
78 	scarFieldX=gs->mapx/32;
79 	scarFieldY=gs->mapy/32;
80 	scarField=new std::set<Scar*>[scarFieldX*scarFieldY];
81 
82 	lastTest=0;
83 	maxOverlap=decalLevel+1;
84 
85 	delete[] buf;
86 
87 	LoadDecalShaders();
88 }
89 
90 
91 
~CGroundDecalHandler()92 CGroundDecalHandler::~CGroundDecalHandler()
93 {
94 	eventHandler.RemoveClient(this);
95 
96 	for (std::vector<TrackType*>::iterator tti = trackTypes.begin(); tti != trackTypes.end(); ++tti) {
97 		for (set<UnitTrackStruct*>::iterator ti = (*tti)->tracks.begin(); ti != (*tti)->tracks.end(); ++ti) {
98 			delete *ti;
99 		}
100 		glDeleteTextures(1, &(*tti)->texture);
101 		delete *tti;
102 	}
103 	for (std::vector<TrackToAdd>::iterator ti = tracksToBeAdded.begin(); ti != tracksToBeAdded.end(); ++ti) {
104 		delete (*ti).tp;
105 		if ((*ti).unit == NULL)
106 			tracksToBeDeleted.push_back((*ti).ts);
107 	}
108 	for (std::vector<UnitTrackStruct *>::iterator ti = tracksToBeDeleted.begin(); ti != tracksToBeDeleted.end(); ++ti) {
109 		delete *ti;
110 	}
111 
112 	for (std::vector<SolidObjectDecalType*>::iterator tti = objectDecalTypes.begin(); tti != objectDecalTypes.end(); ++tti) {
113 		for (set<SolidObjectGroundDecal*>::iterator ti = (*tti)->objectDecals.begin(); ti != (*tti)->objectDecals.end(); ++ti) {
114 			if ((*ti)->owner)
115 				(*ti)->owner->groundDecal = 0;
116 			if ((*ti)->gbOwner)
117 				(*ti)->gbOwner->decal = 0;
118 			delete *ti;
119 		}
120 		glDeleteTextures (1, &(*tti)->texture);
121 		delete *tti;
122 	}
123 	for (std::list<Scar*>::iterator si = scars.begin(); si != scars.end(); ++si) {
124 		delete *si;
125 	}
126 	for (std::vector<Scar*>::iterator si = scarsToBeAdded.begin(); si != scarsToBeAdded.end(); ++si) {
127 		delete *si;
128 	}
129 	if (scarField != NULL) {
130 		delete[] scarField;
131 
132 		glDeleteTextures(1, &scarTex);
133 	}
134 
135 	shaderHandler->ReleaseProgramObjects("[GroundDecalHandler]");
136 	decalShaders.clear();
137 }
138 
LoadDecalShaders()139 void CGroundDecalHandler::LoadDecalShaders() {
140 	#define sh shaderHandler
141 	decalShaders.resize(DECAL_SHADER_LAST, NULL);
142 
143 	// SM3 maps have no baked lighting, so decals blend differently
144 	const bool haveShadingTexture = (readMap->GetShadingTexture() != 0);
145 	const char* fragmentProgramNameARB = haveShadingTexture?
146 		"ARB/GroundDecalsSMF.fp":
147 		"ARB/GroundDecalsSM3.fp";
148 	const std::string extraDef = haveShadingTexture?
149 		"#define HAVE_SHADING_TEX 1\n":
150 		"#define HAVE_SHADING_TEX 0\n";
151 
152 	decalShaders[DECAL_SHADER_ARB ] = sh->CreateProgramObject("[GroundDecalHandler]", "DecalShaderARB",  true);
153 	decalShaders[DECAL_SHADER_GLSL] = sh->CreateProgramObject("[GroundDecalHandler]", "DecalShaderGLSL", false);
154 	decalShaders[DECAL_SHADER_CURR] = decalShaders[DECAL_SHADER_ARB];
155 
156 	if (globalRendering->haveARB && !globalRendering->haveGLSL) {
157 		decalShaders[DECAL_SHADER_ARB]->AttachShaderObject(sh->CreateShaderObject("ARB/GroundDecals.vp", "", GL_VERTEX_PROGRAM_ARB));
158 		decalShaders[DECAL_SHADER_ARB]->AttachShaderObject(sh->CreateShaderObject(fragmentProgramNameARB, "", GL_FRAGMENT_PROGRAM_ARB));
159 		decalShaders[DECAL_SHADER_ARB]->Link();
160 	} else {
161 		if (globalRendering->haveGLSL) {
162 			decalShaders[DECAL_SHADER_GLSL]->AttachShaderObject(sh->CreateShaderObject("GLSL/GroundDecalsVertProg.glsl", "",       GL_VERTEX_SHADER));
163 			decalShaders[DECAL_SHADER_GLSL]->AttachShaderObject(sh->CreateShaderObject("GLSL/GroundDecalsFragProg.glsl", extraDef, GL_FRAGMENT_SHADER));
164 			decalShaders[DECAL_SHADER_GLSL]->Link();
165 
166 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("decalTex");           // idx 0
167 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("shadeTex");           // idx 1
168 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("shadowTex");          // idx 2
169 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("mapSizePO2");         // idx 3
170 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("groundAmbientColor"); // idx 4
171 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("shadowMatrix");       // idx 5
172 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("shadowParams");       // idx 6
173 			decalShaders[DECAL_SHADER_GLSL]->SetUniformLocation("shadowDensity");      // idx 7
174 
175 			decalShaders[DECAL_SHADER_GLSL]->Enable();
176 			decalShaders[DECAL_SHADER_GLSL]->SetUniform1i(0, 0); // decalTex  (idx 0, texunit 0)
177 			decalShaders[DECAL_SHADER_GLSL]->SetUniform1i(1, 1); // shadeTex  (idx 1, texunit 1)
178 			decalShaders[DECAL_SHADER_GLSL]->SetUniform1i(2, 2); // shadowTex (idx 2, texunit 2)
179 			decalShaders[DECAL_SHADER_GLSL]->SetUniform2f(3, 1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE));
180 			decalShaders[DECAL_SHADER_GLSL]->SetUniform1f(7, sky->GetLight()->GetGroundShadowDensity());
181 			decalShaders[DECAL_SHADER_GLSL]->Disable();
182 			decalShaders[DECAL_SHADER_GLSL]->Validate();
183 
184 			decalShaders[DECAL_SHADER_CURR] = decalShaders[DECAL_SHADER_GLSL];
185 		}
186 	}
187 
188 	#undef sh
189 }
190 
SunChanged(const float3 & sunDir)191 void CGroundDecalHandler::SunChanged(const float3& sunDir) {
192 	if (globalRendering->haveGLSL && decalShaders.size() > DECAL_SHADER_GLSL) {
193 		decalShaders[DECAL_SHADER_GLSL]->Enable();
194 		decalShaders[DECAL_SHADER_GLSL]->SetUniform1f(7, sky->GetLight()->GetGroundShadowDensity());
195 		decalShaders[DECAL_SHADER_GLSL]->Disable();
196 	}
197 }
198 
AddQuadVertices(CVertexArray * va,int x,float * yv,int z,const float * uv,unsigned char * color)199 static inline void AddQuadVertices(CVertexArray* va, int x, float* yv, int z, const float* uv, unsigned char* color)
200 {
201 	#define HEIGHT2WORLD(x) ((x) << 3)
202 	#define VERTEX(x, y, z) float3(HEIGHT2WORLD((x)), (y), HEIGHT2WORLD((z)))
203 	va->AddVertexTC( VERTEX(x    , yv[0], z    ),   uv[0], uv[1],   color);
204 	va->AddVertexTC( VERTEX(x + 1, yv[1], z    ),   uv[2], uv[3],   color);
205 	va->AddVertexTC( VERTEX(x + 1, yv[2], z + 1),   uv[4], uv[5],   color);
206 	va->AddVertexTC( VERTEX(x    , yv[3], z + 1),   uv[6], uv[7],   color);
207 	#undef VERTEX
208 	#undef HEIGHT2WORLD
209 }
210 
211 
DrawObjectDecal(SolidObjectGroundDecal * decal)212 inline void CGroundDecalHandler::DrawObjectDecal(SolidObjectGroundDecal* decal)
213 {
214 	// TODO: do we want LOS-checks for decals?
215 	if (!camera->InView(decal->pos, decal->radius))
216 		return;
217 
218 
219 	const float* hm = readMap->GetCornerHeightMapUnsynced();
220 	const int gsmx = gs->mapx;
221 	const int gsmx1 = gsmx + 1;
222 	const int gsmy = gs->mapy;
223 
224 	SColor color(255, 255, 255, int(decal->alpha * 255));
225 
226 	#ifndef DEBUG
227 	#define HEIGHT(z, x) (hm[((z) * gsmx1) + (x)])
228 	#else
229 	#define HEIGHT(z, x) (assert((z) <= gsmy), assert((x) <= gsmx), (hm[((z) * gsmx1) + (x)]))
230 	#endif
231 
232 	if (!decal->va) {
233 		// NOTE: this really needs CLOD'ing
234 		decal->va = new CVertexArray();
235 		decal->va->Initialize();
236 
237 		const int
238 			dxsize = decal->xsize,
239 			dzsize = decal->ysize,
240 			dxpos  = decal->posx,              // top-left quad x-coordinate
241 			dzpos  = decal->posy,              // top-left quad z-coordinate
242 			dxoff  = (dxpos < 0)? -(dxpos): 0, // offset from left map edge
243 			dzoff  = (dzpos < 0)? -(dzpos): 0; // offset from top map edge
244 
245 		const float xts = 1.0f / dxsize;
246 		const float zts = 1.0f / dzsize;
247 
248 		float yv[4] = {0.0f}; // heights at each sub-quad vertex (tl, tr, br, bl)
249 		float uv[8] = {0.0f}; // tex-coors at each sub-quad vertex
250 
251 		// clipped decal dimensions
252 		int cxsize = dxsize - dxoff;
253 		int czsize = dzsize - dzoff;
254 
255 		if ((dxpos + dxsize) > gsmx) { cxsize -= ((dxpos + dxsize) - gsmx); }
256 		if ((dzpos + dzsize) > gsmy) { czsize -= ((dzpos + dzsize) - gsmy); }
257 
258 		for (int vx = 0; vx < cxsize; vx++) {
259 			for (int vz = 0; vz < czsize; vz++) {
260 				const int rx = dxoff + vx;  // x-coor in decal-space
261 				const int rz = dzoff + vz;  // z-coor in decal-space
262 				const int px = dxpos + rx;  // x-coor in heightmap-space
263 				const int pz = dzpos + rz;  // z-coor in heightmap-space
264 
265 				yv[0] = HEIGHT(pz,     px    ); yv[1] = HEIGHT(pz,     px + 1);
266 				yv[2] = HEIGHT(pz + 1, px + 1); yv[3] = HEIGHT(pz + 1, px    );
267 
268 				switch (decal->facing) {
269 					case FACING_SOUTH: {
270 						uv[0] = (rx    ) * xts; uv[1] = (rz    ) * zts; // uv = (0, 0)
271 						uv[2] = (rx + 1) * xts; uv[3] = (rz    ) * zts; // uv = (1, 0)
272 						uv[4] = (rx + 1) * xts; uv[5] = (rz + 1) * zts; // uv = (1, 1)
273 						uv[6] = (rx    ) * xts; uv[7] = (rz + 1) * zts; // uv = (0, 1)
274 					} break;
275 					case FACING_NORTH: {
276 						uv[0] = (dxsize - rx    ) * xts; uv[1] = (dzsize - rz    ) * zts; // uv = (1, 1)
277 						uv[2] = (dxsize - rx - 1) * xts; uv[3] = (dzsize - rz    ) * zts; // uv = (0, 1)
278 						uv[4] = (dxsize - rx - 1) * xts; uv[5] = (dzsize - rz - 1) * zts; // uv = (0, 0)
279 						uv[6] = (dxsize - rx    ) * xts; uv[7] = (dzsize - rz - 1) * zts; // uv = (1, 0)
280 					} break;
281 
282 					case FACING_EAST: {
283 						uv[0] = 1.0f - (rz    ) * zts; uv[1] = (rx    ) * xts; // uv = (1, 0)
284 						uv[2] = 1.0f - (rz    ) * zts; uv[3] = (rx + 1) * xts; // uv = (1, 1)
285 						uv[4] = 1.0f - (rz + 1) * zts; uv[5] = (rx + 1) * xts; // uv = (0, 1)
286 						uv[6] = 1.0f - (rz + 1) * zts; uv[7] = (rx    ) * xts; // uv = (0, 0)
287 					} break;
288 					case FACING_WEST: {
289 						uv[0] = (rz    ) * zts; uv[1] = 1.0f - (rx    ) * xts; // uv = (0, 1)
290 						uv[2] = (rz    ) * zts; uv[3] = 1.0f - (rx + 1) * xts; // uv = (0, 0)
291 						uv[4] = (rz + 1) * zts; uv[5] = 1.0f - (rx + 1) * xts; // uv = (1, 0)
292 						uv[6] = (rz + 1) * zts; uv[7] = 1.0f - (rx    ) * xts; // uv = (1, 1)
293 					} break;
294 				}
295 
296 				AddQuadVertices(decal->va, px, yv, pz, uv, color);
297 			}
298 		}
299 	} else {
300 		const int num = decal->va->drawIndex() / VA_SIZE_TC;
301 		decal->va->ResetPos();
302 		VA_TYPE_TC* mem = decal->va->GetTypedVertexArray<VA_TYPE_TC>(num);
303 
304 		for (int i = 0; i < num; ++i) {
305 			const int x = int(mem[i].p.x) >> 3;
306 			const int z = int(mem[i].p.z) >> 3;
307 
308 			// update the height and alpha
309 			mem[i].p.y = hm[z * gsmx1 + x];
310 			mem[i].c   = color;
311 		}
312 
313 		decal->va->DrawArrayTC(GL_QUADS);
314 	}
315 
316 	#undef HEIGHT
317 }
318 
319 
DrawGroundScar(CGroundDecalHandler::Scar * scar,bool fade)320 inline void CGroundDecalHandler::DrawGroundScar(CGroundDecalHandler::Scar* scar, bool fade)
321 {
322 	// TODO: do we want LOS-checks for decals?
323 	if (!camera->InView(scar->pos, scar->radius + 16))
324 		return;
325 
326 	SColor color(255, 255, 255, 255);
327 
328 	if (scar->va == NULL) {
329 		scar->va = new CVertexArray();
330 		scar->va->Initialize();
331 
332 		float3 pos = scar->pos;
333 		const float radius = scar->radius;
334 		const float radius4 = radius * 4.0f;
335 		const float tx = scar->texOffsetX;
336 		const float ty = scar->texOffsetY;
337 
338 		int sx = (int) max(0.0f,                 (pos.x - radius) * 0.0625f);
339 		int ex = (int) min(float(gs->hmapx - 1), (pos.x + radius) * 0.0625f);
340 		int sz = (int) max(0.0f,                 (pos.z - radius) * 0.0625f);
341 		int ez = (int) min(float(gs->hmapy - 1), (pos.z + radius) * 0.0625f);
342 
343 		// create the scar texture-quads
344 		float px1 = sx * 16;
345 		for (int x = sx; x <= ex; ++x) {
346 			float px2 = px1 + 16;
347 			float pz1 = sz * 16;
348 
349 			for (int z = sz; z <= ez; ++z) {
350 				float pz2 = pz1 + 16;
351 				float tx1 = min(0.5f, (pos.x - px1) / radius4 + 0.25f);
352 				float tx2 = max(0.0f, (pos.x - px2) / radius4 + 0.25f);
353 				float tz1 = min(0.5f, (pos.z - pz1) / radius4 + 0.25f);
354 				float tz2 = max(0.0f, (pos.z - pz2) / radius4 + 0.25f);
355 				float h1 = CGround::GetHeightReal(px1, pz1, false);
356 				float h2 = CGround::GetHeightReal(px2, pz1, false);
357 				float h3 = CGround::GetHeightReal(px2, pz2, false);
358 				float h4 = CGround::GetHeightReal(px1, pz2, false);
359 
360 				scar->va->AddVertexTC(float3(px1, h1, pz1), tx1 + tx, tz1 + ty, color);
361 				scar->va->AddVertexTC(float3(px2, h2, pz1), tx2 + tx, tz1 + ty, color);
362 				scar->va->AddVertexTC(float3(px2, h3, pz2), tx2 + tx, tz2 + ty, color);
363 				scar->va->AddVertexTC(float3(px1, h4, pz2), tx1 + tx, tz2 + ty, color);
364 				pz1 = pz2;
365 			}
366 
367 			px1 = px2;
368 		}
369 	} else {
370 		if (fade) {
371 			if ((scar->creationTime + 10) > gs->frameNum) {
372 				color[3] = (int) (scar->startAlpha * (gs->frameNum - scar->creationTime) * 0.1f);
373 			} else {
374 				color[3] = (int) (scar->startAlpha - (gs->frameNum - scar->creationTime) * scar->alphaFalloff);
375 			}
376 
377 			const int gsmx1 = gs->mapx + 1;
378 			const float* hm = readMap->GetCornerHeightMapUnsynced();
379 
380 			const int num = scar->va->drawIndex() / VA_SIZE_TC;
381 			scar->va->ResetPos();
382 			VA_TYPE_TC* mem = scar->va->GetTypedVertexArray<VA_TYPE_TC>(num);
383 
384 			for (int i = 0; i < num; ++i) {
385 				const int x = int(mem[i].p.x) >> 3;
386 				const int z = int(mem[i].p.z) >> 3;
387 
388 				// update the height and alpha
389 				mem[i].p.y = hm[z * gsmx1 + x];
390 				mem[i].c   = color;
391 			}
392 		}
393 
394 		scar->va->DrawArrayTC(GL_QUADS);
395 	}
396 }
397 
398 
399 
GatherDecalsForType(CGroundDecalHandler::SolidObjectDecalType * decalType)400 void CGroundDecalHandler::GatherDecalsForType(CGroundDecalHandler::SolidObjectDecalType* decalType) {
401 	decalsToDraw.clear();
402 
403 	set<SolidObjectGroundDecal*>::const_iterator bgdi = decalType->objectDecals.begin();
404 
405 	while (bgdi != decalType->objectDecals.end()) {
406 		SolidObjectGroundDecal* decal = *bgdi;
407 		CSolidObject* decalOwner = decal->owner;
408 
409 		const CUnit* decalOwnerUnit = NULL;
410 		const CFeature* decalOwnerFeature = NULL;
411 
412 		// must use static_cast, not enough RTTI
413 		if (decalOwner != NULL) {
414 			if (decalOwner->GetBlockingMapID() < unitHandler->MaxUnits()) {
415 				decalOwnerUnit = static_cast<const CUnit*>(decalOwner);
416 			} else {
417 				decalOwnerFeature = static_cast<const CFeature*>(decalOwner);
418 			}
419 		}
420 
421 		if (decalOwnerUnit != NULL) {
422 			decal->alpha = std::max(0.0f, decalOwnerUnit->buildProgress);
423 		} else if (decalOwner == NULL && decal->gbOwner == NULL) {
424 			decal->alpha -= (decal->alphaFalloff * globalRendering->lastFrameTime * 0.001f * gs->speedFactor);
425 		}
426 
427 		if (decal->alpha < 0.0f) {
428 			// make sure RemoveSolidObject() won't try to modify this decal
429 			if (decalOwner != NULL) {
430 				decalOwner->groundDecal = NULL;
431 			}
432 
433 			bgdi = set_erase(decalType->objectDecals, bgdi);
434 
435 			delete decal;
436 			continue;
437 		}
438 
439 		++bgdi;
440 
441 		if (decalOwner == NULL) {
442 			decalsToDraw.push_back(decal);
443 			continue;
444 		}
445 		if (gu->spectatingFullView) {
446 			decalsToDraw.push_back(decal);
447 			continue;
448 		}
449 
450 		if (decalOwnerUnit == NULL) {
451 			assert(decalOwnerFeature != NULL);
452 
453 			if (decalOwnerFeature->IsInLosForAllyTeam(gu->myAllyTeam)) {
454 				decalsToDraw.push_back(decal);
455 			}
456 		} else {
457 			assert(decalOwnerFeature == NULL);
458 
459 			// unit is in LOS
460 			if ((decalOwnerUnit->losStatus[gu->myAllyTeam] & LOS_INLOS) != 0) {
461 				decalsToDraw.push_back(decal);
462 				continue;
463 			}
464 
465 			// unit is out of LOS
466 			// if ghosted buildings are disabled, ground plates should not
467 			// remain visible even when object itself has been seen before
468 			if (gameSetup->ghostedBuildings && (decalOwnerUnit->losStatus[gu->myAllyTeam] & LOS_PREVLOS) != 0) {
469 				decalsToDraw.push_back(decal);
470 				continue;
471 			}
472 		}
473 	}
474 }
475 
DrawObjectDecals()476 void CGroundDecalHandler::DrawObjectDecals() {
477 	// create and draw the quads for each building decal
478 	for (unsigned int n = 0; n < objectDecalTypes.size(); n++) {
479 		SolidObjectDecalType* decalType = objectDecalTypes[n];
480 
481 		if (decalType->objectDecals.empty())
482 			continue;
483 
484 		glBindTexture(GL_TEXTURE_2D, decalType->texture);
485 
486 		{
487 			GatherDecalsForType(decalType);
488 		}
489 
490 		for (unsigned int k = 0; k < decalsToDraw.size(); k++) {
491 			DrawObjectDecal(decalsToDraw[k]);
492 		}
493 
494 		// glBindTexture(GL_TEXTURE_2D, 0);
495 	}
496 }
497 
498 
499 
AddTracks()500 void CGroundDecalHandler::AddTracks() {
501 	{
502 		// Delayed addition of new tracks
503 		for (std::vector<TrackToAdd>::iterator ti = tracksToBeAdded.begin(); ti != tracksToBeAdded.end(); ++ti) {
504 			const TrackToAdd* tta = &(*ti);
505 
506 			if (tta->ts->owner == NULL) {
507 				delete tta->tp;
508 
509 				if (tta->unit == NULL)
510 					tracksToBeDeleted.push_back(tta->ts);
511 
512 				continue; // unit removed
513 			}
514 
515 			const CUnit* unit = tta->unit;
516 
517 			if (unit == NULL) {
518 				unit = tta->ts->owner;
519 				trackTypes[unit->unitDef->decalDef.trackDecalType]->tracks.insert(tta->ts);
520 			}
521 
522 			TrackPart* tp = tta->tp;
523 
524 			// if the unit is moving in a straight line only place marks at half the rate by replacing old ones
525 			bool replace = false;
526 
527 			if (unit->myTrack->parts.size() > 1) {
528 				list<TrackPart *>::iterator pi = --unit->myTrack->parts.end();
529 				list<TrackPart *>::iterator pi2 = pi--;
530 
531 				replace = (((tp->pos1 + (*pi)->pos1) * 0.5f).SqDistance((*pi2)->pos1) < 1.0f);
532 			}
533 
534 			if (replace) {
535 				delete unit->myTrack->parts.back();
536 				unit->myTrack->parts.back() = tp;
537 			} else {
538 				unit->myTrack->parts.push_back(tp);
539 			}
540 		}
541 
542 		tracksToBeAdded.clear();
543 	}
544 
545 	for (std::vector<UnitTrackStruct *>::iterator ti = tracksToBeDeleted.begin(); ti != tracksToBeDeleted.end(); ++ti) {
546 		delete *ti;
547 	}
548 
549 	tracksToBeDeleted.clear();
550 	tracksToBeCleaned.clear();
551 }
552 
DrawTracks()553 void CGroundDecalHandler::DrawTracks() {
554 	unsigned char curPartColor[4] = {255, 255, 255, 255};
555 	unsigned char nxtPartColor[4] = {255, 255, 255, 255};
556 
557 	// create and draw the unit footprint quads
558 	for (std::vector<TrackType*>::iterator tti = trackTypes.begin(); tti != trackTypes.end(); ++tti) {
559 		TrackType* tt = *tti;
560 
561 		if (tt->tracks.empty())
562 			continue;
563 
564 		set<UnitTrackStruct*>::iterator utsi = tt->tracks.begin();
565 
566 		CVertexArray* va = GetVertexArray();
567 		va->Initialize();
568 		glBindTexture(GL_TEXTURE_2D, tt->texture);
569 
570 		while (utsi != tt->tracks.end()) {
571 			UnitTrackStruct* track = *utsi;
572 			++utsi;
573 
574 			if (track->parts.empty()) {
575 				tracksToBeCleaned.push_back(TrackToClean(track, &(tt->tracks)));
576 				continue;
577 			}
578 
579 			if (gs->frameNum > (track->parts.front()->creationTime + track->lifeTime)) {
580 				tracksToBeCleaned.push_back(TrackToClean(track, &(tt->tracks)));
581 				// still draw the track to avoid flicker
582 				// continue;
583 			}
584 
585 			const auto frontPart = track->parts.front();
586 			const auto backPart = track->parts.back();
587 
588 			if (!camera->InView((frontPart->pos1 + backPart->pos1) * 0.5f, frontPart->pos1.distance(backPart->pos1) + 500.0f))
589 				continue;
590 
591 			// walk across the track parts from front (oldest) to back (newest) and draw
592 			// a quad between "connected" parts (ie. parts differing 8 sim-frames in age)
593 			list<TrackPart*>::const_iterator curPart =   (track->parts.begin());
594 			list<TrackPart*>::const_iterator nxtPart = ++(track->parts.begin());
595 
596 			curPartColor[3] = std::max(0.0f, (1.0f - (gs->frameNum - (*curPart)->creationTime) * track->alphaFalloff) * 255.0f);
597 
598 			va->EnlargeArrays(track->parts.size() * 4, 0, VA_SIZE_TC);
599 
600 			for (; nxtPart != track->parts.end(); ++nxtPart) {
601 				nxtPartColor[3] = std::max(0.0f, (1.0f - (gs->frameNum - (*nxtPart)->creationTime) * track->alphaFalloff) * 255.0f);
602 
603 				if ((*nxtPart)->connected) {
604 					va->AddVertexQTC((*curPart)->pos1, (*curPart)->texPos, 0, curPartColor);
605 					va->AddVertexQTC((*curPart)->pos2, (*curPart)->texPos, 1, curPartColor);
606 					va->AddVertexQTC((*nxtPart)->pos2, (*nxtPart)->texPos, 1, nxtPartColor);
607 					va->AddVertexQTC((*nxtPart)->pos1, (*nxtPart)->texPos, 0, nxtPartColor);
608 				}
609 
610 				curPartColor[3] = nxtPartColor[3];
611 				curPart = nxtPart;
612 			}
613 		}
614 
615 		va->DrawArrayTC(GL_QUADS);
616 	}
617 }
618 
CleanTracks()619 void CGroundDecalHandler::CleanTracks()
620 {
621 	// Cleanup old tracks
622 	for (std::vector<TrackToClean>::iterator ti = tracksToBeCleaned.begin(); ti != tracksToBeCleaned.end(); ++ti) {
623 		TrackToClean* ttc = &(*ti);
624 		UnitTrackStruct* track = ttc->track;
625 
626 		while (!track->parts.empty()) {
627 			// stop at the first part that is still too young for deletion
628 			if (gs->frameNum < (track->parts.front()->creationTime + track->lifeTime))
629 				break;
630 
631 			delete track->parts.front();
632 			track->parts.pop_front();
633 		}
634 
635 		if (track->parts.empty()) {
636 			if (track->owner != NULL) {
637 				track->owner->myTrack = NULL;
638 				track->owner = NULL;
639 			}
640 			ttc->tracks->erase(track);
641 			tracksToBeDeleted.push_back(track);
642 		}
643 	}
644 }
645 
646 
647 
AddScars()648 void CGroundDecalHandler::AddScars()
649 {
650 	scarsToBeChecked.clear();
651 
652 	{
653 		for (std::vector<Scar*>::iterator si = scarsToBeAdded.begin(); si != scarsToBeAdded.end(); ++si)
654 			scarsToBeChecked.push_back(*si);
655 
656 		scarsToBeAdded.clear();
657 	}
658 
659 	for (std::vector<Scar*>::iterator si = scarsToBeChecked.begin(); si != scarsToBeChecked.end(); ++si) {
660 		Scar* s = *si;
661 		TestOverlaps(s);
662 
663 		int x1 = s->x1 / 16;
664 		int x2 = min(scarFieldX - 1, s->x2 / 16);
665 		int y1 = s->y1 / 16;
666 		int y2 = min(scarFieldY - 1, s->y2 / 16);
667 
668 		for (int y = y1; y <= y2; ++y) {
669 			for (int x = x1; x <= x2; ++x) {
670 				std::set<Scar*>* quad = &scarField[y * scarFieldX + x];
671 				quad->insert(s);
672 			}
673 		}
674 
675 		scars.push_back(s);
676 	}
677 }
678 
DrawScars()679 void CGroundDecalHandler::DrawScars() {
680 	// create and draw the 16x16 quads for each ground scar
681 	for (std::list<Scar*>::iterator si = scars.begin(); si != scars.end(); ) {
682 		Scar* scar = *si;
683 
684 		if (scar->lifeTime < gs->frameNum) {
685 			RemoveScar(*si, false);
686 			si = scars.erase(si);
687 			continue;
688 		}
689 
690 		DrawGroundScar(scar, groundScarAlphaFade);
691 		++si;
692 	}
693 }
694 
695 
696 
697 
Draw()698 void CGroundDecalHandler::Draw()
699 {
700 	if (!drawDecals) {
701 		return;
702 	}
703 
704 	const float3 ambientColor = mapInfo->light.groundAmbientColor * CGlobalRendering::SMF_INTENSITY_MULT;
705 	const CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
706 
707 	glEnable(GL_TEXTURE_2D);
708 	glEnable(GL_BLEND);
709 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
710 	glEnable(GL_POLYGON_OFFSET_FILL);
711 	glPolygonOffset(-10, -200);
712 	glDepthMask(0);
713 
714 	glActiveTexture(GL_TEXTURE1);
715 		glEnable(GL_TEXTURE_2D);
716 		glBindTexture(GL_TEXTURE_2D, readMap->GetShadingTexture());
717 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
718 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
719 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
720 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
721 
722 		glMultiTexCoord4f(GL_TEXTURE1_ARB, 1.0f,1.0f,1.0f,1.0f); // workaround a nvidia bug with TexGen
723 		SetTexGen(1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE), 0, 0);
724 
725 	if (gd->DrawExtraTex()) {
726 		glActiveTexture(GL_TEXTURE3);
727 		glEnable(GL_TEXTURE_2D);
728 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB);
729 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
730 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
731 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE);
732 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
733 
734 		glMultiTexCoord4f(GL_TEXTURE3_ARB, 1.0f,1.0f,1.0f,1.0f); // workaround a nvidia bug with TexGen
735 		SetTexGen(1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE), 0, 0);
736 
737 		glBindTexture(GL_TEXTURE_2D, gd->GetActiveInfoTexture());
738 	}
739 
740 	if (shadowHandler->shadowsLoaded) {
741 		glActiveTexture(GL_TEXTURE2);
742 			glEnable(GL_TEXTURE_2D);
743 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
744 			glBindTexture(GL_TEXTURE_2D, shadowHandler->shadowTexture);
745 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);
746 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
747 			glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
748 
749 		decalShaders[DECAL_SHADER_CURR]->Enable();
750 
751 		if (decalShaders[DECAL_SHADER_CURR] == decalShaders[DECAL_SHADER_ARB]) {
752 			decalShaders[DECAL_SHADER_CURR]->SetUniformTarget(GL_VERTEX_PROGRAM_ARB);
753 			decalShaders[DECAL_SHADER_CURR]->SetUniform4f(10, 1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE), 0.0f, 1.0f);
754 			decalShaders[DECAL_SHADER_CURR]->SetUniformTarget(GL_FRAGMENT_PROGRAM_ARB);
755 			decalShaders[DECAL_SHADER_CURR]->SetUniform4f(10, ambientColor.x, ambientColor.y, ambientColor.z, 1.0f);
756 			decalShaders[DECAL_SHADER_CURR]->SetUniform4f(11, 0.0f, 0.0f, 0.0f, sky->GetLight()->GetGroundShadowDensity());
757 
758 			glMatrixMode(GL_MATRIX0_ARB);
759 			glLoadMatrixf(shadowHandler->shadowMatrix.m);
760 			glMatrixMode(GL_MODELVIEW);
761 		} else {
762 			decalShaders[DECAL_SHADER_CURR]->SetUniform4f(4, ambientColor.x, ambientColor.y, ambientColor.z, 1.0f);
763 			decalShaders[DECAL_SHADER_CURR]->SetUniformMatrix4fv(5, false, &shadowHandler->shadowMatrix.m[0]);
764 			decalShaders[DECAL_SHADER_CURR]->SetUniform4fv(6, &(shadowHandler->GetShadowParams().x));
765 		}
766 	}
767 
768 	glActiveTexture(GL_TEXTURE0);
769 	DrawObjectDecals();
770 
771 
772 	if (shadowHandler->shadowsLoaded) {
773 		decalShaders[DECAL_SHADER_CURR]->Disable();
774 
775 		glActiveTexture(GL_TEXTURE2);
776 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
777 			glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
778 			glDisable(GL_TEXTURE_2D);
779 		glActiveTexture(GL_TEXTURE1);
780 
781 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
782 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
783 
784 		glActiveTexture(GL_TEXTURE0);
785 	}
786 
787 
788 
789 	glPolygonOffset(-10, -20);
790 
791 	AddTracks();
792 	DrawTracks();
793 	CleanTracks();
794 
795 	glBindTexture(GL_TEXTURE_2D, scarTex);
796 	glPolygonOffset(-10, -400);
797 
798 	AddScars();
799 	DrawScars();
800 
801 	glDisable(GL_POLYGON_OFFSET_FILL);
802 	glDisable(GL_BLEND);
803 
804 	glActiveTexture(GL_TEXTURE1);
805 		glDisable(GL_TEXTURE_2D);
806 		glDisable(GL_TEXTURE_GEN_S);
807 		glDisable(GL_TEXTURE_GEN_T);
808 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
809 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
810 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE);
811 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
812 	glActiveTexture(GL_TEXTURE3); //! infotex
813 		glDisable(GL_TEXTURE_2D);
814 		glDisable(GL_TEXTURE_GEN_S);
815 		glDisable(GL_TEXTURE_GEN_T);
816 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
817 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
818 		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE);
819 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
820 	glActiveTexture(GL_TEXTURE0);
821 }
822 
823 
RenderUnitMoved(const CUnit * unit,const float3 & newpos)824 void CGroundDecalHandler::RenderUnitMoved(const CUnit* unit, const float3& newpos)
825 {
826 	if (decalLevel == 0)
827 		return;
828 
829 	AddDecalAndTrack(const_cast<CUnit*>(unit), newpos);
830 }
831 
832 
AddDecalAndTrack(CUnit * unit,const float3 & newPos)833 void CGroundDecalHandler::AddDecalAndTrack(CUnit* unit, const float3& newPos)
834 {
835 	SolidObjectDecalDef& decalDef = *const_cast<SolidObjectDecalDef*>(&unit->unitDef->decalDef);
836 
837 	if (decalDef.useGroundDecal)
838 		MoveSolidObject(const_cast<CUnit *>(unit), newPos);
839 
840 	if (!unit->leaveTracks)
841 		return;
842 
843 	if (!unit->unitDef->IsGroundUnit())
844 		return;
845 
846 	if (decalDef.trackDecalType < -1)
847 		return;
848 
849 	if (decalDef.trackDecalType < 0) {
850 		decalDef.trackDecalType = GetTrackType(decalDef.trackDecalTypeName);
851 		if (decalDef.trackDecalType < -1)
852 			return;
853 	}
854 
855 	if (unit->myTrack != NULL && unit->myTrack->lastUpdate >= (gs->frameNum - 7))
856 		return;
857 
858 	if (!((unit->losStatus[gu->myAllyTeam] & LOS_INLOS) || gu->spectatingFullView))
859 		return;
860 
861 	// calculate typemap-index
862 	const int tmz = newPos.z / (SQUARE_SIZE * 2);
863 	const int tmx = newPos.x / (SQUARE_SIZE * 2);
864 	const int tmi = Clamp(tmz * gs->hmapx + tmx, 0, gs->hmapx * gs->hmapy - 1);
865 
866 	const unsigned char* typeMap = readMap->GetTypeMapSynced();
867 	const CMapInfo::TerrainType& terType = mapInfo->terrainTypes[ typeMap[tmi] ];
868 
869 	if (!terType.receiveTracks)
870 		return;
871 
872 	const float trackLifeTime = GAME_SPEED * decalLevel * decalDef.trackDecalStrength;
873 
874 	if (trackLifeTime <= 0.0f)
875 		return;
876 
877 	const float3 pos = newPos + unit->frontdir * decalDef.trackDecalOffset;
878 
879 	TrackPart* tp = new TrackPart();
880 	tp->pos1 = pos + unit->rightdir * decalDef.trackDecalWidth * 0.5f;
881 	tp->pos2 = pos - unit->rightdir * decalDef.trackDecalWidth * 0.5f;
882 	tp->pos1.y = CGround::GetHeightReal(tp->pos1.x, tp->pos1.z, false);
883 	tp->pos2.y = CGround::GetHeightReal(tp->pos2.x, tp->pos2.z, false);
884 	tp->creationTime = gs->frameNum;
885 
886 	TrackToAdd tta;
887 	tta.tp = tp;
888 	tta.unit = unit;
889 
890 	if (unit->myTrack == NULL) {
891 		unit->myTrack = new UnitTrackStruct(unit);
892 		unit->myTrack->lifeTime = trackLifeTime;
893 		unit->myTrack->alphaFalloff = 1.0f / trackLifeTime;
894 
895 		tta.unit = NULL; // signal new trackstruct
896 
897 		tp->texPos = 0;
898 		tp->connected = false;
899 	} else {
900 		const TrackPart* prevPart = unit->myTrack->lastAdded;
901 
902 		tp->texPos = prevPart->texPos + (tp->pos1.distance(prevPart->pos1) / decalDef.trackDecalWidth) * decalDef.trackDecalStretch;
903 		tp->connected = (prevPart->creationTime == (gs->frameNum - 8));
904 	}
905 
906 	unit->myTrack->lastUpdate = gs->frameNum;
907 	unit->myTrack->lastAdded = tp;
908 
909 	tta.ts = unit->myTrack;
910 	tracksToBeAdded.push_back(tta);
911 }
912 
913 
GetTrackType(const std::string & name)914 int CGroundDecalHandler::GetTrackType(const std::string& name)
915 {
916 	if (decalLevel == 0) {
917 		return -2;
918 	}
919 
920 	const std::string lowerName = StringToLower(name);
921 
922 	int a = 0;
923 	std::vector<TrackType*>::iterator ti;
924 	for(ti = trackTypes.begin(); ti != trackTypes.end(); ++ti) {
925 		if ((*ti)->name == lowerName) {
926 			return a;
927 		}
928 		++a;
929 	}
930 
931 	TrackType* tt = new TrackType;
932 	tt->name = lowerName;
933 	tt->texture = LoadTexture(lowerName);
934 
935 	trackTypes.push_back(tt);
936 
937 	return (trackTypes.size() - 1);
938 }
939 
940 
LoadTexture(const std::string & name)941 unsigned int CGroundDecalHandler::LoadTexture(const std::string& name)
942 {
943 	std::string fullName = name;
944 	if (fullName.find_first_of('.') == string::npos) {
945 		fullName += ".bmp";
946 	}
947 	if ((fullName.find_first_of('\\') == string::npos) &&
948 	    (fullName.find_first_of('/')  == string::npos)) {
949 		fullName = string("bitmaps/tracks/") + fullName;
950 	}
951 
952 	CBitmap bm;
953 	if (!bm.Load(fullName)) {
954 		throw content_error("Could not load ground decal from file " + fullName);
955 	}
956 	if (FileSystem::GetExtension(fullName) == "bmp") {
957 		//! bitmaps don't have an alpha channel
958 		//! so use: red := brightness & green := alpha
959 		for (int y = 0; y < bm.ysize; ++y) {
960 			for (int x = 0; x < bm.xsize; ++x) {
961 				const int index = ((y * bm.xsize) + x) * 4;
962 				bm.mem[index + 3]    = bm.mem[index + 1];
963 				const int brightness = bm.mem[index + 0];
964 				bm.mem[index + 0] = (brightness * 90) / 255;
965 				bm.mem[index + 1] = (brightness * 60) / 255;
966 				bm.mem[index + 2] = (brightness * 30) / 255;
967 			}
968 		}
969 	}
970 
971 	return bm.CreateTexture(true);
972 }
973 
974 
AddExplosion(float3 pos,float damage,float radius,bool addScar)975 void CGroundDecalHandler::AddExplosion(float3 pos, float damage, float radius, bool addScar)
976 {
977 	if (decalLevel == 0 || !addScar)
978 		return;
979 
980 	const float altitude = pos.y - CGround::GetHeightReal(pos.x, pos.z, false);
981 
982 	// no decals for below-ground explosions
983 	if (altitude <= -1.0f)
984 		return;
985 	if (altitude >= radius)
986 		return;
987 
988 	pos.y -= altitude;
989 	radius -= altitude;
990 
991 	if (radius < 5.0f)
992 		return;
993 
994 	damage = std::min(damage, radius * 30.0f);
995 	damage *= (radius / (radius + altitude));
996 	radius = std::min(radius, damage * 0.25f);
997 
998 	if (damage > 400.0f)
999 		damage = 400.0f + math::sqrt(damage - 399.0f);
1000 
1001 	const int ttl = std::max(1.0f, decalLevel * damage * 3.0f);
1002 
1003 	Scar* s = new Scar();
1004 	s->pos = pos.cClampInBounds();
1005 	s->radius = radius * 1.4f;
1006 	s->creationTime = gs->frameNum;
1007 	s->startAlpha = std::max(50.0f, std::min(255.0f, damage));
1008 	s->lifeTime = int(gs->frameNum + ttl);
1009 	s->alphaFalloff = s->startAlpha / ttl;
1010 	// atlas contains 2x2 textures, pick one of them
1011 	s->texOffsetX = (gu->RandInt() & 128)? 0: 0.5f;
1012 	s->texOffsetY = (gu->RandInt() & 128)? 0: 0.5f;
1013 
1014 	s->x1 = int(std::max(0.f,                  (s->pos.x - radius) / (SQUARE_SIZE * 2)    ));
1015 	s->x2 = int(std::min(float(gs->hmapx - 1), (s->pos.x + radius) / (SQUARE_SIZE * 2) + 1));
1016 	s->y1 = int(std::max(0.f,                  (s->pos.z - radius) / (SQUARE_SIZE * 2)    ));
1017 	s->y2 = int(std::min(float(gs->hmapy - 1), (s->pos.z + radius) / (SQUARE_SIZE * 2) + 1));
1018 
1019 	s->basesize = (s->x2 - s->x1) * (s->y2 - s->y1);
1020 	s->overdrawn = 0;
1021 	s->lastTest = 0;
1022 
1023 	scarsToBeAdded.push_back(s);
1024 }
1025 
1026 
LoadScar(const std::string & file,unsigned char * buf,int xoffset,int yoffset)1027 void CGroundDecalHandler::LoadScar(const std::string& file, unsigned char* buf,
1028                                    int xoffset, int yoffset)
1029 {
1030 	CBitmap bm;
1031 	if (!bm.Load(file)) {
1032 		throw content_error("Could not load scar from file " + file);
1033 	}
1034 
1035 	if (FileSystem::GetExtension(file) == "bmp") {
1036 		//! bitmaps don't have an alpha channel
1037 		//! so use: red := brightness & green := alpha
1038 		for (int y = 0; y < bm.ysize; ++y) {
1039 			for (int x = 0; x < bm.xsize; ++x) {
1040 				const int memIndex = ((y * bm.xsize) + x) * 4;
1041 				const int bufIndex = (((y + yoffset) * 512) + x + xoffset) * 4;
1042 				buf[bufIndex + 3]    = bm.mem[memIndex + 1];
1043 				const int brightness = bm.mem[memIndex + 0];
1044 				buf[bufIndex + 0] = (brightness * 90) / 255;
1045 				buf[bufIndex + 1] = (brightness * 60) / 255;
1046 				buf[bufIndex + 2] = (brightness * 30) / 255;
1047 			}
1048 		}
1049 	} else {
1050 		for (int y = 0; y < bm.ysize; ++y) {
1051 			for (int x = 0; x < bm.xsize; ++x) {
1052 				const int memIndex = ((y * bm.xsize) + x) * 4;
1053 				const int bufIndex = (((y + yoffset) * 512) + x + xoffset) * 4;
1054 				buf[bufIndex + 0]    = bm.mem[memIndex + 0];
1055 				buf[bufIndex + 1]    = bm.mem[memIndex + 1];
1056 				buf[bufIndex + 2]    = bm.mem[memIndex + 2];
1057 				buf[bufIndex + 3]    = bm.mem[memIndex + 3];
1058 			}
1059 		}
1060 	}
1061 }
1062 
1063 
OverlapSize(Scar * s1,Scar * s2)1064 int CGroundDecalHandler::OverlapSize(Scar* s1, Scar* s2)
1065 {
1066 	if(s1->x1>=s2->x2 || s1->x2<=s2->x1)
1067 		return 0;
1068 	if(s1->y1>=s2->y2 || s1->y2<=s2->y1)
1069 		return 0;
1070 
1071 	int xs;
1072 	if(s1->x1<s2->x1)
1073 		xs=s1->x2-s2->x1;
1074 	else
1075 		xs=s2->x2-s1->x1;
1076 
1077 	int ys;
1078 	if(s1->y1<s2->y1)
1079 		ys=s1->y2-s2->y1;
1080 	else
1081 		ys=s2->y2-s1->y1;
1082 
1083 	return xs*ys;
1084 }
1085 
1086 
TestOverlaps(Scar * scar)1087 void CGroundDecalHandler::TestOverlaps(Scar* scar)
1088 {
1089 	int x1=scar->x1/16;
1090 	int x2=min(scarFieldX-1,scar->x2/16);
1091 	int y1=scar->y1/16;
1092 	int y2=min(scarFieldY-1,scar->y2/16);
1093 
1094 	++lastTest;
1095 
1096 	for(int y=y1;y<=y2;++y){
1097 		for(int x=x1;x<=x2;++x){
1098 			std::set<Scar*>* quad=&scarField[y*scarFieldX+x];
1099 			bool redoScan=false;
1100 			do {
1101 				redoScan=false;
1102 				for(std::set<Scar*>::iterator si=quad->begin();si!=quad->end();++si){
1103 					if(lastTest!=(*si)->lastTest && scar->lifeTime>=(*si)->lifeTime){
1104 						Scar* tested=*si;
1105 						tested->lastTest=lastTest;
1106 						int overlap=OverlapSize(scar,tested);
1107 						if(overlap>0 && tested->basesize>0){
1108 							float part=overlap/tested->basesize;
1109 							tested->overdrawn+=part;
1110 							if(tested->overdrawn>maxOverlap){
1111 								RemoveScar(tested,true);
1112 								redoScan=true;
1113 								break;
1114 							}
1115 						}
1116 					}
1117 				}
1118 			} while(redoScan);
1119 		}
1120 	}
1121 }
1122 
1123 
RemoveScar(Scar * scar,bool removeFromScars)1124 void CGroundDecalHandler::RemoveScar(Scar* scar, bool removeFromScars)
1125 {
1126 	int x1 = scar->x1 / 16;
1127 	int x2 = min(scarFieldX - 1, scar->x2 / 16);
1128 	int y1 = scar->y1 / 16;
1129 	int y2 = min(scarFieldY - 1, scar->y2 / 16);
1130 
1131 	for (int y = y1;y <= y2; ++y) {
1132 		for (int x = x1; x <= x2; ++x) {
1133 			std::set<Scar*>* quad = &scarField[y * scarFieldX + x];
1134 			quad->erase(scar);
1135 		}
1136 	}
1137 
1138 	if (removeFromScars)
1139 		scars.remove(scar);
1140 
1141 	delete scar;
1142 }
1143 
1144 
MoveSolidObject(CSolidObject * object,const float3 & pos)1145 void CGroundDecalHandler::MoveSolidObject(CSolidObject* object, const float3& pos)
1146 {
1147 	if (decalLevel == 0)
1148 		return;
1149 
1150 	SolidObjectDecalDef& decalDef = *const_cast<SolidObjectDecalDef*>(&object->objectDef->decalDef);
1151 	if (!decalDef.useGroundDecal || decalDef.groundDecalType < -1)
1152 		return;
1153 
1154 	if (decalDef.groundDecalType < 0) {
1155 		decalDef.groundDecalType = GetSolidObjectDecalType(decalDef.groundDecalTypeName);
1156 		if (!decalDef.useGroundDecal || decalDef.groundDecalType < -1)
1157 			return;
1158 	}
1159 
1160 	SolidObjectGroundDecal* olddecal = object->groundDecal;
1161 	if (olddecal != NULL) {
1162 		olddecal->owner = NULL;
1163 		olddecal->gbOwner = NULL;
1164 	}
1165 
1166 	const int sizex = decalDef.groundDecalSizeX;
1167 	const int sizey = decalDef.groundDecalSizeY;
1168 
1169 	SolidObjectGroundDecal* decal = new SolidObjectGroundDecal();
1170 
1171 	decal->owner = object;
1172 	decal->gbOwner = 0;
1173 	decal->alphaFalloff = decalDef.groundDecalDecaySpeed;
1174 	decal->alpha = 0.0f;
1175 	decal->pos = pos;
1176 	decal->radius = math::sqrtf(float(sizex * sizex + sizey * sizey)) * SQUARE_SIZE + 20.0f;
1177 	decal->facing = object->buildFacing;
1178 	// convert to heightmap coors
1179 	decal->xsize = sizex << 1;
1180 	decal->ysize = sizey << 1;
1181 
1182 	if (object->buildFacing == FACING_EAST || object->buildFacing == FACING_WEST) {
1183 		// swap xsize and ysize if object faces East or West
1184 		std::swap(decal->xsize, decal->ysize);
1185 	}
1186 
1187 	// position of top-left corner
1188 	decal->posx = (pos.x / SQUARE_SIZE) - (decal->xsize >> 1);
1189 	decal->posy = (pos.z / SQUARE_SIZE) - (decal->ysize >> 1);
1190 
1191 	object->groundDecal = decal;
1192 	objectDecalTypes[decalDef.groundDecalType]->objectDecals.insert(decal);
1193 }
1194 
1195 
RemoveSolidObject(CSolidObject * object,GhostSolidObject * gb)1196 void CGroundDecalHandler::RemoveSolidObject(CSolidObject* object, GhostSolidObject* gb)
1197 {
1198 	if (decalLevel == 0)
1199 		return;
1200 
1201 	assert(object);
1202 	SolidObjectGroundDecal* decal = object->groundDecal;
1203 
1204 	if (decal == NULL)
1205 		return;
1206 
1207 	if (gb != NULL)
1208 		gb->decal = decal;
1209 
1210 	decal->owner = NULL;
1211 	decal->gbOwner = gb;
1212 	object->groundDecal = NULL;
1213 }
1214 
1215 
1216 /**
1217  * @brief immediately remove an object's ground decal, if any (without fade out)
1218  */
ForceRemoveSolidObject(CSolidObject * object)1219 void CGroundDecalHandler::ForceRemoveSolidObject(CSolidObject* object)
1220 {
1221 	if (decalLevel == 0)
1222 		return;
1223 
1224 	SolidObjectGroundDecal* decal = object->groundDecal;
1225 
1226 	if (decal == NULL)
1227 		return;
1228 
1229 	decal->owner = NULL;
1230 	decal->alpha = 0.0f;
1231 	object->groundDecal = NULL;
1232 }
1233 
1234 
GetSolidObjectDecalType(const std::string & name)1235 int CGroundDecalHandler::GetSolidObjectDecalType(const std::string& name)
1236 {
1237 	if (decalLevel == 0)
1238 		return -2;
1239 
1240 	const std::string& lowerName = StringToLower(name);
1241 	const std::string& fullName = "unittextures/" + lowerName;
1242 
1243 	int decalType = 0;
1244 
1245 	std::vector<SolidObjectDecalType*>::iterator bi;
1246 	for (bi = objectDecalTypes.begin(); bi != objectDecalTypes.end(); ++bi) {
1247 		if ((*bi)->name == lowerName) {
1248 			return decalType;
1249 		}
1250 		++decalType;
1251 	}
1252 
1253 	CBitmap bm;
1254 	if (!bm.Load(fullName)) {
1255 		LOG_L(L_ERROR, "[%s] Could not load object-decal from file \"%s\"", __FUNCTION__, fullName.c_str());
1256 		return -2;
1257 	}
1258 
1259 	SolidObjectDecalType* tt = new SolidObjectDecalType();
1260 	tt->name = lowerName;
1261 	tt->texture = bm.CreateTexture(true);
1262 
1263 	objectDecalTypes.push_back(tt);
1264 	return (objectDecalTypes.size() - 1);
1265 }
1266 
GhostCreated(CSolidObject * object,GhostSolidObject * gb)1267 void CGroundDecalHandler::GhostCreated(CSolidObject* object, GhostSolidObject* gb) {
1268 	if (object->objectDef->decalDef.useGroundDecal)
1269 		RemoveSolidObject(object, gb);
1270 }
1271 
GhostDestroyed(GhostSolidObject * gb)1272 void CGroundDecalHandler::GhostDestroyed(GhostSolidObject* gb) {
1273 	if (gb->decal)
1274 		gb->decal->gbOwner = NULL;
1275 }
1276 
1277 
~SolidObjectGroundDecal()1278 SolidObjectGroundDecal::~SolidObjectGroundDecal() {
1279 	SafeDelete(va);
1280 }
1281 
~Scar()1282 CGroundDecalHandler::Scar::~Scar() {
1283 	SafeDelete(va);
1284 }
1285 
ExplosionOccurred(const CExplosionEvent & event)1286 void CGroundDecalHandler::ExplosionOccurred(const CExplosionEvent& event) {
1287 	AddExplosion(event.GetPos(), event.GetDamage(), event.GetRadius(), ((event.GetWeaponDef() != NULL) && event.GetWeaponDef()->visuals.explosionScar));
1288 }
1289 
RenderUnitCreated(const CUnit * unit,int cloaked)1290 void CGroundDecalHandler::RenderUnitCreated(const CUnit* unit, int cloaked) {
1291 	if (unit->unitDef->decalDef.useGroundDecal)
1292 		MoveSolidObject(const_cast<CUnit*>(unit), unit->pos);
1293 }
1294 
RenderUnitDestroyed(const CUnit * unit)1295 void CGroundDecalHandler::RenderUnitDestroyed(const CUnit* unit) {
1296 	if (decalLevel == 0)
1297 		return;
1298 
1299 	CUnit* u = const_cast<CUnit*>(unit);
1300 	RemoveSolidObject(u, NULL);
1301 
1302 	if (unit->myTrack != NULL) {
1303 		u->myTrack->owner = NULL;
1304 		u->myTrack = NULL;
1305 	}
1306 }
1307 
RenderFeatureCreated(const CFeature * feature)1308 void CGroundDecalHandler::RenderFeatureCreated(const CFeature* feature)
1309 {
1310 	if (feature->objectDef->decalDef.useGroundDecal)
1311 		MoveSolidObject(const_cast<CFeature*>(feature), feature->pos);
1312 }
1313 
RenderFeatureMoved(const CFeature * feature,const float3 & oldpos,const float3 & newpos)1314 void CGroundDecalHandler::RenderFeatureMoved(const CFeature* feature, const float3& oldpos, const float3& newpos) {
1315 	if (feature->objectDef->decalDef.useGroundDecal && (feature->def->drawType == DRAWTYPE_MODEL))
1316 		MoveSolidObject(const_cast<CFeature *>(feature), newpos);
1317 }
1318 
UnitLoaded(const CUnit * unit,const CUnit * transport)1319 void CGroundDecalHandler::UnitLoaded(const CUnit* unit, const CUnit* transport) {
1320 	if (unit->unitDef->decalDef.useGroundDecal)
1321 		RemoveSolidObject(const_cast<CUnit *>(unit), NULL); // FIXME: Add a RenderUnitLoaded event
1322 }
1323 
UnitUnloaded(const CUnit * unit,const CUnit * transport)1324 void CGroundDecalHandler::UnitUnloaded(const CUnit* unit, const CUnit* transport) {
1325 	if (unit->unitDef->decalDef.useGroundDecal)
1326 		MoveSolidObject(const_cast<CUnit *>(unit), unit->pos); // FIXME: Add a RenderUnitUnloaded event
1327 }
1328