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