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