1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2011
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20
21 #include <common/Defines.h>
22 #include <common/VectorLib.h>
23 #include <graph/ParticleEmitter.h>
24 #include <graph/ParticleTypes.h>
25 #include <sprites/DebrisActionRenderer.h>
26 #include <sprites/SmokeActionRenderer.h>
27 #include <sprites/ExplosionTextures.h>
28 #include <sprites/NapalmRenderer.h>
29 #include <sprites/ExplosionNukeRenderer.h>
30 #include <sprites/WallActionRenderer.h>
31 #include <landscape/Landscape.h>
32 #include <math.h>
33 #include <stdlib.h>
34
randomVector(Vector & result,Vector & start,Vector & end)35 static void randomVector(Vector &result, Vector &start, Vector &end)
36 {
37 result[0] = start[0] + ((end[0] - start[0]) * RAND);
38 result[1] = start[1] + ((end[1] - start[1]) * RAND);
39 result[2] = start[2] + ((end[2] - start[2]) * RAND);
40 }
41
randomCounterVector(Vector & result,Vector & current,Vector & start,Vector & end,float life)42 static void randomCounterVector(Vector &result, Vector ¤t,
43 Vector &start, Vector &end,
44 float life)
45 {
46 randomVector(result, start, end);
47 result -= current;
48 result /= life;
49 }
50
randomScalar(float start,float end)51 static float randomScalar(float start, float end)
52 {
53 return start + ((end - start) * RAND);
54 }
55
randomCounterScalar(float current,float start,float end,float life)56 static float randomCounterScalar(float current,
57 float start, float end,
58 float life)
59 {
60 float result = randomScalar(start, end);
61 result -= current;
62 result /= life;
63 return result;
64 }
65
ParticleEmitter()66 ParticleEmitter::ParticleEmitter()
67 {
68 }
69
~ParticleEmitter()70 ParticleEmitter::~ParticleEmitter()
71 {
72 }
73
setLife(float life1,float life2)74 void ParticleEmitter::setLife(float life1, float life2)
75 {
76 life1_ = life1; life2_ = life2;
77 }
78
setMass(float mass1,float mass2)79 void ParticleEmitter::setMass(float mass1, float mass2)
80 {
81 mass1_ = mass1; mass2_ = mass2;
82 }
83
setFriction(float friction1,float friction2)84 void ParticleEmitter::setFriction(float friction1, float friction2)
85 {
86 friction1_ = friction1; friction2_ = friction2;
87 }
88
setStartColor(Vector startColor1,float startAlpha1,Vector startColor2,float startAlpha2)89 void ParticleEmitter::setStartColor(
90 Vector startColor1, float startAlpha1,
91 Vector startColor2, float startAlpha2)
92 {
93 startAlpha1_ = startAlpha1; endAlpha1_ = startAlpha1;
94 startAlpha2_ = startAlpha2; endAlpha2_ = startAlpha2;
95 startColor1_ = startColor1; endColor1_ = startColor1;
96 startColor2_ = startColor2; endColor2_ = startColor2;
97 }
98
setEndColor(Vector endColor1,float endAlpha1,Vector endColor2,float endAlpha2)99 void ParticleEmitter::setEndColor(
100 Vector endColor1, float endAlpha1,
101 Vector endColor2, float endAlpha2)
102 {
103 endColor1_ = endColor1; endAlpha1_ = endAlpha1;
104 endColor2_ = endColor2; endAlpha2_ = endAlpha2;
105 }
106
setVelocity(Vector velocity1,Vector velocity2)107 void ParticleEmitter::setVelocity(Vector velocity1, Vector velocity2)
108 {
109 velocity1_ = velocity1; velocity2_ = velocity2;
110 }
111
setStartSize(float startX1,float startY1,float startX2,float startY2)112 void ParticleEmitter::setStartSize(
113 float startX1, float startY1,
114 float startX2, float startY2)
115 {
116 startSize1_[0] = startX1; startSize1_[1] = startY1;
117 startSize2_[0] = startX2; startSize2_[1] = startY2;
118 }
119
setEndSize(float endX1,float endY1,float endX2,float endY2)120 void ParticleEmitter::setEndSize(
121 float endX1, float endY1,
122 float endX2, float endY2)
123 {
124 endSize1_[0] = endX1; endSize1_[1] = endY1;
125 endSize2_[0] = endX2; endSize2_[1] = endY2;
126 }
127
setGravity(Vector gravity)128 void ParticleEmitter::setGravity(Vector gravity)
129 {
130 gravity_ = gravity;
131 }
132
setAdditiveTexture(bool additiveTexture)133 void ParticleEmitter::setAdditiveTexture(bool additiveTexture)
134 {
135 additiveTexture_ = additiveTexture;
136 }
137
setWindAffect(bool windAffect)138 void ParticleEmitter::setWindAffect(bool windAffect)
139 {
140 windAffect_ = windAffect;
141 }
142
setAttributes(float life1,float life2,float mass1,float mass2,float friction1,float friction2,Vector velocity1,Vector velocity2,Vector startColor1,float startAlpha1,Vector startColor2,float startAlpha2,Vector endColor1,float endAlpha1,Vector endColor2,float endAlpha2,float startX1,float startY1,float startX2,float startY2,float endX1,float endY1,float endX2,float endY2,Vector gravity,bool additiveTexture,bool windAffect)143 void ParticleEmitter::setAttributes(
144 float life1, float life2,
145 float mass1, float mass2,
146 float friction1, float friction2,
147 Vector velocity1, Vector velocity2,
148 Vector startColor1, float startAlpha1,
149 Vector startColor2, float startAlpha2,
150 Vector endColor1, float endAlpha1,
151 Vector endColor2, float endAlpha2,
152 float startX1, float startY1,
153 float startX2, float startY2,
154 float endX1, float endY1,
155 float endX2, float endY2,
156 Vector gravity,
157 bool additiveTexture,
158 bool windAffect)
159 {
160 setLife(life1, life2);
161 setMass(mass1, mass2);
162 setFriction(friction1, friction2);
163 setVelocity(velocity1, velocity2);
164 setStartColor(startColor1, startAlpha1,
165 startColor2, startAlpha2);
166 setEndColor(endColor1, endAlpha1,
167 endColor2, endAlpha2);
168 setStartSize(startX1, startY1,
169 startX2, startY2);
170 setEndSize(endX1, endY1,
171 endX2, endY2);
172 setGravity(gravity);
173 setAdditiveTexture(additiveTexture);
174 setWindAffect(windAffect);
175 }
176
createDefaultParticle(Particle & particle)177 void ParticleEmitter::createDefaultParticle(Particle &particle)
178 {
179 float life = randomScalar(life1_, life2_);
180 float mass = randomScalar(mass1_, mass2_);
181 float friction = randomScalar(friction1_, friction2_);
182 float alpha = randomScalar(startAlpha1_, startAlpha2_);
183 float alphac = randomCounterScalar(alpha, endAlpha1_, endAlpha2_, life);
184
185 Vector velocity;
186 randomVector(velocity, velocity1_, velocity2_);
187 Vector color, colorc;
188 randomVector(color, startColor1_, startColor2_);
189 randomCounterVector(colorc, color, endColor1_, endColor2_, life);
190 Vector size, sizec;
191 randomVector(size, startSize1_, startSize2_);
192 randomCounterVector(sizec, size, endSize1_, endSize2_, life);
193
194 particle.setParticle(life, mass,
195 friction, velocity, gravity_,
196 color, colorc,
197 size, sizec,
198 alpha, alphac,
199 additiveTexture_,
200 windAffect_);
201 }
202
emitLinear(int number,Vector & position1,Vector & position2,ParticleEngine & engine,ParticleRenderer * renderer,GLTextureSet * set,bool animate)203 void ParticleEmitter::emitLinear(int number,
204 Vector &position1, Vector &position2,
205 ParticleEngine &engine,
206 ParticleRenderer *renderer,
207 GLTextureSet *set,
208 bool animate)
209 {
210 for (int i=0; i<number; i++)
211 {
212 Particle *particle = engine.getNextAliveParticle(ParticleLinear);
213 if (!particle) return;
214
215 Vector position;
216 randomVector(position, position1, position2);
217
218 createDefaultParticle(*particle);
219 particle->position_ = position;
220 particle->renderer_ = renderer;
221 particle->textureCoord_ = int(RAND * 4.0f);
222 if (animate)
223 {
224 particle->textureSet_ = set;
225 }
226 else
227 {
228 int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
229 particle->texture_ = set->getTexture(index);
230 }
231 }
232 }
233
emitExplosionRing(int number,Vector & position,Vector & inAxis,ParticleEngine & engine,float width,GLTextureSet * set,bool animate)234 void ParticleEmitter::emitExplosionRing(int number,
235 Vector &position,
236 Vector &inAxis,
237 ParticleEngine &engine,
238 float width,
239 GLTextureSet *set,
240 bool animate)
241 {
242 for (int i=0; i<number; i++)
243 {
244 Particle *particle = engine.getNextAliveParticle(ParticleRing);
245 if (!particle) return;
246
247 createDefaultParticle(*particle);
248
249 float ang = RAND * 2.0f * 3.14f;
250 float speed = width * 4.0f;
251 Vector axis = inAxis.Normalize();
252 Vector otheraxis(0.0f, 1.0f, 0.0f);
253 if (fabs(axis[1]) > 0.7f)
254 {
255 otheraxis = Vector(1.0f, 0.0f, 0.0f);
256 }
257 Vector p = axis * otheraxis;
258 Vector velocity = VectorLib::rotationAroundAxis(p, ang, axis) * speed;
259
260 particle->velocity_ = velocity;
261 particle->position_ = position;
262 particle->renderer_ = ParticleRendererQuads::getInstance();
263 particle->textureCoord_ = int(RAND * 4.0f);
264 if (animate)
265 {
266 particle->textureSet_ = set;
267 }
268 else
269 {
270 int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
271 particle->texture_ = set->getTexture(index);
272 }
273 }
274 }
275
emitDebris(int number,Vector & position,ParticleEngine & engine)276 void ParticleEmitter::emitDebris(int number,
277 Vector &position,
278 ParticleEngine &engine)
279 {
280 for (int i=0; i<number; i++)
281 {
282 Particle *particle = engine.getNextAliveParticle(ParticleDebris);
283 if (!particle) return;
284
285 createDefaultParticle(*particle);
286
287 float direction = RAND * 3.14f * 2.0f;
288 float speed = RAND * 25.0f + 5.0f;
289 float height = RAND * 25.0f + 15.0f;
290 Vector velocity(getFastSin(direction) * speed,
291 getFastCos(direction) * speed, height);
292
293 if (RAND > 0.5f)
294 {
295 DebrisActionRenderer *debris = new DebrisActionRenderer;
296 particle->velocity_ = velocity;
297 particle->position_ = position;
298 particle->renderer_ = ParticleRendererDebris::getInstance();
299 particle->userData_ = debris;
300 }
301 else
302 {
303 SmokeActionRenderer *smoke = new SmokeActionRenderer;
304 particle->velocity_ = velocity;
305 particle->position_ = position;
306 particle->renderer_ = ParticleRendererSmoke::getInstance();
307 particle->userData_ = smoke;
308 }
309 }
310 }
311
emitSmoke(int number,Vector & position,ParticleEngine & engine)312 void ParticleEmitter::emitSmoke(int number,
313 Vector &position,
314 ParticleEngine &engine)
315 {
316 for (int i=0; i<number; i++)
317 {
318 Particle *particle = engine.getNextAliveParticle(ParticleSmoke);
319 if (!particle) return;
320
321 createDefaultParticle(*particle);
322
323 particle->position_ = position;
324 particle->renderer_ = ParticleRendererQuads::getInstance();
325 particle->texture_ = &ExplosionTextures::instance()->smokeTexture;
326 particle->shadow_ = (RAND > 0.25f);
327 particle->textureCoord_ = (int) (RAND * 4.0f);
328 }
329 }
330
emitNapalm(Vector & position,ParticleEngine & engine,GLTextureSet * set)331 void ParticleEmitter::emitNapalm(
332 Vector &position,
333 ParticleEngine &engine,
334 GLTextureSet *set)
335 {
336 Particle *particle = engine.getNextAliveParticle(ParticleNapalm);
337 if (!particle) return;
338
339 createDefaultParticle(*particle);
340
341 particle->position_ = position;
342 particle->renderer_ = ParticleRendererNapalm::getInstance();
343 particle->textureCoord_ = 0;
344 particle->userData_ = new NapalmRenderer(set);
345 }
346
emitSpray(Vector & position,ParticleEngine & engine,float width,GLTexture * texture)347 void ParticleEmitter::emitSpray(
348 Vector &position,
349 ParticleEngine &engine,
350 float width,
351 GLTexture *texture)
352 {
353 for (int i=0; i<6 + int(width) * 2; i++)
354 {
355 Particle *particle = engine.getNextAliveParticle(ParticleSpray);
356 if (!particle) return;
357
358 createDefaultParticle(*particle);
359
360 float rotation = RAND * 2.0f * 3.14f;
361 float x = sinf(rotation);
362 float y = cosf(rotation);
363 Vector pos = position;
364 pos[0] += (x * width * RAND);
365 pos[1] += (y * width * RAND);
366 Vector velocity;
367 velocity[0] = (x * RAND) / 10.0f;
368 velocity[1] = (y * RAND) / 10.0f;
369 velocity[2] = 25.0f * RAND + 15.0f;
370
371 particle->texture_ = texture;
372 particle->velocity_ = velocity;
373 particle->position_ = pos;
374 particle->renderer_ = ParticleRendererQuads::getInstance();
375 particle->textureCoord_ = (int) (RAND * 4.0f);
376 }
377 }
378
emitTalk(Vector & position,ParticleEngine & engine)379 void ParticleEmitter::emitTalk(
380 Vector &position,
381 ParticleEngine &engine)
382 {
383 Particle *particle = engine.getNextAliveParticle(ParticleTalk);
384 if (!particle) return;
385
386 createDefaultParticle(*particle);
387
388 particle->position_ = position;
389 particle->renderer_ = ParticleRendererQuads::getInstance();
390 particle->textureCoord_ = 0;
391 particle->texture_ = &ExplosionTextures::instance()->talkTexture;
392 }
393
emitWallHit(Vector & position,ParticleEngine & engine,OptionsTransient::WallSide type)394 void ParticleEmitter::emitWallHit(
395 Vector &position,
396 ParticleEngine &engine,
397 OptionsTransient::WallSide type)
398 {
399 Particle *particle = engine.getNextAliveParticle(ParticleWall);
400 if (!particle) return;
401
402 createDefaultParticle(*particle);
403
404 particle->position_ = position;
405 particle->renderer_ = ParticleRendererWall::getInstance();
406 particle->userData_ = new WallActionRenderer(position, type);
407 }
408
emitTransport(Vector & position,ParticleEngine & engine,GLTextureSet * set)409 void ParticleEmitter::emitTransport(
410 Vector &position,
411 ParticleEngine &engine,
412 GLTextureSet *set)
413 {
414 Particle *particle = engine.getNextAliveParticle(ParticleTransport);
415 if (!particle) return;
416
417 Vector velocity;
418 createDefaultParticle(*particle);
419
420 particle->position_ = position;
421 particle->renderer_ = ParticleRendererQuads::getInstance();
422 particle->textureCoord_ = 0;
423 particle->textureSet_ = set;
424 }
425
emitExplosion(Vector & position,ParticleEngine & engine,float width,GLTextureSet * set,bool animate)426 void ParticleEmitter::emitExplosion(
427 Vector &position,
428 ParticleEngine &engine,
429 float width,
430 GLTextureSet *set,
431 bool animate)
432 {
433 for (int i=0; i<int(width) * 4; i++)
434 {
435 Particle *particle = engine.getNextAliveParticle(ParticleExplosion);
436 if (!particle) return;
437
438 float randRotXY = (RAND * TWOPI);
439 float randRotXZ = (RAND * TWOPI);
440 float cosRandRotXZ = (float) cos(randRotXZ);
441 Vector velocity;
442 velocity[0] = float(sin(randRotXY) * cosRandRotXZ);
443 velocity[1] = float(cos(randRotXY) * cosRandRotXZ);
444 velocity[2] = float(sin(randRotXZ));
445 velocity *= (width * 2.5f);
446
447 float size = RAND * width * 2.0f + 2.0f;
448
449 setEndSize(size, size, size, size);
450
451 createDefaultParticle(*particle);
452
453 particle->velocity_ = velocity;
454 particle->position_ = position;
455 particle->renderer_ = ParticleRendererQuads::getInstance();
456 particle->textureCoord_ = int(RAND * 4.0f);
457 if (animate)
458 {
459 particle->textureSet_ = set;
460 }
461 else
462 {
463 int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
464 particle->texture_ = set->getTexture(index);
465 }
466 }
467 }
468
emitMushroom(Vector & position,ParticleEngine & engine,int number,float width,GLTextureSet * set,bool animate)469 void ParticleEmitter::emitMushroom(
470 Vector &position,
471 ParticleEngine &engine,
472 int number,
473 float width,
474 GLTextureSet *set,
475 bool animate)
476 {
477 for (int i=0; i<number; i++)
478 {
479 Particle *particle = engine.getNextAliveParticle(ParticleMushroom);
480 if (!particle) return;
481
482 createDefaultParticle(*particle);
483
484 particle->position_ = position;
485 particle->renderer_ = ParticleRendererMushroom::getInstance();
486 particle->textureCoord_ = int(RAND * 4.0f);
487 particle->shadow_ = (RAND > 0.80f);
488 particle->userData_ = new ExplosionNukeRendererEntry(position, width);
489
490 if (animate)
491 {
492 particle->textureSet_ = set;
493 }
494 else
495 {
496 int index = MIN(int(RAND * (set->getNoTextures() - 1)), set->getNoTextures() - 1);
497 particle->texture_ = set->getTexture(index);
498 }
499 }
500 }
501
emitPrecipitation(Vector & position,ParticleEngine & engine,int number,bool rain)502 void ParticleEmitter::emitPrecipitation(
503 Vector &position,
504 ParticleEngine &engine,
505 int number,
506 bool rain)
507 {
508 for (int i=0; i<number; i++)
509 {
510 Particle *particle = engine.getNextAliveParticle(ParticlePrecipitation);
511 if (!particle) return;
512
513 createDefaultParticle(*particle);
514
515 if (rain)
516 {
517 particle->texture_ = &ExplosionTextures::instance()->rainTexture;
518 particle->renderer_ = ParticleRendererRain::getInstance();
519 }
520 else
521 {
522 particle->texture_ = &ExplosionTextures::instance()->snowTexture;
523 particle->renderer_ = ParticleRendererSnow::getInstance();
524 }
525 particle->position_[0] = position[0] + RAND * 400.0f - 200.0f;
526 particle->position_[1] = position[1] + RAND * 400.0f - 200.0f;
527 particle->position_[2] = 180.0f;
528 particle->shadow_ = false;
529 }
530 }
531