1 /*
2 * Copyright (C) 2002 Terence M. Welsh
3 * Ported to Linux by Tugrul Galatali <tugrul@galatali.com>
4 *
5 * Skyrocket is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Skyrocket is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #include <math.h>
20 #include <GL/gl.h>
21 #include <GL/glu.h>
22
23 #include <list>
24
25 #include "rsDefines.h"
26 #include "rsRand.h"
27 #include "rsMath/rsMatrix.h"
28 #include "rsMath/rsQuat.h"
29 #include "skyrocket_smoke.h"
30 #include "skyrocket_flare.h"
31 #include "skyrocket_particle.h"
32 #include "skyrocket_world.h"
33 #include "skyrocket_shockwave.h"
34 #include "skyrocket_sound.h"
35
36 // skyrocket.cpp
37 extern std::list < particle > particles;
38 extern particle *addParticle ();
39 extern int dAmbient;
40 extern int dSmoke;
41 extern int dExplosionsmoke;
42 extern int dWind;
43 extern int dFlare;
44 extern int dClouds;
45 extern int dIllumination;
46 extern int dSound;
47 extern float elapsedTime;
48 extern rsVec cameraPos; // used for positioning sounds
49
50 // skyrocket_world.cpp
51 extern float clouds[CLOUDMESH + 1][CLOUDMESH + 1][9];
52
53 // skyrocket_flare.cpp
54 extern unsigned int flarelist[4];
55
56 // skyrocket_smoke.cpp
57 extern float smokeTime[SMOKETIMES];
58 extern int whichSmoke[WHICHSMOKES];
59 extern unsigned int smokelist[5];
60
61 double billboardMat[16];
62
particle()63 particle::particle ()
64 {
65 type = STAR;
66 displayList = flarelist[0];
67 drag = 0.612f; // terminal velocity of 20 ft/s
68 t = 2.0f;
69 tr = t;
70 bright = 1.0f;
71 life = bright;
72 size = 30.0f;
73 makeSmoke = 0;
74
75 smokeTimeIndex = 0;
76 smokeTrailLength = 0.0f;
77 sparkTrailLength = 0.0f;
78 depth = 0.0f;
79 }
80
randomColor()81 rsVec particle::randomColor ()
82 {
83 int i = 0, j = 0, k = 0;
84 rsVec color;
85
86 switch (rsRandi (6)) {
87 case 0:
88 i = 0;
89 j = 1, k = 2;
90 break;
91 case 1:
92 i = 0;
93 j = 2, k = 1;
94 break;
95 case 2:
96 i = 1;
97 j = 0, k = 2;
98 break;
99 case 3:
100 i = 1;
101 j = 2, k = 0;
102 break;
103 case 4:
104 i = 2;
105 j = 0, k = 1;
106 break;
107 case 5:
108 i = 2;
109 j = 1, k = 0;
110 }
111
112 color[i] = 1.0f;
113 color[j] = rsRandf (1.0f);
114 color[k] = rsRandf (0.2f);
115
116 return (color);
117 }
118
initRocket()119 void particle::initRocket ()
120 {
121 type = ROCKET;
122 //displayList = rocketlist;
123 xyz[0] = rsRandf (200.0f) - 100.0f;
124 xyz[1] = 5.0f;
125 xyz[2] = rsRandf (200.0f) - 100.0f;
126 lastxyz[0] = xyz[0];
127 lastxyz[1] = 4.0f;
128 lastxyz[2] = xyz[2];
129 vel.set (0.0f, 60.0f, 0.0f);
130 rgb.set (rsRandf (0.7f) + 0.3f, rsRandf (0.7f) + 0.3f, 0.3f);
131 size = 1.0f;
132 drag = 0.281f; // terminal velocity of 50 ft/s
133 t = rsRandf (2.0f) + 5.0f;
134 tr = t;
135 bright = 0.0f;
136 thrust = rsRandf (100.0f) + 200.0f;
137 endthrust = rsRandf (0.1f) + 0.3f;
138 spin = rsRandf (40.0f) - 20.0f;
139 tilt = rsRandf (30.0f * float (fabs (spin)));
140
141 tiltvec.set (cos (spin), 0.0f, sin (spin));
142 if (!rsRandi (200)) { // crash the occasional rocket
143 spin = 0.0f;
144 tilt = rsRandf (100.0f) + 75.0f;
145 float temp = rsRandf (PIx2);
146
147 tiltvec.set (cos (temp), 0.0f, sin (temp));
148 }
149
150 makeSmoke = 1;
151 smokeTrailLength = 0.0f;
152 sparkTrailLength = 0.0f;
153 explosiontype = 0;
154
155 #ifdef HAVE_OPENAL
156 if (dSound) {
157 if (rsRandi (2))
158 insertSoundNode (LAUNCH1SOUND, xyz, cameraPos);
159 else
160 insertSoundNode (LAUNCH2SOUND, xyz, cameraPos);
161 }
162 #endif
163 }
164
initFountain()165 void particle::initFountain ()
166 {
167 type = FOUNTAIN;
168 displayList = flarelist[0];
169 size = 30.0f;
170 // position can be defined here because these are always on the ground
171 xyz[0] = rsRandf (300.0f) - 150.0f;
172 xyz[1] = 5.0f;
173 xyz[2] = rsRandf (300.0f) - 150.0f;
174 rgb = randomColor ();
175 t = rsRandf (5.0f) + 10.0f;
176 tr = t;
177 bright = 0.0f;
178 makeSmoke = 0;
179
180 #ifdef HAVE_OPENAL
181 if (dSound) {
182 if (rsRandi (2))
183 insertSoundNode (LAUNCH1SOUND, xyz, cameraPos);
184 else
185 insertSoundNode (LAUNCH2SOUND, xyz, cameraPos);
186 }
187 #endif
188 }
189
initSpinner()190 void particle::initSpinner ()
191 {
192 type = SPINNER;
193 displayList = flarelist[0];
194 drag = 0.612f; // terminal velocity of 20 ft/s
195 rgb = randomColor ();
196 spin = rsRandf (3.0f) + 12.0f; // radial velocity
197 tilt = rsRandf (PIx2); // original rotation
198 tiltvec.set (rsRandf (2.0f) - 1.0f, rsRandf (2.0f) - 1.0f, rsRandf (2.0f) - 1.0f);
199 tiltvec.normalize (); // vector around which this spinner spins
200 t = rsRandf (2.0f) + 6.0f;
201 tr = t;
202 bright = 0.0f;
203 size = 20.0f;
204 makeSmoke = 1;
205 sparkTrailLength = 0.0f;
206
207 #ifdef HAVE_OPENAL
208 if (dSound) {
209 if (rsRandi (2))
210 insertSoundNode (LAUNCH1SOUND, xyz, cameraPos);
211 else
212 insertSoundNode (LAUNCH2SOUND, xyz, cameraPos);
213 }
214 #endif
215 }
216
initSmoke(rsVec pos,rsVec speed)217 void particle::initSmoke (rsVec pos, rsVec speed)
218 {
219 type = SMOKE;
220 displayList = smokelist[rsRandi (5)];
221 xyz = pos;
222 vel = speed;
223 rgb[0] = rgb[1] = rgb[2] = 0.01f * float (dAmbient);
224
225 drag = 2.0f;
226 // time for each smoke particle varies and must be assigned by the particle that produces the smoke
227 size = 0.1f;
228 makeSmoke = 0;
229 }
230
initStar()231 void particle::initStar ()
232 {
233 type = STAR;
234 displayList = flarelist[0];
235 drag = 0.612f; // terminal velocity of 20 ft/s
236 size = 30.0f;
237 t = rsRandf (1.0f) + 2.0f;
238 tr = t;
239 static int someSmoke = 0;
240
241 makeSmoke = whichSmoke[someSmoke];
242 smokeTrailLength = 0.0f;
243 someSmoke++;
244 if (someSmoke >= WHICHSMOKES)
245 someSmoke = 0;
246 }
247
initStreamer()248 void particle::initStreamer ()
249 {
250 type = STREAMER;
251 displayList = flarelist[0];
252 drag = 0.612f; // terminal velocity of 20 ft/s
253 size = 30.0f;
254 t = rsRandf (1.0f) + 3.0f;
255 tr = t;
256 sparkTrailLength = 0.0f;
257 }
258
initMeteor()259 void particle::initMeteor ()
260 {
261 type = METEOR;
262 displayList = flarelist[0];
263 drag = 0.612f; // terminal velocity of 20 ft/s
264 t = rsRandf (1.0f) + 3.0f;
265 tr = t;
266 size = 20.0f;
267 sparkTrailLength = 0.0f;
268 }
269
initStarPopper()270 void particle::initStarPopper ()
271 {
272 type = POPPER;
273 displayList = flarelist[0];
274 drag = 0.4f;
275 t = rsRandf (1.5f) + 3.0f;
276 tr = t;
277 makeSmoke = 1;
278 explosiontype = STAR;
279 size = 0.0f;
280 smokeTrailLength = 0.0f;
281 }
282
initStreamerPopper()283 void particle::initStreamerPopper ()
284 {
285 type = POPPER;
286 displayList = flarelist[0];
287 size = 0.0f;
288 drag = 0.4f;
289 t = rsRandf (1.5f) + 3.0f;
290 tr = t;
291 makeSmoke = 1;
292 explosiontype = STREAMER;
293 smokeTrailLength = 0.0f;
294 }
295
initMeteorPopper()296 void particle::initMeteorPopper ()
297 {
298 type = POPPER;
299 displayList = flarelist[0];
300 size = 0.0f;
301 drag = 0.4f;
302 t = rsRandf (1.5f) + 3.0f;
303 tr = t;
304 makeSmoke = 1;
305 explosiontype = METEOR;
306 smokeTrailLength = 0.0f;
307 }
308
initLittlePopper()309 void particle::initLittlePopper ()
310 {
311 type = POPPER;
312 displayList = flarelist[0];
313 drag = 0.4f;
314 t = 4.0f * (0.5f - sin (rsRandf (PI))) + 4.5f;
315 tr = t;
316 size = rsRandf (3.0f) + 7.0f;
317 makeSmoke = 0;
318 explosiontype = POPPER;
319 }
320
initBee()321 void particle::initBee ()
322 {
323 type = BEE;
324 displayList = flarelist[0];
325 size = 10.0f;
326 drag = 0.3f;
327 t = rsRandf (1.0f) + 2.5f;
328 tr = t;
329 makeSmoke = 0;
330 sparkTrailLength = 0.0f;
331
332 // these variables will be misused to describe bee acceleration vector
333 thrust = rsRandf (PIx2) + PI;
334 endthrust = rsRandf (PIx2) + PI;
335 spin = rsRandf (PIx2) + PI;
336 tiltvec.set (rsRandf (PIx2), rsRandf (PIx2), rsRandf (PIx2));
337 }
338
initSucker()339 void particle::initSucker ()
340 {
341 int i;
342 particle *newp;
343 rsVec color;
344 float temp1, temp2, ch, sh, cp, sp;
345
346 type = SUCKER;
347 drag = 0.612f; // terminal velocity of 20 ft/s
348 displayList = flarelist[2];
349 rgb.set (1.0f, 1.0f, 1.0f);
350 size = 300.0f;
351 t = tr = 4.0f;
352 makeSmoke = 0;
353
354 // make explosion
355 newp = addParticle ();
356 newp->type = EXPLOSION;
357 newp->xyz = xyz;
358 newp->vel = vel;
359 newp->rgb.set (1.0f, 1.0f, 1.0f);
360 newp->size = 200.0f;
361 newp->t = newp->tr = 4.0f;
362
363 // Make double ring to go along with sucker
364 color = randomColor ();
365 temp1 = rsRandf (PI); // heading
366 temp2 = rsRandf (PI); // pitch
367 ch = cos (temp1);
368 sh = sin (temp1);
369 cp = cos (temp2);
370 sp = sin (temp2);
371 for (i = 0; i < 90; i++) {
372 newp = addParticle ();
373 newp->initStar ();
374 newp->xyz = xyz;
375 newp->vel[0] = rsRandf (1.0f) - 0.5f;
376 newp->vel[1] = 0.0f;
377 newp->vel[2] = rsRandf (1.0f) - 0.5f;
378 newp->vel.normalize ();
379 // pitch
380 newp->vel[1] = sp * newp->vel[2];
381 newp->vel[2] = cp * newp->vel[2];
382 // heading
383 temp1 = newp->vel[0];
384 newp->vel[0] = ch * temp1 + sh * newp->vel[1];
385 newp->vel[1] = -sh * temp1 + ch * newp->vel[1];
386 // multiply velocity
387 newp->vel[0] *= 350.0f + rsRandf (30.0f);
388 newp->vel[1] *= 350.0f + rsRandf (30.0f);
389 newp->vel[2] *= 350.0f + rsRandf (30.0f);
390 newp->vel[0] += vel[0];
391 newp->vel[1] += vel[1];
392 newp->vel[2] += vel[2];
393 newp->rgb = color;
394 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
395 newp->makeSmoke = 0;
396 }
397 color = randomColor ();
398 temp1 = rsRandf (PI); // heading
399 temp2 = rsRandf (PI); // pitch
400 ch = cos (temp1);
401 sh = sin (temp1);
402 cp = cos (temp2);
403 sp = sin (temp2);
404 for (i = 0; i < 90; i++) {
405 newp = addParticle ();
406 newp->initStar ();
407 newp->xyz = xyz;
408 newp->vel[0] = rsRandf (1.0f) - 0.5f;
409 newp->vel[1] = 0.0f;
410 newp->vel[2] = rsRandf (1.0f) - 0.5f;
411 newp->vel.normalize ();
412 // pitch
413 newp->vel[1] = sp * newp->vel[2];
414 newp->vel[2] = cp * newp->vel[2];
415 // heading
416 temp1 = newp->vel[0];
417 newp->vel[0] = ch * temp1 + sh * newp->vel[1];
418 newp->vel[1] = -sh * temp1 + ch * newp->vel[1];
419 // multiply velocity
420 newp->vel[0] *= 600.0f + rsRandf (50.0f);
421 newp->vel[1] *= 600.0f + rsRandf (50.0f);
422 newp->vel[2] *= 600.0f + rsRandf (50.0f);
423 newp->vel[0] += vel[0];
424 newp->vel[1] += vel[1];
425 newp->vel[2] += vel[2];
426 newp->rgb = color;
427 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
428 newp->makeSmoke = 0;
429 }
430
431 #ifdef HAVE_OPENAL
432 if (dSound)
433 insertSoundNode (SUCKSOUND, xyz, cameraPos);
434 #endif
435 }
436
initShockwave()437 void particle::initShockwave ()
438 {
439 int i;
440 particle *newp;
441 rsVec color;
442
443 type = SHOCKWAVE;
444 drag = 0.612f; // terminal velocity of 20 ft/s
445 rgb.set (1.0f, 1.0f, 1.0f);
446 size = 0.0f;
447 t = tr = 5.0f;
448
449 // make explosion
450 newp = addParticle ();
451 newp->type = EXPLOSION;
452 newp->xyz = xyz;
453 newp->vel = vel;
454 newp->rgb.set (1.0f, 1.0f, 1.0f);
455 newp->size = 300.0f;
456 newp->t = newp->tr = 3.0f;
457 makeSmoke = 0;
458
459 // Little sphere without smoke
460 color = randomColor ();
461 for (i = 0; i < 75; i++) {
462 newp = addParticle ();
463 newp->initStar ();
464 newp->xyz = xyz;
465 newp->vel[0] = rsRandf (1.0f) - 0.5f;
466 newp->vel[1] = rsRandf (1.0f) - 0.5f;
467 newp->vel[2] = rsRandf (1.0f) - 0.5f;
468 newp->vel.normalize ();
469 newp->vel *= (rsRandf (10.0f) + 100.0f);
470 newp->vel += vel;
471 newp->rgb = color;
472 newp->size = 100.0f;
473 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
474 newp->makeSmoke = 0;
475 }
476
477 // Disk of stars without smoke
478 color = randomColor ();
479 for (i = 0; i < 150; i++) {
480 newp = addParticle ();
481 newp->initStar ();
482 newp->drag = 0.2f;
483 newp->xyz = xyz;
484 newp->vel[0] = rsRandf (1.0f) - 0.5f;
485 newp->vel[1] = rsRandf (0.03f) - 0.005f;
486 newp->vel[2] = rsRandf (1.0f) - 0.5f;
487 newp->vel.normalize ();
488 // multiply velocity
489 newp->vel *= (rsRandf (30.0f) + 500.0f);
490 newp->vel += vel;
491 newp->rgb = color;
492 newp->size = 50.0f;
493 newp->t = newp->tr = rsRandf (2.0f) + 3.0f;
494 newp->makeSmoke = 0;
495 }
496
497 #ifdef HAVE_OPENAL
498 if (dSound)
499 insertSoundNode (NUKESOUND, xyz, cameraPos);
500 #endif
501 }
502
initStretcher()503 void particle::initStretcher ()
504 {
505 int i;
506 particle *newp;
507 rsVec color;
508
509 type = STRETCHER;
510 drag = 0.612f; // terminal velocity of 20 ft/s
511 displayList = flarelist[3];
512 rgb.set (1.0f, 1.0f, 1.0f);
513 size = 0.0f;
514 t = tr = 4.0f;
515 makeSmoke = 0;
516
517 // explosion
518 newp = addParticle ();
519 newp->type = EXPLOSION;
520 newp->displayList = flarelist[0];
521 newp->xyz = xyz;
522 newp->vel = vel;
523 newp->rgb.set (1.0f, 0.8f, 0.6f);
524 newp->size = 400.0f;
525 newp->t = newp->tr = 4.0f;
526 newp->makeSmoke = 0;
527
528 // Make triple ring to go along with stretcher
529 color = randomColor ();
530 for (i = 0; i < 80; i++) {
531 newp = addParticle ();
532 newp->initStar ();
533 newp->xyz = xyz;
534 newp->vel[0] = rsRandf (1.0f) - 0.5f;
535 newp->vel[1] = 0.0f;
536 newp->vel[2] = rsRandf (1.0f) - 0.5f;
537 newp->vel.normalize ();
538 newp->vel[0] *= 400.0f + rsRandf (30.0f);
539 newp->vel[1] += rsRandf (70.0f) - 35.0f;
540 newp->vel[2] *= 400.0f + rsRandf (30.0f);
541 newp->vel[0] += vel[0];
542 newp->vel[1] += vel[1];
543 newp->vel[2] += vel[2];
544 newp->rgb = color;
545 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
546 newp->makeSmoke = 0;
547 }
548 color = randomColor ();
549 for (i = 0; i < 80; i++) {
550 newp = addParticle ();
551 newp->initStar ();
552 newp->xyz = xyz;
553 newp->vel[0] = rsRandf (1.0f) - 0.5f;
554 newp->vel[1] = 0.0f;
555 newp->vel[2] = rsRandf (1.0f) - 0.5f;
556 newp->vel.normalize ();
557 newp->vel[0] *= 550.0f + rsRandf (40.0f);
558 newp->vel[1] += rsRandf (70.0f) - 35.0f;
559 newp->vel[2] *= 550.0f + rsRandf (40.0f);
560 newp->vel[0] += vel[0];
561 newp->vel[1] += vel[1];
562 newp->vel[2] += vel[2];
563 newp->rgb = color;
564 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
565 newp->makeSmoke = 0;
566 }
567 color = randomColor ();
568 for (i = 0; i < 80; i++) {
569 newp = addParticle ();
570 newp->initStar ();
571 newp->xyz = xyz;
572 newp->vel[0] = rsRandf (1.0f) - 0.5f;
573 newp->vel[1] = 0.0f;
574 newp->vel[2] = rsRandf (1.0f) - 0.5f;
575 newp->vel.normalize ();
576 newp->vel[0] *= 700.0f + rsRandf (50.0f);
577 newp->vel[1] += rsRandf (70.0f) - 35.0f;
578 newp->vel[2] *= 700.0f + rsRandf (50.0f);
579 newp->vel[0] += vel[0];
580 newp->vel[1] += vel[1];
581 newp->vel[2] += vel[2];
582 newp->rgb = color;
583 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
584 newp->makeSmoke = 0;
585 }
586
587 #ifdef HAVE_OPENAL
588 if (dSound)
589 insertSoundNode (SUCKSOUND, xyz, cameraPos);
590 #endif
591 }
592
initBigmama()593 void particle::initBigmama ()
594 {
595 int i;
596 particle *newp;
597 rsVec color;
598 float temp;
599
600 type = BIGMAMA;
601 drag = 0.612f; // terminal velocity of 20 ft/s
602 displayList = flarelist[2];
603 rgb.set (0.6f, 0.6f, 1.0f);
604 size = 0.0f;
605 t = tr = 5.0f;
606 makeSmoke = 0;
607
608 // explosion
609 newp = addParticle ();
610 newp->type = EXPLOSION;
611 newp->xyz = xyz;
612 newp->vel = vel;
613 newp->drag = 0.0f;
614 newp->rgb.set (0.8f, 0.8f, 1.0f);
615 newp->size = 200.0f;
616 newp->t = newp->tr = 2.5f;
617 newp->makeSmoke = 0;
618
619 // vertical stars
620 newp = addParticle ();
621 newp->initStar ();
622 newp->xyz = xyz;
623 newp->vel = vel;
624 newp->drag = 0.0f;
625 newp->vel[1] += 15.0f;
626 newp->rgb.set (1.0f, 1.0f, 0.9f);
627 newp->size = 400.0f;
628 newp->t = newp->tr = 3.0f;
629 newp->makeSmoke = 0;
630 newp = addParticle ();
631 newp->initStar ();
632 newp->xyz = xyz;
633 newp->vel = vel;
634 newp->drag = 0.0f;
635 newp->vel[1] -= 15.0f;
636 newp->rgb.set (1.0f, 1.0f, 0.9f);
637 newp->size = 400.0f;
638 newp->t = newp->tr = 3.0f;
639 newp->makeSmoke = 0;
640 newp = addParticle ();
641 newp->initStar ();
642 newp->xyz = xyz;
643 newp->vel = vel;
644 newp->drag = 0.0f;
645 newp->vel[1] += 45.0f;
646 newp->rgb.set (1.0f, 1.0f, 0.6f);
647 newp->size = 400.0f;
648 newp->t = newp->tr = 3.5f;
649 newp->makeSmoke = 0;
650 newp = addParticle ();
651 newp->initStar ();
652 newp->xyz = xyz;
653 newp->vel = vel;
654 newp->drag = 0.0f;
655 newp->vel[1] -= 45.0f;
656 newp->rgb.set (1.0f, 1.0f, 0.6f);
657 newp->size = 400.0f;
658 newp->t = newp->tr = 3.5f;
659 newp->makeSmoke = 0;
660 newp = addParticle ();
661 newp->initStar ();
662 newp->xyz = xyz;
663 newp->vel = vel;
664 newp->drag = 0.0f;
665 newp->vel[1] += 75.0f;
666 newp->rgb.set (1.0f, 0.5f, 0.3f);
667 newp->size = 400.0f;
668 newp->t = newp->tr = 4.0f;
669 newp->makeSmoke = 0;
670 newp = addParticle ();
671 newp->initStar ();
672 newp->xyz = xyz;
673 newp->vel = vel;
674 newp->drag = 0.0f;
675 newp->vel[1] -= 75.0f;
676 newp->rgb.set (1.0f, 0.5f, 0.3f);
677 newp->size = 400.0f;
678 newp->t = newp->tr = 4.0f;
679 newp->makeSmoke = 0;
680 newp = addParticle ();
681 newp->initStar ();
682 newp->xyz = xyz;
683 newp->vel = vel;
684 newp->drag = 0.0f;
685 newp->vel[1] += 105.0f;
686 newp->rgb.set (1.0f, 0.0f, 0.0f);
687 newp->size = 400.0f;
688 newp->t = newp->tr = 4.5f;
689 newp->makeSmoke = 0;
690 newp = addParticle ();
691 newp->initStar ();
692 newp->xyz = xyz;
693 newp->vel = vel;
694 newp->drag = 0.0f;
695 newp->vel[1] -= 105.0f;
696 newp->rgb.set (1.0f, 0.0f, 0.0f);
697 newp->size = 400.0f;
698 newp->t = newp->tr = 4.5f;
699 newp->makeSmoke = 0;
700
701 // Sphere without smoke
702 color = randomColor ();
703 for (i = 0; i < 75; i++) {
704 newp = addParticle ();
705 newp->initStar ();
706 newp->xyz = xyz;
707 newp->vel[0] = rsRandf (1.0f) - 0.5f;
708 newp->vel[1] = rsRandf (1.0f) - 0.5f;
709 newp->vel[2] = rsRandf (1.0f) - 0.5f;
710 newp->vel.normalize ();
711 temp = 600.0f + rsRandf (100.0f);
712 newp->vel[0] *= temp;
713 newp->vel[1] *= temp;
714 newp->vel[2] *= temp;
715 newp->vel[0] += vel[0];
716 newp->vel[1] += vel[1];
717 newp->vel[2] += vel[2];
718 newp->rgb = color;
719 newp->t = newp->tr = rsRandf (2.0f) + 2.0f;
720 newp->makeSmoke = 0;
721 }
722
723 // disk of big streamers
724 color = randomColor ();
725 for (i = 0; i < 50; i++) {
726 newp = addParticle ();
727 newp->initStreamer ();
728 newp->drag = 0.3f;
729 newp->xyz = xyz;
730 newp->vel[0] = rsRandf (1.0f) - 0.5f;
731 newp->vel[1] = 0.0f;
732 newp->vel[2] = rsRandf (1.0f) - 0.5f;
733 newp->vel.normalize ();
734 newp->vel[0] *= 1000.0f + rsRandf (100.0f);
735 newp->vel[1] += rsRandf (100.0f) - 50.0f;
736 newp->vel[2] *= 1000.0f + rsRandf (100.0f);
737 newp->vel[0] += vel[0];
738 newp->vel[1] += vel[1];
739 newp->vel[2] += vel[2];
740 newp->rgb = color;
741 newp->size = 100.0f;
742 newp->t = newp->tr = rsRandf (6.0f) + 3.0f;
743 newp->makeSmoke = 0;
744 }
745
746 #ifdef HAVE_OPENAL
747 if (dSound)
748 insertSoundNode (NUKESOUND, xyz, cameraPos);
749 #endif
750 }
751
initExplosion()752 void particle::initExplosion ()
753 {
754 type = EXPLOSION;
755 displayList = flarelist[0];
756 drag = 0.612f;
757 t = 0.5f;
758 tr = t;
759 bright = 1.0f;
760 life = bright;
761 size = 100.0f;
762 makeSmoke = 0;
763
764 // Don't do massive explosions too close to the ground
765 if ((explosiontype == 19 || explosiontype == 20) && (xyz[1] < 1000.0f))
766 explosiontype = 0;
767
768 switch (explosiontype) {
769 case 0:
770 rgb = randomColor ();
771 if (!rsRandi (10)) // big sphere
772 popSphere (225, 1000.0f, rgb);
773 else // regular sphere
774 popSphere (175, rsRandf (100.0f) + 400.0f, rgb);
775 break;
776 case 1:
777 rgb = randomColor ();
778 if (!rsRandi (10)) // big split sphere
779 popSplitSphere (225, 1000.0f, rgb);
780 else // regular split sphere
781 popSplitSphere (175, rsRandf (100.0f) + 400.0f, rgb);
782 break;
783 case 2:
784 rgb.set (1.0f, 1.0f, 1.0f);
785 if (!rsRandi (10)) // big multicolored sphere
786 popMultiColorSphere (225, 1000.0f);
787 else // regular multicolored sphere
788 popMultiColorSphere (175, rsRandf (100.0f) + 400.0f);
789 break;
790 case 3: // ring
791 rgb = randomColor ();
792 popRing (70, rsRandf (100.0f) + 400.0f, rgb);
793 break;
794 case 4: // double sphere
795 rgb = randomColor ();
796 popSphere (90, rsRandf (50.0f) + 200.0f, randomColor ());
797 popSphere (150, rsRandf (100.0f) + 500.0f, rgb);
798 break;
799 case 5: // sphere and ring
800 rgb = randomColor ();
801 popRing (70, rsRandf (100.0f) + 500.0f, randomColor ());
802 popSphere (150, rsRandf (50.0f) + 200.0f, rgb);
803 break;
804 case 6: // Sphere of streamers
805 rgb = randomColor ();
806 popStreamers (40, rsRandf (100.0f) + 400.0f, rgb);
807 break;
808 case 7: // Sphere of meteors
809 rgb = randomColor ();
810 popMeteors (40, rsRandf (100.0f) + 400.0f, rgb);
811 break;
812 case 8: // Small sphere of stars and large sphere of streamers
813 rgb = randomColor ();
814 popStreamers (25, rsRandf (100.0f) + 500.0f, rgb);
815 popSphere (90, rsRandf (50.0f) + 200.0f, randomColor ());
816 break;
817 case 9: // Small sphere of stars and large sphere of meteors
818 rgb = randomColor ();
819 popMeteors (25, rsRandf (100.0f) + 500.0f, rgb);
820 popSphere (90, rsRandf (50.0f) + 200.0f, randomColor ());
821 break;
822 case 10: // Sphere of streamers inside sphere of stars
823 rgb = randomColor ();
824 popStreamers (20, rsRandf (100.0f) + 450.0f, rgb);
825 popSphere (150, rsRandf (50.0f) + 500.0f, randomColor ());
826 break;
827 case 11: // Sphere of meteors inside sphere of stars
828 rgb = randomColor ();
829 popMeteors (20, rsRandf (100.0f) + 450.0f, rgb);
830 popSphere (150, rsRandf (50.0f) + 500.0f, randomColor ());
831 break;
832 case 12: // a few bombs that fall and explode into stars
833 rgb = randomColor ();
834 popStarPoppers (8, rsRandf (100.0f) + 300.0f, rgb);
835 break;
836 case 13: // a few bombs that fall and explode into streamers
837 rgb = randomColor ();
838 popStreamerPoppers (8, rsRandf (100.0f) + 300.0f, rgb);
839 break;
840 case 14: // a few bombs that fall and explode into meteors
841 rgb = randomColor ();
842 popMeteorPoppers (8, rsRandf (100.0f) + 300.0f, rgb);
843 break;
844 case 15: // lots of little falling firecrackers
845 popLittlePoppers (250, rsRandf (50.0f) + 150.0f);
846 break;
847 case 16:
848 rgb = randomColor ();
849 popBees (30, 10.0f, rgb);
850 break;
851 case 17: // Boom! (loud noise and flash of light)
852 rgb.set (1.0f, 1.0f, 1.0f);
853 size = 150.0f;
854 break;
855 // 18 is a spinner, which doesn't require explosion
856 case 19:
857 rgb.set (1.0f, 1.0f, 1.0f);
858 initSucker ();
859 break;
860 case 20:
861 rgb.set (1.0f, 1.0f, 1.0f);
862 initStretcher ();
863 break;
864 case 100: // these three are little explosions for poppers
865 popSphere (30, 100.0f, rgb);
866 break;
867 case 101:
868 popStreamers (10, 100.0f, rgb);
869 break;
870 case 102:
871 popMeteors (10, 100.0f, rgb);
872 }
873
874 #ifdef HAVE_OPENAL
875 if (dSound) {
876 if (explosiontype == 17) // extra resounding boom
877 insertSoundNode (BOOM4SOUND, xyz, cameraPos);
878 // make bees and big booms whistle sometimes
879 if (explosiontype == 16 || explosiontype == 17)
880 if (rsRandi (2))
881 insertSoundNode (WHISTLESOUND, xyz, cameraPos);
882 // regular booms
883 if (explosiontype <= 16 || explosiontype >= 100)
884 insertSoundNode (BOOM1SOUND + rsRandi (3), xyz, cameraPos);
885 // sucker and stretcher take care of their own sounds
886 }
887 #endif
888 }
889
popSphere(int numParts,float v0,rsVec color)890 void particle::popSphere (int numParts, float v0, rsVec color)
891 {
892 particle *newp = NULL;
893 float temp;
894
895 for (int i = 0; i < numParts; i++) {
896 newp = addParticle ();
897 newp->initStar ();
898 newp->xyz = xyz;
899 newp->vel[0] = rsRandf (1.0f) - 0.5f;
900 newp->vel[1] = rsRandf (1.0f) - 0.5f;
901 newp->vel[2] = rsRandf (1.0f) - 0.5f;
902 newp->vel.normalize ();
903 temp = v0 + rsRandf (50.0f);
904 newp->vel *= temp;
905 newp->vel += vel;
906 newp->rgb = color;
907 }
908
909 if (!rsRandi (100))
910 newp->t = newp->tr = rsRandf (20.0f) + 5.0f;
911 }
912
popSplitSphere(int numParts,float v0,rsVec color1)913 void particle::popSplitSphere (int numParts, float v0, rsVec color1)
914 {
915 particle *newp = NULL;
916 rsVec color2;
917 rsVec planeNormal;
918 float temp;
919
920 color2 = randomColor ();
921 planeNormal[0] = rsRandf (1.0f) - 0.5f;
922 planeNormal[1] = rsRandf (1.0f) - 0.5f;
923 planeNormal[2] = rsRandf (1.0f) - 0.5f;
924 planeNormal.normalize ();
925 for (int i = 0; i < numParts; i++) {
926 newp = addParticle ();
927 newp->initStar ();
928 newp->xyz = xyz;
929 newp->vel[0] = rsRandf (1.0f) - 0.5f;
930 newp->vel[1] = rsRandf (1.0f) - 0.5f;
931 newp->vel[2] = rsRandf (1.0f) - 0.5f;
932 newp->vel.normalize ();
933 if (planeNormal.dot (newp->vel) > 0.0f)
934 newp->rgb = color1;
935 else
936 newp->rgb = color2;
937 temp = v0 + rsRandf (50.0f);
938 newp->vel *= temp;
939 newp->vel += vel;
940 }
941
942 if (!rsRandi (100))
943 newp->t = newp->tr = rsRandf (20.0f) + 5.0f;
944 }
945
popMultiColorSphere(int numParts,float v0)946 void particle::popMultiColorSphere (int numParts, float v0)
947 {
948 int j;
949 particle *newp = NULL;
950 float temp;
951 rsVec color[3];;
952
953 color[0] = randomColor ();
954 color[1] = randomColor ();
955 color[2] = randomColor ();
956 j = 0;
957 for (int i = 0; i < numParts; i++) {
958 newp = addParticle ();
959 newp->initStar ();
960 newp->xyz = xyz;
961 newp->vel[0] = rsRandf (1.0f) - 0.5f;
962 newp->vel[1] = rsRandf (1.0f) - 0.5f;
963 newp->vel[2] = rsRandf (1.0f) - 0.5f;
964 newp->vel.normalize ();
965 temp = v0 + rsRandf (30.0f);
966 newp->vel *= temp;
967 newp->vel += vel;
968 newp->rgb = color[j];
969 j++;
970 if (j >= 3)
971 j = 0;
972 }
973
974 if (!rsRandi (100))
975 newp->t = newp->tr = rsRandf (20.0f) + 5.0f;
976 }
977
popRing(int numParts,float v0,rsVec color)978 void particle::popRing (int numParts, float v0, rsVec color)
979 {
980 particle *newp = NULL;
981 float temp1, temp2;
982 float ch, sh, cp, sp;
983
984 temp1 = rsRandf (PI); // heading
985 temp2 = rsRandf (PI); // pitch
986 ch = cos (temp1);
987 sh = sin (temp1);
988 cp = cos (temp2);
989 sp = sin (temp2);
990 for (int i = 0; i < numParts; i++) {
991 newp = addParticle ();
992 newp->initStar ();
993 newp->xyz = xyz;
994 newp->vel[0] = rsRandf (1.0f) - 0.5f;
995 newp->vel[1] = 0.0f;
996 newp->vel[2] = rsRandf (1.0f) - 0.5f;
997 newp->vel.normalize ();
998 // pitch
999 newp->vel[1] = sp * newp->vel[2];
1000 newp->vel[2] = cp * newp->vel[2];
1001 // heading
1002 temp1 = newp->vel[0];
1003 newp->vel[0] = ch * temp1 + sh * newp->vel[1];
1004 newp->vel[1] = -sh * temp1 + ch * newp->vel[1];
1005 // multiply velocity
1006 newp->vel[0] *= v0 + rsRandf (50.0f);
1007 newp->vel[1] *= v0 + rsRandf (50.0f);
1008 newp->vel[2] *= v0 + rsRandf (50.0f);
1009 newp->vel += vel;
1010 newp->rgb = color;
1011 }
1012
1013 if (!rsRandi (100))
1014 newp->t = newp->tr = rsRandf (20.0f) + 5.0f;
1015 }
1016
popStreamers(int numParts,float v0,rsVec color)1017 void particle::popStreamers (int numParts, float v0, rsVec color)
1018 {
1019 particle *newp = NULL;
1020 float temp;
1021
1022 for (int i = 0; i < numParts; i++) {
1023 newp = addParticle ();
1024 newp->initStreamer ();
1025 newp->xyz = xyz;
1026 newp->vel[0] = rsRandf (1.0f) - 0.5f;
1027 newp->vel[1] = rsRandf (1.0f) - 0.5f;
1028 newp->vel[2] = rsRandf (1.0f) - 0.5f;
1029 newp->vel.normalize ();
1030 temp = v0 + rsRandf (50.0f);
1031 newp->vel *= temp;
1032 newp->vel += vel;
1033 newp->rgb = color;
1034 }
1035 }
1036
popMeteors(int numParts,float v0,rsVec color)1037 void particle::popMeteors (int numParts, float v0, rsVec color)
1038 {
1039 particle *newp = NULL;
1040 float temp;
1041
1042 for (int i = 0; i < numParts; i++) {
1043 newp = addParticle ();
1044 newp->initMeteor ();
1045 newp->xyz = xyz;
1046 newp->vel[0] = rsRandf (1.0f) - 0.5f;
1047 newp->vel[1] = rsRandf (1.0f) - 0.5f;
1048 newp->vel[2] = rsRandf (1.0f) - 0.5f;
1049 newp->vel.normalize ();
1050 temp = v0 + rsRandf (50.0f);
1051 newp->vel *= temp;
1052 newp->vel += vel;
1053 newp->rgb = color;
1054 }
1055 }
1056
popStarPoppers(int numParts,float v0,rsVec color)1057 void particle::popStarPoppers (int numParts, float v0, rsVec color)
1058 {
1059 particle *newp = NULL;
1060 float v0x2 = v0 * 2;
1061
1062 for (int i = 0; i < numParts; i++) {
1063 newp = addParticle ();
1064 newp->initStarPopper ();
1065 newp->xyz = xyz;
1066 newp->vel[0] = vel[0] + rsRandf (v0x2) - v0;
1067 newp->vel[1] = vel[1] + rsRandf (v0x2) - v0;
1068 newp->vel[2] = vel[2] + rsRandf (v0x2) - v0;
1069 newp->rgb = color;
1070 }
1071 }
1072
popStreamerPoppers(int numParts,float v0,rsVec color)1073 void particle::popStreamerPoppers (int numParts, float v0, rsVec color)
1074 {
1075 particle *newp = NULL;
1076 float v0x2 = v0 * 2;
1077
1078 for (int i = 0; i < numParts; i++) {
1079 newp = addParticle ();
1080 newp->initStreamerPopper ();
1081 newp->xyz = xyz;
1082 newp->vel[0] = vel[0] + rsRandf (v0x2) - v0;
1083 newp->vel[1] = vel[1] + rsRandf (v0x2) - v0;
1084 newp->vel[2] = vel[2] + rsRandf (v0x2) - v0;
1085 newp->rgb = color;
1086 }
1087 }
1088
popMeteorPoppers(int numParts,float v0,rsVec color)1089 void particle::popMeteorPoppers (int numParts, float v0, rsVec color)
1090 {
1091 int i;
1092 particle *newp;
1093 float v0x2 = v0 * 2;
1094
1095 for (i = 0; i < numParts; i++) {
1096 newp = addParticle ();
1097 newp->initMeteorPopper ();
1098 newp->xyz = xyz;
1099 newp->vel[0] = vel[0] + rsRandf (v0x2) - v0;
1100 newp->vel[1] = vel[1] + rsRandf (v0x2) - v0;
1101 newp->vel[2] = vel[2] + rsRandf (v0x2) - v0;
1102 newp->rgb = color;
1103 }
1104 }
1105
popLittlePoppers(int numParts,float v0)1106 void particle::popLittlePoppers (int numParts, float v0)
1107 {
1108 particle *newp = NULL;
1109 float v0x2 = v0 * 2;
1110
1111 for (int i = 0; i < numParts; i++) {
1112 newp = addParticle ();
1113 newp->initLittlePopper ();
1114 newp->xyz = xyz;
1115 newp->vel[0] = vel[0] + rsRandf (v0x2) - v0;
1116 newp->vel[1] = vel[1] + rsRandf (v0x2) - v0;
1117 newp->vel[2] = vel[2] + rsRandf (v0x2) - v0;
1118 }
1119
1120 #ifdef HAVE_OPENAL
1121 if (dSound)
1122 insertSoundNode (POPPERSOUND, xyz, cameraPos);
1123 #endif
1124 }
1125
popBees(int numParts,float v0,rsVec color)1126 void particle::popBees (int numParts, float v0, rsVec color)
1127 {
1128 particle *newp = NULL;
1129
1130 for (int i = 0; i < numParts; i++) {
1131 newp = addParticle ();
1132 newp->initBee ();
1133 newp->xyz = xyz;
1134 newp->vel[0] = rsRandf (1.0f) - 0.5f;
1135 newp->vel[1] = rsRandf (1.0f) - 0.5f;
1136 newp->vel[2] = rsRandf (1.0f) - 0.5f;
1137 newp->vel *= v0;
1138 newp->vel += vel;
1139 newp->rgb = color;
1140 }
1141 }
1142
findDepth()1143 void particle::findDepth ()
1144 {
1145 // This isn't the actual distance from the camera. It is the the
1146 // distance along the view vector coming straight out of the camera.
1147 // This is calculated with a simple dot product. The billboards don't
1148 // actually face the camera; they all face the same direction (straight
1149 // down the view vector of the camera, so sorting is done a little
1150 // differently than one might expect).
1151 depth = (cameraPos[0] - xyz[0]) * float (billboardMat[8])
1152 + (cameraPos[1] - xyz[1]) * float (billboardMat[9])
1153 + (cameraPos[2] - xyz[2]) * float (billboardMat[10]);
1154 }
1155
1156 // Rockets and explosions illuminate smoke
1157 // Only explosions illuminate clouds
illuminate(particle * ill)1158 void illuminate (particle * ill)
1159 {
1160 float temp;
1161 // desaturate illumination colors
1162 float newrgb[3] = { ill->rgb[0] * 0.6f + 0.4f, ill->rgb[1] * 0.6f + 0.4f, ill->rgb[2] * 0.6f + 0.4f };
1163
1164 // Smoke illumination
1165 if ((ill->type == ROCKET) || (ill->type == FOUNTAIN)) {
1166 float distsquared;
1167
1168 std::list < particle >::iterator smk = particles.begin ();
1169 while (smk != particles.end ()) {
1170 if (smk->type == SMOKE) {
1171 distsquared = (ill->xyz[0] - smk->xyz[0]) * (ill->xyz[0] - smk->xyz[0])
1172 + (ill->xyz[1] - smk->xyz[1]) * (ill->xyz[1] - smk->xyz[1])
1173 + (ill->xyz[2] - smk->xyz[2]) * (ill->xyz[2] - smk->xyz[2]);
1174 if (distsquared < 40000.0f) {
1175 temp = (40000.0f - distsquared) * 0.000025f;
1176 temp = temp * temp * ill->bright;
1177 smk->rgb[0] += temp * newrgb[0];
1178 if (smk->rgb[0] > 1.0f)
1179 smk->rgb[0] = 1.0f;
1180 smk->rgb[1] += temp * newrgb[1];
1181 if (smk->rgb[1] > 1.0f)
1182 smk->rgb[1] = 1.0f;
1183 smk->rgb[2] += temp * newrgb[2];
1184 if (smk->rgb[2] > 1.0f)
1185 smk->rgb[2] = 1.0f;
1186 }
1187 }
1188 smk++;
1189 }
1190 } else if (ill->type == EXPLOSION) {
1191 float distsquared;
1192
1193 std::list < particle >::iterator smk = particles.begin ();
1194 while (smk != particles.end ()) {
1195 if (smk->type == SMOKE) {
1196 distsquared = (ill->xyz[0] - smk->xyz[0]) * (ill->xyz[0] - smk->xyz[0])
1197 + (ill->xyz[1] - smk->xyz[1]) * (ill->xyz[1] - smk->xyz[1])
1198 + (ill->xyz[2] - smk->xyz[2]) * (ill->xyz[2] - smk->xyz[2]);
1199 if (distsquared < 640000.0f) {
1200 temp = (640000.0f - distsquared) * 0.0000015625f;
1201 temp = temp * temp * ill->bright;
1202 smk->rgb[0] += temp * newrgb[0];
1203 if (smk->rgb[0] > 1.0f)
1204 smk->rgb[0] = 1.0f;
1205 smk->rgb[1] += temp * newrgb[1];
1206 if (smk->rgb[1] > 1.0f)
1207 smk->rgb[1] = 1.0f;
1208 smk->rgb[2] += temp * newrgb[2];
1209 if (smk->rgb[2] > 1.0f)
1210 smk->rgb[2] = 1.0f;
1211 }
1212 }
1213 smk++;
1214 }
1215
1216 // cloud illumination
1217 if (dClouds) {
1218 int north, south, west, east; // limits of cloud indices to inspect
1219 int halfmesh = CLOUDMESH / 2;
1220 float distsquared;
1221
1222 // remember clouds have 20000-foot radius from world.h, hence 0.00005
1223 // Hardcoded values like this are evil, but oh well
1224 south = int ((ill->xyz[2] - 1600.0f) * 0.00005f * float (halfmesh)) + halfmesh;
1225 north = int ((ill->xyz[2] + 1600.0f) * 0.00005f * float (halfmesh) + 0.5f) + halfmesh;
1226 west = int ((ill->xyz[0] - 1600.0f) * 0.00005f * float (halfmesh)) + halfmesh;
1227 east = int ((ill->xyz[0] + 1600.0f) * 0.00005f * float (halfmesh) + 0.5f) + halfmesh;
1228
1229 for (int i = west; i <= east; i++) {
1230 for (int j = south; j <= north; j++) {
1231 distsquared = (clouds[i][j][0] - ill->xyz[0]) * (clouds[i][j][0] - ill->xyz[0])
1232 + (clouds[i][j][1] - ill->xyz[1]) * (clouds[i][j][1] - ill->xyz[1])
1233 + (clouds[i][j][2] - ill->xyz[2]) * (clouds[i][j][2] - ill->xyz[2]);
1234 if (distsquared < 2560000.0f) {
1235 temp = (2560000.0f - distsquared) * 0.000000390625f;
1236 temp = temp * temp * ill->bright;
1237 clouds[i][j][6] += temp * newrgb[0];
1238 if (clouds[i][j][6] > 1.0f)
1239 clouds[i][j][6] = 1.0f;
1240 clouds[i][j][7] += temp * newrgb[1];
1241 if (clouds[i][j][7] > 1.0f)
1242 clouds[i][j][7] = 1.0f;
1243 clouds[i][j][8] += temp * newrgb[2];
1244 if (clouds[i][j][8] > 1.0f)
1245 clouds[i][j][8] = 1.0f;
1246 }
1247 }
1248 }
1249 }
1250 }
1251 }
1252
1253 // pulling of other particles
pulling(particle * suck)1254 void pulling (particle * suck)
1255 {
1256 rsVec diff;
1257 float pulldistsquared;
1258 float pullconst = (1.0f - suck->life) * 0.01f * elapsedTime;
1259
1260 std::list < particle >::iterator puller = particles.begin ();
1261 while (puller != particles.end ()) {
1262 diff = suck->xyz - puller->xyz;
1263 pulldistsquared = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
1264 if (pulldistsquared < 250000.0f && pulldistsquared != 0.0f && puller->type != SUCKER) {
1265 diff.normalize ();
1266 puller->vel += diff * ((250000.0f - pulldistsquared) * pullconst);
1267 }
1268
1269 puller++;
1270 }
1271 }
1272
1273 // pushing of other particles
pushing(particle * shock)1274 void pushing (particle * shock)
1275 {
1276 rsVec diff;
1277 float pushdistsquared;
1278 float pushconst = (1.0f - shock->life) * 0.002f * elapsedTime;
1279
1280 std::list < particle >::iterator pusher = particles.begin ();
1281 while (pusher != particles.end ()) {
1282 diff = pusher->xyz - shock->xyz;
1283 pushdistsquared = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
1284 if (pushdistsquared < 640000.0f && pushdistsquared != 0.0f && pusher->type != SHOCKWAVE) {
1285 diff.normalize ();
1286 pusher->vel += diff * ((640000.0f - pushdistsquared) * pushconst);
1287 }
1288
1289 pusher++;
1290 }
1291 }
1292
1293 // vertical stretching of other particles (x, z sucking; y pushing)
stretching(particle * stretch)1294 void stretching (particle * stretch)
1295 {
1296 rsVec diff;
1297 float stretchdistsquared, temp;
1298 float stretchconst = (1.0f - stretch->life) * 0.002f * elapsedTime;
1299
1300 std::list < particle >::iterator stretcher = particles.begin ();
1301 while (stretcher != particles.end ()) {
1302 diff = stretch->xyz - stretcher->xyz;
1303 stretchdistsquared = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
1304 if (stretchdistsquared < 640000.0f && stretchdistsquared != 0.0f && stretcher->type != STRETCHER) {
1305 diff.normalize ();
1306 temp = (640000.0f - stretchdistsquared) * stretchconst;
1307 stretcher->vel[0] += diff[0] * temp * 5.0f;
1308 stretcher->vel[1] -= diff[1] * temp;
1309 stretcher->vel[2] += diff[2] * temp * 5.0f;
1310 }
1311
1312 stretcher++;
1313 }
1314 }
1315
1316 //******************************************
1317 // Update particles
1318 //******************************************
update()1319 void particle::update ()
1320 {
1321 int i;
1322 float temp;
1323 static rsVec dir, crossvec, velvec, rocketEjection;
1324 static rsQuat spinquat;
1325 static rsMatrix spinmat;
1326 particle *newp;
1327
1328 // update velocities
1329 if (type == ROCKET && life > endthrust) {
1330 dir = vel;
1331 dir.normalize ();
1332 crossvec.cross (dir, tiltvec); // correct sidevec
1333 tiltvec.cross (crossvec, dir);
1334 tiltvec.normalize ();
1335 spinquat.make (spin * elapsedTime, dir[0], dir[1], dir[2]); // twist tiltvec
1336 spinmat.fromQuat (spinquat);
1337 tiltvec.transVec (spinmat);
1338 vel += dir * (thrust * elapsedTime); // apply thrust
1339 vel += tiltvec * (tilt * elapsedTime); // apply tilt
1340 }
1341
1342 if (type == BEE) {
1343 vel[0] += 800.0f * cos (tiltvec[0]) * elapsedTime;
1344 vel[1] += 800.0f * (cos (tiltvec[1]) - 0.2f) * elapsedTime;
1345 vel[2] += 800.0f * cos (tiltvec[2]) * elapsedTime;
1346 }
1347
1348 if (type != SMOKE)
1349 vel[1] -= elapsedTime * 32.0f; // gravity
1350
1351 // apply air resistance
1352 temp = 1.0f / (1.0f + drag * elapsedTime);
1353 //temp = temp * temp;
1354 vel *= temp * temp;
1355
1356 // update position
1357 // (Fountains don't move)
1358 if (type != FOUNTAIN) {
1359 lastxyz = xyz;
1360 xyz += vel * elapsedTime;
1361 // Wind: 1/10 wind on ground; -1/2 wind at 500 feet; full wind at 2000 feet;
1362 // This value is calculated to coincide with movement of the clouds in world.h
1363 // Here's the polynomial wind equation that simulates windshear:
1364 xyz[0] += (0.1f - 0.00175f * xyz[1] + 0.0000011f * xyz[1] * xyz[1]) * dWind * elapsedTime;
1365 }
1366
1367 // brightness and life
1368 tr -= elapsedTime;
1369 switch (type) {
1370 case ROCKET:
1371 life = tr / t;
1372 if (life > endthrust) { // Light up rocket gradually after it is launched
1373 bright += 2.0f * elapsedTime;
1374 if (bright > 1.0f)
1375 bright = 1.0f;
1376 } else { // Darken rocket after it stops thrusting
1377 bright -= elapsedTime;
1378 if (bright < 0.0f)
1379 bright = 0.0f;
1380 }
1381 break;
1382 case SMOKE:
1383 life = tr / t;
1384 bright = life * 0.7f;
1385 size += (30.0f - size) * (1.2f * elapsedTime);
1386 break;
1387 case FOUNTAIN:
1388 case SPINNER:
1389 life = tr / t;
1390 bright = life * life;
1391 // dim newborn fountains and spinners
1392 temp = t - tr;
1393 if (temp < 0.5f)
1394 bright *= temp * 2.0f;
1395 break;
1396 case EXPLOSION:
1397 life = tr / t;
1398 bright = life * life;
1399 break;
1400 case STAR:
1401 case STREAMER:
1402 case METEOR:
1403 temp = (t - tr) / t;
1404 temp = temp * temp;
1405 bright = 1.0f - (temp * temp);
1406 life = bright;
1407 break;
1408 case POPPER:
1409 life = tr;
1410 break;
1411 case BEE:
1412 temp = (t - tr) / t;
1413 temp = temp * temp;
1414 bright = 1.0f - (temp * temp);
1415 life = bright;
1416 // Update bee acceleration (tiltvec) using misused variables
1417 tiltvec[0] += thrust * elapsedTime;
1418 tiltvec[1] += endthrust * elapsedTime;
1419 tiltvec[2] += spin * elapsedTime;
1420 break;
1421 case SUCKER:
1422 life = tr / t;
1423 bright = life;
1424 size = 250.0f * life;
1425 break;
1426 case SHOCKWAVE:
1427 life = tr / t;
1428 bright = life;
1429 rgb[2] = life * 0.5f + 0.5f; // get a little yellow
1430 size += 400.0f * elapsedTime;
1431 break;
1432 case STRETCHER:
1433 life = tr / t;
1434 bright = 1.0f - ((1.0f - life) * (1.0f - life));
1435 size = 400.0f * bright;
1436 break;
1437 case BIGMAMA:
1438 life = tr / t;
1439 bright = life * 2.0f - 1.0f;
1440 if (bright < 0.0f)
1441 bright = 0.0f;
1442 size += 1500.0f * elapsedTime;
1443 }
1444
1445 if (makeSmoke && dSmoke) {
1446 rsVec diff = xyz - lastxyz;
1447
1448 // distance rocket traveled since last frame
1449 temp = diff.length ();
1450 smokeTrailLength += temp;
1451 // number of smoke puffs to release (1 every 2 feet)
1452 int puffs = int (smokeTrailLength * 0.5f);
1453 float multiplier = 2.0f / smokeTrailLength;
1454 smokeTrailLength -= float (puffs) * 2.0f;
1455 rsVec smkpos = lastxyz;
1456
1457 if ((type == ROCKET) && (life > endthrust)) { // eject the smoke forcefully
1458 rocketEjection = vel;
1459 rocketEjection.normalize ();
1460 rocketEjection *= -2.0f * thrust * (life - endthrust);
1461 for (i = 0; i < puffs; i++) { // make puffs of smoke
1462 smkpos += diff * multiplier;
1463 newp = addParticle ();
1464 velvec[0] = rocketEjection[0] + rsRandf (20.0f) - 10.0f;
1465 velvec[1] = rocketEjection[1] + rsRandf (20.0f) - 10.0f;
1466 velvec[2] = rocketEjection[2] + rsRandf (20.0f) - 10.0f;
1467 newp->initSmoke (smkpos, velvec);
1468 newp->t = newp->tr = smokeTime[smokeTimeIndex];
1469 smokeTimeIndex++;
1470 if (smokeTimeIndex >= SMOKETIMES)
1471 smokeTimeIndex = 0;
1472 }
1473 } else { // just form smoke in place
1474 for (i = 0; i < puffs; i++) {
1475 smkpos += diff * multiplier;
1476 newp = addParticle ();
1477 velvec[0] = rsRandf (20.0f) - 10.0f;
1478 velvec[1] = rsRandf (20.0f) - 10.0f;
1479 velvec[2] = rsRandf (20.0f) - 10.0f;
1480 newp->initSmoke (smkpos, velvec);
1481 newp->t = newp->tr = smokeTime[smokeTimeIndex];
1482 smokeTimeIndex++;
1483 if (smokeTimeIndex >= SMOKETIMES)
1484 smokeTimeIndex = 0;
1485 }
1486 }
1487 }
1488
1489 switch (type) {
1490 case ROCKET:
1491 // Sparks thrusting from rockets
1492 if (life > endthrust) {
1493 rsVec diff = xyz - lastxyz;
1494
1495 // distance rocket traveled since last frame
1496 temp = diff.length ();
1497 sparkTrailLength += temp;
1498 // number of sparks to release
1499 int sparks = int (sparkTrailLength * 0.4f);
1500 sparkTrailLength -= float (sparks) * 2.5f;
1501
1502 rocketEjection = vel;
1503 rocketEjection.normalize ();
1504 rocketEjection *= -thrust * (life - endthrust);
1505 for (i = 0; i < sparks; i++) { // make sparks
1506 newp = addParticle ();
1507 newp->initStar ();
1508 newp->xyz = xyz - (diff * rsRandf (1.0f));
1509 newp->vel[0] = rocketEjection[0] + rsRandf (60.0f) - 30.0f;
1510 newp->vel[1] = rocketEjection[1] + rsRandf (60.0f) - 30.0f;
1511 newp->vel[2] = rocketEjection[2] + rsRandf (60.0f) - 30.0f;
1512 newp->rgb = rgb;
1513 newp->t = rsRandf (0.2f) + 0.1f;
1514 newp->tr = newp->t;
1515 newp->size = 8.0f * life;
1516 newp->displayList = flarelist[3];
1517 newp->makeSmoke = 0;
1518 }
1519 }
1520 break;
1521
1522 case FOUNTAIN: {
1523 // Stars shooting up from fountain
1524 // spew 10-20 particles per second at maximum brightness
1525 sparkTrailLength += elapsedTime * bright * (rsRandf (10.0f) + 10.0f);
1526 int stars = int (sparkTrailLength);
1527 sparkTrailLength -= float(stars);
1528
1529 for (i = 0; i < stars; i++) {
1530 newp = addParticle ();
1531 newp->initStar ();
1532 newp->drag = 0.342f; // terminal velocity is 40 ft/s
1533 newp->xyz = xyz;
1534 newp->xyz[1] += rsRandf (elapsedTime * 100.0f);
1535 if (newp->xyz[1] > 50.0f)
1536 newp->xyz[1] = 50.0f;
1537 newp->vel.set (rsRandf (20.0f) - 10.0f, rsRandf (30.0f) + 100.0f, rsRandf (20.0f) - 10.0f);
1538 newp->size = 10.0f;
1539 newp->rgb = rgb;
1540 newp->makeSmoke = 0;
1541 }
1542
1543 }
1544 break;
1545
1546 case SPINNER: {
1547 // Stars shooting out from spinner
1548 dir.set (1.0f, 0.0f, 0.0f);
1549 crossvec.cross (dir, tiltvec);
1550 crossvec.normalize ();
1551 crossvec *= 400.0f;
1552 temp = spin * elapsedTime; // radius of spin this frame
1553 // spew 90-100 particles per second at maximum brightness
1554 sparkTrailLength += elapsedTime * bright * (rsRandf (10.0f) + 90.0f);
1555 int stars = int (sparkTrailLength);
1556 sparkTrailLength -= float (stars);
1557
1558 for (i = 0; i < stars; i++) {
1559 spinquat.make (tilt + rsRandf (temp), tiltvec[0], tiltvec[1], tiltvec[2]);
1560 spinquat.toMat (spinmat.m);
1561 newp = addParticle ();
1562 newp->initStar ();
1563 newp->xyz = xyz;
1564 newp->vel.set (vel[0] - (spinmat[0] * crossvec[0] + spinmat[4] * crossvec[1] + spinmat[8] * crossvec[2]) + rsRandf (20.0f) - 10.0f,
1565 vel[1] - (spinmat[1] * crossvec[0] + spinmat[5] * crossvec[1] + spinmat[9] * crossvec[2]) + rsRandf (20.0f) - 10.0f,
1566 vel[2] - (spinmat[2] * crossvec[0] + spinmat[6] * crossvec[1] + spinmat[10] * crossvec[2]) + rsRandf (20.0f) - 10.0f);
1567 newp->size = 15.0f;
1568 newp->rgb = rgb;
1569 newp->makeSmoke = 0;
1570 newp->t = newp->tr = rsRandf (0.5f) + 1.5f;
1571 }
1572 tilt += temp;
1573
1574 }
1575 break;
1576
1577 case STREAMER: {
1578 // trail from streamers
1579 rsVec diff = xyz - lastxyz;
1580
1581 // distance streamer traveled since last frame
1582 sparkTrailLength += diff.length ();
1583 // number of sparks to release each frame
1584 int sparks = int (sparkTrailLength * 0.04f);
1585 sparkTrailLength -= float (sparks) * 25.0f;
1586
1587 for (i = 0; i < sparks; i++) {
1588 newp = addParticle ();
1589 newp->initStar ();
1590 newp->xyz = xyz - (diff * rsRandf (1.0f));
1591 newp->vel.set (vel[0] + rsRandf (80.0f) - 40.0f, vel[1] + rsRandf (80.0f) - 40.0f, vel[2] + rsRandf (80.0f) - 40.0f);
1592 newp->drag = 2.5f;
1593 newp->size = rsRandf (8.0f) + 4.0f;
1594 newp->rgb.set (1.0f, 0.8f, 0.6f);
1595 newp->t = rsRandf (2.0f) + 1.0f;
1596 newp->tr = newp->t;
1597 newp->makeSmoke = 0;
1598 }
1599
1600 }
1601 break;
1602
1603 case METEOR: {
1604 // trail from meteors
1605 rsVec diff = xyz - lastxyz;
1606
1607 // distance rocket traveled since last frame
1608 sparkTrailLength += diff.length ();
1609 // number of smoke puffs to release
1610 int stars = int (sparkTrailLength * 0.1f + 0.5f);
1611 rsVec smkpos = lastxyz;
1612
1613 // release star every 10 feet
1614 float multiplier = 10.0f / sparkTrailLength;
1615
1616 for (i = 0; i < stars; i++) {
1617 smkpos += diff * multiplier;
1618 newp = addParticle ();
1619 newp->initStar ();
1620 newp->xyz = smkpos;
1621 newp->vel.set (vel[0] + rsRandf (40.0f) - 20.0f, vel[1] + rsRandf (40.0f) - 20.0f, vel[2] + rsRandf (40.0f) - 20.0f);
1622 newp->rgb = rgb;
1623 newp->drag = 2.0f;
1624 newp->t = newp->tr = rsRandf (0.5f) + 1.5f;
1625 newp->size = 10.0f;
1626 newp->makeSmoke = 0;
1627 }
1628 sparkTrailLength -= float (stars) * 10.0f;
1629
1630 }
1631 break;
1632
1633 case BEE: {
1634 // trail from bees
1635 rsVec diff = xyz - lastxyz;
1636
1637 // distance rocket traveled since last frame
1638 sparkTrailLength += diff.length ();
1639 // number of smoke puffs to release
1640 int stars = int (sparkTrailLength * 0.1f + 0.5f);
1641 rsVec smkpos = lastxyz;
1642
1643 // release star every 10 feet
1644 float multiplier = 10.0f / sparkTrailLength;
1645
1646 for (i = 0; i < stars; i++) {
1647 smkpos += diff * multiplier;
1648 newp = addParticle ();
1649 newp->initStar ();
1650 newp->xyz = smkpos;
1651 newp->vel.set (rsRandf (100.0f) - 50.0f - vel[0] * 0.5f, rsRandf (100.0f) - 50.0f - vel[1] * 0.5f, rsRandf (100.0f) - 50.0f - vel[2] * 0.5f);
1652 newp->rgb = rgb;
1653 newp->t = newp->tr = rsRandf (0.1f) + 0.15f;
1654 newp->size = 7.0f;
1655 newp->displayList = flarelist[3];
1656 newp->makeSmoke = 0;
1657 }
1658 sparkTrailLength -= float (stars) * 10.0f;
1659
1660 }
1661 break;
1662
1663 case SUCKER:
1664 // pulling of particles by suckers
1665 pulling (this);
1666 break;
1667
1668 case SHOCKWAVE:
1669 // pushing of particles by shockwaves
1670 pushing (this);
1671 break;
1672
1673 case STRETCHER:
1674 // stretching of particles by stretchers
1675 stretching (this);
1676 }
1677
1678 // smoke and cloud illumination from rockets and explosions
1679 if (dIllumination && ((type == ROCKET) || (type == FOUNTAIN) || (type == EXPLOSION)))
1680 illuminate (this);
1681 }
1682
draw()1683 void particle::draw ()
1684 {
1685 if (life <= 0.0f)
1686 return; // don't draw dead particles
1687
1688 // cull small particles that are behind camera
1689 if (depth < 0.0f && type != SHOCKWAVE)
1690 return;
1691
1692 // don't draw invisible particles
1693 if (type == POPPER)
1694 return;
1695
1696 glPushMatrix ();
1697 glTranslatef (xyz[0], xyz[1], xyz[2]);
1698
1699 if (type == SHOCKWAVE) {
1700 glScalef (size, size, size);
1701 drawShockwave (life, float (sqrt (size)) * 0.08f);
1702
1703 if (life > 0.7f) { // Big torus just for fun
1704 glMultMatrixd (billboardMat);
1705 glScalef (5.0f, 5.0f, 5.0f);
1706 glColor4f (1.0f, life, 1.0f, (life - 0.7f) * 3.333f);
1707 glCallList (flarelist[2]);
1708 }
1709 glPopMatrix ();
1710 return;
1711 }
1712
1713 glScalef (size, size, size);
1714 glMultMatrixd (billboardMat);
1715 if (type == SMOKE) {
1716 glColor4f (rgb[0], rgb[1], rgb[2], bright);
1717 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1718 glCallList (displayList);
1719 } else {
1720 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
1721 if (type == EXPLOSION) {
1722 glColor4f (1.0f, 1.0f, 1.0f, bright);
1723 glScalef (bright, bright, bright);
1724 glCallList (displayList);
1725 } else {
1726 glColor4f (rgb[0], rgb[1], rgb[2], bright);
1727 glCallList (displayList);
1728 glScalef (0.35f, 0.35f, 0.35f);
1729 glColor4f (1.0f, 1.0f, 1.0f, bright);
1730 glCallList (displayList);
1731 }
1732 }
1733
1734 glPopMatrix ();
1735 }
1736