1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include "Game/Camera.h"
5 #include "Game/GameHelper.h"
6 #include "Game/GlobalUnsynced.h"
7 #include "Map/Ground.h"
8 #include "Rendering/GlobalRendering.h"
9 #include "Rendering/GL/VertexArray.h"
10 #include "Rendering/Textures/TextureAtlas.h"
11 #include "Rendering/Colors.h"
12 #include "Rendering/ProjectileDrawer.h"
13 #include "Rendering/UnitDrawer.h"
14 #include "Rendering/Models/IModelParser.h"
15 #include "Rendering/Models/3DOParser.h"
16 #include "Rendering/Models/S3OParser.h"
17 #include "Sim/Misc/GlobalSynced.h"
18 #include "Sim/Projectiles/PieceProjectile.h"
19 #include "Sim/Projectiles/ProjectileHandler.h"
20 #include "Sim/Projectiles/Unsynced/SmokeTrailProjectile.h"
21 #include "Sim/Units/Unit.h"
22 #include "Sim/Units/UnitDef.h"
23 #include "System/Matrix44f.h"
24 #include "System/myMath.h"
25 #include "System/Sync/SyncTracer.h"
26 #include "System/Util.h"
27
28 #define SMOKE_TIME 40
29 #define NUM_TRAIL_PARTS (sizeof(fireTrailPoints) / sizeof(fireTrailPoints[0]))
30
31 CR_BIND_DERIVED(CPieceProjectile, CProjectile, (NULL, NULL, ZeroVector, ZeroVector, 0, 0))
32
33 CR_REG_METADATA(CPieceProjectile,(
34 CR_SETFLAG(CF_Synced),
35
36 CR_MEMBER(age),
37 CR_MEMBER(explFlags),
38 CR_MEMBER(dispList),
39 // NOTE: what about this?
40 // CR_MEMBER(omp),
41 CR_MEMBER(curCallback),
42 CR_MEMBER(spinVec),
43 CR_MEMBER(spinSpeed),
44 CR_MEMBER(spinAngle),
45 CR_MEMBER(oldSmokePos),
46 CR_MEMBER(oldSmokeDir),
47 CR_MEMBER(alphaThreshold),
48 // CR_MEMBER(target),
49 CR_MEMBER(drawTrail),
50
51 CR_RESERVED(36)
52 ))
53
CPieceProjectile(CUnit * owner,LocalModelPiece * lmp,const float3 & pos,const float3 & speed,int flags,float radius)54 CPieceProjectile::CPieceProjectile(
55 CUnit* owner,
56 LocalModelPiece* lmp,
57 const float3& pos,
58 const float3& speed,
59 int flags,
60 float radius
61 ):
62 CProjectile(pos, speed, owner, true, false, true),
63
64 age(0),
65 explFlags(flags),
66 dispList((lmp != NULL)? lmp->dispListID: 0),
67
68 omp(NULL),
69 curCallback(NULL),
70
71 spinSpeed(0.0f),
72 spinAngle(0.0f),
73 alphaThreshold(0.1f),
74
75 oldSmokePos(pos),
76 oldSmokeDir(FwdVector),
77
78 drawTrail(true)
79 {
80 checkCol = false;
81
82 if (owner != NULL) {
83 if ((explFlags & PF_NoCEGTrail) == 0) {
84 explGenHandler->GenExplosion((cegID = owner->unitDef->GetPieceExplosionGeneratorID(gs->randInt())), pos, speed, 100, 0.0f, 0.0f, NULL, NULL);
85 }
86
87 model = owner->model;
88 alphaThreshold = owner->alphaThreshold;
89 explFlags |= (PF_NoCEGTrail * (cegID == -1u));
90 }
91
92 if (lmp) {
93 omp = lmp->original;
94 } else {
95 omp = NULL;
96 }
97
98 castShadow = true;
99
100 if (pos.y - CGround::GetApproximateHeight(pos.x, pos.z) > 10) {
101 useAirLos = true;
102 }
103
104 oldSmokeDir = speed;
105 oldSmokeDir.Normalize();
106
107
108 const float3 camVec = pos - camera->GetPos();
109 const float camDst = camVec.Length() + 0.01f;
110 const float3 camDir = camVec / camDst;
111
112 drawTrail = ((camDst + (1.0f - math::fabs(camDir.dot(oldSmokeDir))) * 3000.0f) >= 200.0f);
113
114 // neither spinVec nor spinSpeed technically
115 // needs to be synced, but since instances of
116 // this class are themselves synced and have
117 // LuaSynced{Ctrl, Read} exposure we treat
118 // them that way for consistency
119 spinVec = gs->randVector();
120 spinVec.Normalize();
121 spinSpeed = gs->randFloat() * 20;
122
123 for (unsigned int a = 0; a < NUM_TRAIL_PARTS; ++a) {
124 fireTrailPoints[a] = new FireTrailPoint();
125 fireTrailPoints[a]->pos = pos;
126 fireTrailPoints[a]->size = gu->RandFloat() * 2 + 2;
127 }
128
129 SetRadiusAndHeight(radius, 0.0f);
130 drawRadius = 32.0f;
131
132 #ifdef TRACE_SYNC
133 tracefile << "New CPieceProjectile: ";
134 tracefile << pos.x << " " << pos.y << " " << pos.z << " " << speed.x << " " << speed.y << " " << speed.z << "\n";
135 #endif
136
137 projectileHandler->AddProjectile(this);
138 }
139
Detach()140 void CPieceProjectile::Detach()
141 {
142 // SYNCED
143 if (curCallback) {
144 // this is unsynced, but it prevents some callback crash on exit
145 curCallback->drawCallbacker = NULL;
146 }
147
148 CProjectile::Detach();
149 }
150
~CPieceProjectile()151 CPieceProjectile::~CPieceProjectile()
152 {
153 // UNSYNCED
154 for (unsigned int a = 0; a < NUM_TRAIL_PARTS; ++a) {
155 delete fireTrailPoints[a];
156 }
157 }
158
159
Collision()160 void CPieceProjectile::Collision()
161 {
162 const float3& norm = CGround::GetNormal(pos.x, pos.z);
163 const float ns = speed.dot(norm);
164
165 SetVelocityAndSpeed(speed - (norm * ns * 1.6f));
166 SetPosition(pos + (norm * 0.1f));
167
168 if (explFlags & PF_Explode) {
169 const DamageArray damageArray(50.0f);
170 const CGameHelper::ExplosionParams params = {
171 pos,
172 ZeroVector,
173 damageArray,
174 NULL, // weaponDef
175 owner(),
176 NULL, // hitUnit
177 NULL, // hitFeature
178 5.0f, // craterAreaOfEffect
179 5.0f, // damageAreaOfEffect
180 0.0f, // edgeEffectiveness
181 10.0f, // explosionSpeed
182 1.0f, // gfxMod
183 false, // impactOnly
184 false, // ignoreOwner
185 true, // damageGround
186 static_cast<unsigned int>(id)
187 };
188
189 helper->Explosion(params);
190 }
191 if (explFlags & PF_Smoke) {
192 if (explFlags & PF_NoCEGTrail) {
193 CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(
194 owner(),
195 pos, oldSmokePos,
196 dir, oldSmokeDir,
197 false,
198 true,
199 (NUM_TRAIL_PARTS - 1),
200 SMOKE_TIME,
201 0.5f,
202 drawTrail,
203 NULL,
204 projectileDrawer->smoketrailtex);
205
206 tp->creationTime += (NUM_TRAIL_PARTS - (age & (NUM_TRAIL_PARTS - 1)));
207 }
208 }
209
210 // keep flying with some small probability
211 if (gs->randFloat() < 0.666f) {
212 CProjectile::Collision();
213 }
214
215 oldSmokePos = pos;
216 }
217
Collision(CUnit * unit)218 void CPieceProjectile::Collision(CUnit* unit)
219 {
220 if (unit == owner())
221 return;
222
223 if (explFlags & PF_Explode) {
224 const DamageArray damageArray(50.0f);
225 const CGameHelper::ExplosionParams params = {
226 pos,
227 ZeroVector,
228 damageArray,
229 NULL, // weaponDef
230 owner(),
231 unit, // hitUnit
232 NULL, // hitFeature
233 5.0f, // craterAreaOfEffect
234 5.0f, // damageAreaOfEffect
235 0.0f, // edgeEffectiveness
236 10.0f, // explosionSpeed
237 1.0f, // gfxMod
238 false, // impactOnly
239 false, // ignoreOwner
240 true, // damageGround
241 static_cast<unsigned int>(id)
242 };
243
244 helper->Explosion(params);
245 }
246 if (explFlags & PF_Smoke) {
247 if (explFlags & PF_NoCEGTrail) {
248 CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(
249 owner(),
250 pos, oldSmokePos,
251 dir, oldSmokeDir,
252 false,
253 true,
254 NUM_TRAIL_PARTS - 1,
255 SMOKE_TIME,
256 0.5f,
257 drawTrail,
258 NULL,
259 projectileDrawer->smoketrailtex
260 );
261
262 tp->creationTime += (NUM_TRAIL_PARTS - (age & (NUM_TRAIL_PARTS - 1)));
263 }
264 }
265
266 CProjectile::Collision(unit);
267 oldSmokePos = pos;
268 }
269
270
HasVertices() const271 bool CPieceProjectile::HasVertices() const
272 {
273 if (omp == NULL)
274 return false;
275
276 return (omp->GetVertexCount() > 0);
277 }
278
RandomVertexPos()279 float3 CPieceProjectile::RandomVertexPos()
280 {
281 if (!HasVertices())
282 return ZeroVector;
283
284 const int vertexNum = (int) (gu->RandFloat() * 0.99f * omp->GetVertexCount());
285 const float3& pos = omp->GetVertexPos(vertexNum);
286
287 return pos;
288 }
289
290
Update()291 void CPieceProjectile::Update()
292 {
293 if (!luaMoveCtrl) {
294 if (explFlags & PF_Fall)
295 speed.y += mygravity;
296
297 SetVelocityAndSpeed(speed * 0.997f);
298 SetPosition(pos + speed);
299 }
300
301 spinAngle += spinSpeed;
302 age += 1;
303 checkCol |= (age > 10);
304
305 if ((explFlags & PF_Fire) != 0 && HasVertices()) {
306 FireTrailPoint* tempTrailPoint = fireTrailPoints[NUM_TRAIL_PARTS - 1];
307
308 for (int a = NUM_TRAIL_PARTS - 2; a >= 0; --a) {
309 fireTrailPoints[a + 1] = fireTrailPoints[a];
310 }
311
312 CMatrix44f m(pos);
313 m.Rotate(spinAngle * PI / 180.0f, spinVec);
314 m.Translate(RandomVertexPos());
315
316 fireTrailPoints[0] = tempTrailPoint;
317 fireTrailPoints[0]->pos = m.GetPos();
318 fireTrailPoints[0]->size = gu->RandFloat() * 1 + 1;
319 }
320
321 if ((explFlags & PF_NoCEGTrail) == 0) {
322 // TODO: pass a more sensible ttl to the CEG (age-related?)
323 explGenHandler->GenExplosion(cegID, pos, speed, 100, 0.0f, 0.0f, NULL, NULL);
324 return;
325 }
326
327 if ((explFlags & PF_Smoke) != 0) {
328 if ((age & (NUM_TRAIL_PARTS - 1)) == 0) {
329 if (curCallback != NULL) {
330 curCallback->drawCallbacker = NULL;
331 }
332
333 curCallback = new CSmokeTrailProjectile(
334 owner(),
335 pos, oldSmokePos,
336 dir, oldSmokeDir,
337 age == (NUM_TRAIL_PARTS - 1),
338 false,
339 14,
340 SMOKE_TIME,
341 0.5f,
342 drawTrail,
343 this,
344 projectileDrawer->smoketrailtex
345 );
346
347 useAirLos = curCallback->useAirLos;
348
349 oldSmokePos = pos;
350 oldSmokeDir = dir;
351
352 if (!drawTrail) {
353 float3 camDir = pos - camera->GetPos();
354
355 const float camPieceDist = camDir.LengthNormalize();
356 const float camAngleScale = (1.0f - math::fabs(camDir.dot(dir))) * 3000.0f;
357
358 drawTrail = ((camPieceDist + camAngleScale) >= 300.0f);
359 }
360 }
361 }
362 }
363
364
365
Draw()366 void CPieceProjectile::Draw()
367 {
368 if ((explFlags & PF_NoCEGTrail) == 0)
369 return;
370
371 if ((explFlags & PF_Smoke) != 0) {
372 // this piece leaves a default (non-CEG) smoketrail
373 inArray = true;
374
375 const int numParts = age & (NUM_TRAIL_PARTS - 1);
376 float age2 = (age & (NUM_TRAIL_PARTS - 1)) + globalRendering->timeOffset;
377 float color = 0.5f;
378 unsigned char col[4];
379
380 va->EnlargeArrays(4+4*numParts,0,VA_SIZE_TC);
381
382 if (drawTrail) {
383 // draw the trail as a single quad if camera close enough
384 const float3 dif = (drawPos - camera->GetPos()).Normalize();
385 const float3 dir1 = (dif.cross(dir)).Normalize();
386 const float3 dif2 = (oldSmokePos - camera->GetPos()).Normalize();
387 const float3 dir2 = (dif2.cross(oldSmokeDir)).Normalize();
388
389 float a1 = ((1 - 0.0f / SMOKE_TIME) * 255) * (0.7f + math::fabs(dif.dot(dir)));
390 float alpha = std::min(255.0f, std::max(0.0f, a1));
391 col[0] = (unsigned char) (color * alpha);
392 col[1] = (unsigned char) (color * alpha);
393 col[2] = (unsigned char) (color * alpha);
394 col[3] = (unsigned char) (alpha);
395
396 unsigned char col2[4];
397 float a2 = ((1 - float(age2) / SMOKE_TIME) * 255) * (0.7f + math::fabs(dif2.dot(oldSmokeDir)));
398
399 if (age < NUM_TRAIL_PARTS)
400 a2 = 0.0f;
401
402 alpha = std::min(255.0f, std::max(0.0f, a2));
403 col2[0] = (unsigned char) (color * alpha);
404 col2[1] = (unsigned char) (color * alpha);
405 col2[2] = (unsigned char) (color * alpha);
406 col2[3] = (unsigned char) (alpha);
407
408 const float size = 1.0f;
409 const float size2 = 1 + (age2 * (1.0f / SMOKE_TIME)) * 14;
410 const float txs =
411 projectileDrawer->smoketrailtex->xstart -
412 (projectileDrawer->smoketrailtex->xend - projectileDrawer->smoketrailtex->xstart) *
413 (age2 / NUM_TRAIL_PARTS);
414
415 va->AddVertexQTC(drawPos - dir1 * size, txs, projectileDrawer->smoketrailtex->ystart, col);
416 va->AddVertexQTC(drawPos + dir1 * size, txs, projectileDrawer->smoketrailtex->yend, col);
417 va->AddVertexQTC(oldSmokePos + dir2 * size2, projectileDrawer->smoketrailtex->xend, projectileDrawer->smoketrailtex->yend, col2);
418 va->AddVertexQTC(oldSmokePos - dir2 * size2, projectileDrawer->smoketrailtex->xend, projectileDrawer->smoketrailtex->ystart, col2);
419 } else {
420 // draw the trail as particles
421 const float dist = pos.distance(oldSmokePos);
422 const float3 dirpos1 = pos - dir * dist * 0.33f;
423 const float3 dirpos2 = oldSmokePos + oldSmokeDir * dist * 0.33f;
424
425 // CAUTION: loop count must match EnlargeArrays above
426 for (int a = 0; a < numParts; ++a) {
427 float alpha = 255.0f;
428 col[0] = (unsigned char) (color * alpha);
429 col[1] = (unsigned char) (color * alpha);
430 col[2] = (unsigned char) (color * alpha);
431 col[3] = (unsigned char) (alpha);
432
433 const float size = 1.0f + ((a) * (1.0f / SMOKE_TIME)) * 14.0f;
434 const float3 pos1 = CalcBeizer(float(a) / (numParts), pos, dirpos1, dirpos2, oldSmokePos);
435
436 #define st projectileDrawer->smoketex[0]
437 va->AddVertexQTC(pos1 + ( camera->up+camera->right) * size, st->xstart, st->ystart, col);
438 va->AddVertexQTC(pos1 + ( camera->up-camera->right) * size, st->xend, st->ystart, col);
439 va->AddVertexQTC(pos1 + (-camera->up-camera->right) * size, st->xend, st->ystart, col);
440 va->AddVertexQTC(pos1 + (-camera->up+camera->right) * size, st->xstart, st->ystart, col);
441 #undef st
442 }
443 }
444 }
445
446 DrawCallback();
447 }
448
DrawOnMinimap(CVertexArray & lines,CVertexArray & points)449 void CPieceProjectile::DrawOnMinimap(CVertexArray& lines, CVertexArray& points)
450 {
451 points.AddVertexQC(pos, color4::red);
452 }
453
DrawCallback()454 void CPieceProjectile::DrawCallback()
455 {
456 inArray = true;
457
458 // fireTrailPoints contains nothing useful if piece is empty
459 if ((explFlags & PF_Fire) != 0 && HasVertices()) {
460 va->EnlargeArrays(NUM_TRAIL_PARTS * 4, 0, VA_SIZE_TC);
461
462 for (unsigned int age = 0; age < NUM_TRAIL_PARTS; ++age) {
463 const float3 interPos = fireTrailPoints[age]->pos;
464 const float size = fireTrailPoints[age]->size;
465 const float modage = age * 1.0f;
466
467 const float alpha = (7.5f - modage) * (1.0f / NUM_TRAIL_PARTS);
468 const float drawsize = (0.5f + modage) * size;
469
470 unsigned char col[4];
471
472 col[0] = (unsigned char) (255 * alpha);
473 col[1] = (unsigned char) (200 * alpha);
474 col[2] = (unsigned char) (150 * alpha);
475 col[3] = (unsigned char) (alpha * 50);
476
477 #define eft projectileDrawer->explofadetex
478 va->AddVertexQTC(interPos - camera->right * drawsize-camera->up * drawsize, eft->xstart, eft->ystart, col);
479 va->AddVertexQTC(interPos + camera->right * drawsize-camera->up * drawsize, eft->xend, eft->ystart, col);
480 va->AddVertexQTC(interPos + camera->right * drawsize+camera->up * drawsize, eft->xend, eft->yend, col);
481 va->AddVertexQTC(interPos - camera->right * drawsize+camera->up * drawsize, eft->xstart, eft->yend, col);
482 #undef eft
483 }
484 }
485 }
486