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 // Skyrocket screen saver
20 
21 #include <math.h>
22 #include <stdio.h>
23 #include <GL/gl.h>
24 #include <GL/glu.h>
25 
26 #include <list>
27 
28 #include "driver.h"
29 #include "rsDefines.h"
30 #include "rsRand.h"
31 #include "rsMath/rsVec.h"
32 #include "skyrocket_flare.h"
33 #include "skyrocket_particle.h"
34 #include "skyrocket_smoke.h"
35 #include "skyrocket_sound.h"
36 #include "skyrocket_shockwave.h"
37 #include "skyrocket_world.h"
38 
39 const char *hack_name = "skyrocket";
40 
41 double modelMat[16], projMat[16];
42 int viewport[4];
43 extern double billboardMat[16];
44 
45 // Global variables
46 int readyToDraw = 0;
47 
48 // list of particles
49 std::list < particle > particles;
50 // Time from one frame to the next
51 float elapsedTime = 0.0f;
52 
53 // Window variables
54 int xsize, ysize, centerx, centery;
55 float aspectRatio;
56 
57 // Camera variables
58 rsVec lookFrom[3];	// 3 = position, target position, last position
59 rsVec lookAt[3]		// 3 = position, target position, last position
60 	= { rsVec (0.0f, 1200.0f, 0.0f),
61 	rsVec (0.0f, 1200.0f, 0.0f),
62 	rsVec (0.0f, 1200.0f, 0.0f)
63 };
64 rsVec cameraPos;		// used for positioning sounds (same as lookFrom[0])
65 rsVec cameraVel;		// used for doppler shift
66 
67 // Mouse variables
68 float mouseIdleTime;
69 int mouseButtons, mousex, mousey;
70 float mouseSpeed;
71 
72 int numRockets = 0;
73 
74 #define MAXFLARES 110		// Required 100 and 10 extra for good measure
75 float allflares[MAXFLARES][7];	// type, x, y, r, g, b, alpha
76 int numFlares = 0;
77 
78 // Parameters edited in the dialog box
79 int dMaxrockets;
80 int dSmoke;
81 int dExplosionsmoke;
82 int dWind;
83 int dAmbient;
84 int dStardensity;
85 int dFlare;
86 int dMoonglow;
87 int dMoon;
88 int dClouds;
89 int dEarth;
90 int dIllumination;
91 int dSound;
92 int dPriority;
93 
94 // Commands given from keyboard
95 int kAction = 1;
96 int kCamera = 1;		// 0 = paused, 1 = autonomous, 2 = mouse control
97 int kNewCamera = 0;
98 int kSlowMotion = 0;
99 int userDefinedExplosion = -1;
100 
addParticle()101 particle *addParticle ()
102 {
103 	static particle *tempPart;
104 
105 	particles.push_back (particle ());
106 	tempPart = &(particles.back ());
107 	tempPart->depth = 10000;
108 
109 	return tempPart;
110 }
111 
112 // Makes list of lens flares.  Must be a called even when action is paused
113 // because camera might still be moving.
makeFlareList()114 void makeFlareList ()
115 {
116 	rsVec cameraDir, partDir;
117 
118 	cameraDir = lookAt[0] - lookFrom[0];
119 	cameraDir.normalize ();
120 	std::list < particle >::iterator curlight = particles.begin ();
121 	while (curlight != particles.end () && numFlares < MAXFLARES) {
122 		if (curlight->type == EXPLOSION) {
123 			double winx, winy, winz;
124 
125 			gluProject (curlight->xyz[0], curlight->xyz[1], curlight->xyz[2], modelMat, projMat, viewport, &winx, &winy, &winz);
126 			partDir = curlight->xyz - cameraPos;
127 			if (partDir.dot (cameraDir) > 1.0f) {	// is light source in front of camera?
128 				allflares[numFlares][0] = 0;
129 				allflares[numFlares][1] = (float (winx) / float (xsize))*aspectRatio;
130 				allflares[numFlares][2] = float (winy) / float (ysize);
131 
132 				allflares[numFlares][3] = curlight->rgb[0];
133 				allflares[numFlares][4] = curlight->rgb[1];
134 				allflares[numFlares][5] = curlight->rgb[2];
135 				rsVec vector = curlight->xyz - cameraPos;	// find distance attenuation factor
136 				float distatten = (10000.0f - vector.length ()) * 0.0001f;
137 
138 				if (distatten < 0.0f)
139 					distatten = 0.0f;
140 				allflares[numFlares][6] = curlight->bright * float (dFlare) * 0.01f * distatten;
141 
142 				numFlares++;
143 			}
144 		}
145 		if (curlight->type == SUCKER || curlight->type == SHOCKWAVE || curlight->type == STRETCHER || curlight->type == BIGMAMA) {
146 			double winx, winy, winz;
147 			float temp;
148 
149 			gluProject (curlight->xyz[0], curlight->xyz[1], curlight->xyz[2], modelMat, projMat, viewport, &winx, &winy, &winz);
150 			partDir = curlight->xyz - cameraPos;
151 			if (partDir.dot (cameraDir) > 1.0f) {	// is light source in front of camera?
152 				allflares[numFlares][0] = 1;	// superFlare
153 				allflares[numFlares][1] = (float (winx) / float (xsize))*aspectRatio;
154 				allflares[numFlares][2] = float (winy) / float (ysize);
155 
156 				allflares[numFlares][3] = curlight->rgb[0];
157 				allflares[numFlares][4] = curlight->rgb[1];
158 				allflares[numFlares][5] = curlight->rgb[2];
159 				rsVec vector = curlight->xyz - cameraPos;	// find distance attenuation factor
160 				float distatten = (20000.0f - vector.length ()) * 0.00005f;
161 
162 				if (distatten < 0.0f)
163 					distatten = 0.0f;
164 				temp = 1.0f - (curlight->bright - 0.5f) * float (dFlare) * 0.02f;
165 
166 				allflares[numFlares][6] = (1.0f - temp * temp * temp * temp) * distatten;
167 				numFlares++;
168 			}
169 		}
170 		curlight++;
171 	}
172 }
173 
draw()174 void draw ()
175 {
176 	int i, j;
177 	static int lastCameraMode = kCamera;
178 	// time, elapsed time, step (1.0 - 0.0)
179 	static float cameraTime[3] = { 20.0f, 0.0f, 0.0f };
180 
181 	// super fast easter egg
182 	if (!rsRandi (2000))
183 		elapsedTime *= 5.0f;
184 
185 	// build viewing matrix
186 	glMatrixMode (GL_PROJECTION);
187 	glLoadIdentity ();
188 	gluPerspective (60.0f, aspectRatio, 1.0f, 40000.0f);
189 	glGetDoublev (GL_PROJECTION_MATRIX, projMat);
190 
191 	if (kNewCamera) {	// Make new random camera view
192 		cameraTime[0] = rsRandf (25.0f) + 5.0f;
193 		cameraTime[1] = 0.0f;
194 		cameraTime[2] = 0.0f;
195 		// choose new positions
196 		lookFrom[1][0] = rsRandf (6000.0f) - 3000.0f;	// new target position
197 		lookFrom[1][1] = rsRandf (1700.0f) + 5.0f;
198 		lookFrom[1][2] = rsRandf (6000.0f) - 3000.0f;
199 		lookAt[1][0] = rsRandf (1000.0f) - 500.0f;	// new target position
200 		lookAt[1][1] = rsRandf (1100.0f) + 200.0f;
201 		lookAt[1][2] = rsRandf (1000.0f) - 500.0f;
202 		// cut to a new view
203 		lookFrom[2][0] = rsRandf (6000.0f) - 3000.0f;	// new last position
204 		lookFrom[2][1] = rsRandf (1700.0f) + 5.0f;
205 		lookFrom[2][2] = rsRandf (6000.0f) - 3000.0f;
206 		lookAt[2][0] = rsRandf (1000.0f) - 500.0f;	// new last position
207 		lookAt[2][1] = rsRandf (1100.0f) + 200.0f;
208 		lookAt[2][2] = rsRandf (1000.0f) - 500.0f;
209 		kNewCamera = 0;
210 	}
211 
212 	if (kCamera == 1) {	// if the camera is active
213 		if (lastCameraMode == 2) {	// camera was controlled by mouse last frame
214 			cameraTime[0] = 10.0f;
215 			cameraTime[1] = 0.0f;
216 			cameraTime[2] = 0.0f;
217 			lookFrom[2] = lookFrom[0];
218 			lookFrom[1][0] = rsRandf (6000.0f) - 3000.0f;	// new target position
219 			lookFrom[1][1] = rsRandf (1700.0f) + 5.0f;
220 			lookFrom[1][2] = rsRandf (6000.0f) - 3000.0f;
221 			lookAt[2] = lookAt[0];
222 			lookAt[1][0] = rsRandf (1000.0f) - 500.0f;	// new target position
223 			lookAt[1][1] = rsRandf (1100.0f) + 200.0f;
224 			lookAt[1][2] = rsRandf (1000.0f) - 500.0f;
225 		}
226 
227 		cameraTime[1] += elapsedTime;
228 		cameraTime[2] = cameraTime[1] / cameraTime[0];
229 		if (cameraTime[2] >= 1.0f) {	// reset camera sequence
230 			// reset timer
231 			cameraTime[0] = rsRandf (25.0f) + 5.0f;
232 			cameraTime[1] = 0.0f;
233 			cameraTime[2] = 0.0f;
234 			// choose new positions
235 			lookFrom[2] = lookFrom[1];	// last = target
236 			lookFrom[1][0] = rsRandf (6000.0f) - 3000.0f;	// new target position
237 			lookFrom[1][1] = rsRandf (1700.0f) + 5.0f;
238 			lookFrom[1][2] = rsRandf (6000.0f) - 3000.0f;
239 			lookAt[2] = lookAt[1];	// last = target
240 			lookAt[1][0] = rsRandf (1000.0f) - 500.0f;	// new target position
241 			lookAt[1][1] = rsRandf (1100.0f) + 200.0f;
242 			lookAt[1][2] = rsRandf (1000.0f) - 500.0f;
243 			if (!rsRandi (3)) {	// cut to a new view
244 				lookFrom[2][0] = rsRandf (6000.0f) - 3000.0f;	// new last position
245 				lookFrom[2][1] = rsRandf (1700.0f) + 5.0f;
246 				lookFrom[2][2] = rsRandf (6000.0f) - 3000.0f;
247 				lookAt[2][0] = rsRandf (1000.0f) - 500.0f;	// new last position
248 				lookAt[2][1] = rsRandf (1100.0f) + 200.0f;
249 				lookAt[2][2] = rsRandf (1000.0f) - 500.0f;
250 			}
251 		}
252 
253 		// change camera position and angle
254 		float cameraStep = 0.5f * (1.0f - cos (cameraTime[2] * PI));
255 
256 		lookFrom[0] = lookFrom[2] + ((lookFrom[1] - lookFrom[2]) * cameraStep);
257 		lookAt[0] = lookAt[2] + ((lookAt[1] - lookAt[2]) * cameraStep);
258 		// update variables used for sound and lens flares
259 		cameraVel = lookFrom[0] - cameraPos;
260 		cameraPos = lookFrom[0];
261 	}
262 
263 	// Build modelview matrix
264 	// Don't use gluLookAt() because it's easier to find the billboard matrix
265 	// if we know the heading and pitch
266 	glMatrixMode (GL_MODELVIEW);
267 	glLoadIdentity ();
268 	static float heading, pitch;
269 
270 	// Control camera with the mouse
271 	if (kCamera == 2) {
272 		heading += 100.0f * elapsedTime * aspectRatio * float (centerx - mousex) / float (xsize);
273 		pitch += 100.0f * elapsedTime * float (centery - mousey) / float (ysize);
274 
275 		if (heading > 180.0f)
276 			heading -= 360.0f;
277 		if (heading < -180.0f)
278 			heading += 360.0f;
279 		if (pitch > 90.0f)
280 			pitch = 90.0f;
281 		if (pitch < -90.0f)
282 			pitch = -90.0f;
283 /*
284 		if(mouseButtons & MK_LBUTTON)
285 			mouseSpeed += 400.0f * elapsedTime;
286 		if(mouseButtons & MK_RBUTTON)
287 			mouseSpeed -= 400.0f * elapsedTime;
288 		if((mouseButtons & MK_MBUTTON) || ((mouseButtons & MK_LBUTTON) && (mouseButtons & MK_RBUTTON)))
289 			mouseSpeed = 0.0f;
290 		if(mouseSpeed > 4000.0f)
291 			mouseSpeed = 4000.0f;
292 		if(mouseSpeed < -4000.0f)
293 			mouseSpeed = -4000.0f;
294 */
295 		float ch = cos (DEG2RAD * heading);
296 		float sh = sin (DEG2RAD * heading);
297 		float cp = cos (DEG2RAD * pitch);
298 		float sp = sin (DEG2RAD * pitch);
299 
300 		lookFrom[0][0] -= mouseSpeed * sh * cp * elapsedTime;
301 		lookFrom[0][1] += mouseSpeed * sp * elapsedTime;
302 		lookFrom[0][2] -= mouseSpeed * ch * cp * elapsedTime;
303 		cameraPos = lookFrom[0];
304 		// Calculate new lookAt position so that lens flares will be computed correctly
305 		// and so that transition back to autonomous camera mode is smooth
306 		lookAt[0][0] = lookFrom[0][0] - 500.0f * sh * cp;
307 		lookAt[0][1] = lookFrom[0][1] + 500.0f * sp;
308 		lookAt[0][2] = lookFrom[0][2] - 500.0f * ch * cp;
309 	} else {
310 		float radius = sqrt ((lookAt[0][0] - lookFrom[0][0]) * (lookAt[0][0] - lookFrom[0][0])
311 				     + (lookAt[0][2] - lookFrom[0][2]) * (lookAt[0][2] - lookFrom[0][2]));
312 
313 		pitch = RAD2DEG * atan2 (lookAt[0][1] - lookFrom[0][1], radius);
314 		heading = RAD2DEG * atan2 (lookFrom[0][0] - lookAt[0][0], lookFrom[0][2] - lookAt[0][2]);
315 	}
316 
317 	glRotatef (-pitch, 1, 0, 0);
318 	glRotatef (-heading, 0, 1, 0);
319 	glTranslatef (-lookFrom[0][0], -lookFrom[0][1], -lookFrom[0][2]);
320 	// get modelview matrix for flares
321 	glGetDoublev (GL_MODELVIEW_MATRIX, modelMat);
322 
323 	// store this frame's camera mode for next frame
324 	lastCameraMode = kCamera;
325 
326 	// Update mouse idle time
327 	if (kCamera == 2) {
328 		mouseIdleTime += elapsedTime;
329 		if (mouseIdleTime > 300.0f)	// return to autonomous camera mode after 5 minutes
330 			kCamera = 1;
331 	}
332 
333 	// update billboard rotation matrix for particles
334 	glPushMatrix ();
335 	glLoadIdentity ();
336 	glRotatef (heading, 0, 1, 0);
337 	glRotatef (pitch, 1, 0, 0);
338 	glGetDoublev (GL_MODELVIEW_MATRIX, billboardMat);
339 	glPopMatrix ();
340 
341 	// clear the screen
342 	glClear (GL_COLOR_BUFFER_BIT);
343 
344 	// Slows fireworks, but not camera
345 	if (kSlowMotion)
346 		elapsedTime *= 0.5f;
347 
348 	// Pause the animation?
349 	if (kAction) {
350 		// update world
351 		updateWorld ();
352 
353 		// darken smoke
354 		std::list < particle >::iterator darkener = particles.begin ();
355 		static float ambientlight = float (dAmbient) * 0.01f;
356 
357 		while (darkener != particles.end ()) {
358 			if (darkener->type == SMOKE)
359 				darkener->rgb[0] = darkener->rgb[1] = darkener->rgb[2] = ambientlight;
360 			darkener++;
361 		}
362 
363 		// Change rocket firing rate
364 		static float rocketTimer = 0.0f;
365 		static float rocketTimeConst = 10.0f / float (dMaxrockets);
366 		static float changeRocketTimeConst = 20.0f;
367 
368 		changeRocketTimeConst -= elapsedTime;
369 		if (changeRocketTimeConst <= 0.0f) {
370 			float temp = rsRandf (4.0f);
371 
372 			rocketTimeConst = (temp * temp) + (10.0f / float (dMaxrockets));
373 			changeRocketTimeConst = rsRandf (30.0f) + 10.0f;
374 		}
375 
376 		// add new rocket to list
377 		rocketTimer -= elapsedTime;
378 		if ((rocketTimer <= 0.0f) || (userDefinedExplosion >= 0)) {
379 			if (numRockets < dMaxrockets) {
380 				particle *rock = addParticle ();
381 
382 				if (rsRandi (30) || (userDefinedExplosion >= 0)) {	// Usually launch a rocket
383 					rock->initRocket ();
384 					if (userDefinedExplosion >= 0)
385 						rock->explosiontype = userDefinedExplosion;
386 					else {
387 						if (!rsRandi (3000)) {	// big one!
388 							if (rsRandi (2))
389 								rock->explosiontype = 19;	// sucker and shockwave
390 							else
391 								rock->explosiontype = 20;	// stretcher and bigmama
392 						} else {
393 							// Distribution of regular explosions
394 							if (rsRandi (2)) {	// 0 - 2 (all types of spheres)
395 								if (!rsRandi (10))
396 									rock->explosiontype = 2;
397 								else
398 									rock->explosiontype = rsRandi (2);
399 							} else {
400 								if (!rsRandi (3))	//  ring, double sphere, sphere and ring
401 									rock->explosiontype = rsRandi (3) + 3;
402 								else {
403 									if (rsRandi (2)) {	// 6, 7, 8, 9, 10, 11
404 										if (rsRandi (2))
405 											rock->explosiontype = rsRandi (2) + 6;
406 										else
407 											rock->explosiontype = rsRandi (4) + 8;
408 									} else {
409 										if (rsRandi (2))	// 12, 13, 14
410 											rock->explosiontype = rsRandi (3) + 12;
411 										else	// 15 - 18
412 											rock->explosiontype = rsRandi (4) + 15;
413 									}
414 								}
415 							}
416 						}
417 					}
418 					numRockets++;
419 				} else {	// sometimes make fountains and spinners instead of rockets
420 					rock->initFountain ();
421 					i = rsRandi (2);
422 					for (j = 0; j < i; j++) {
423 						rock = addParticle ();
424 						rock->initFountain ();
425 					}
426 				}
427 			}
428 
429 			if (dMaxrockets)
430 				rocketTimer = rsRandf (rocketTimeConst);
431 			else
432 				rocketTimer = 60.0f;	// arbitrary number since no rockets ever fire
433 
434 			if (userDefinedExplosion >= 0) {
435 				userDefinedExplosion = -1;
436 				rocketTimer = 20.0f;	// Wait 20 seconds after user launches a rocket before launching any more
437 			}
438 		}
439 
440 		// update particles
441 		numRockets = 0;
442 		std::list < particle >::iterator curpart = particles.begin ();
443 		while (curpart != particles.end ()) {
444 			curpart->update ();
445 
446 			if (curpart->type == ROCKET)
447 				numRockets++;
448 
449 			curpart->findDepth ();
450 			if (curpart->life <= 0.0f || curpart->xyz[1] < 0.0f) {
451 				if (curpart->type == ROCKET) {	// become explosion
452 					if (curpart->xyz[1] <= 0.0f) {	// move above ground for explosion
453 						curpart->xyz[1] = 0.1f;
454 						curpart->vel[1] *= -0.7f;
455 					}
456 					if (curpart->explosiontype == 18)
457 						curpart->initSpinner ();
458 					else
459 						curpart->initExplosion ();
460 				} else if (curpart->type == POPPER) {
461 					switch (curpart->explosiontype) {
462 					case STAR:
463 						curpart->explosiontype = 100;
464 						curpart->initExplosion ();
465 						break;
466 					case STREAMER:
467 						curpart->explosiontype = 101;
468 						curpart->initExplosion ();
469 						break;
470 					case METEOR:
471 						curpart->explosiontype = 102;
472 						curpart->initExplosion ();
473 						break;
474 					case POPPER:
475 						curpart->type = STAR;
476 						curpart->rgb.set (1.0f, 0.8f, 0.6f);
477 						curpart->t = curpart->tr = 0.2f;
478 					}
479 				} else if (curpart->type == SUCKER) {
480 					curpart->initShockwave ();
481 				} else if (curpart->type == STRETCHER) {	// become big explosion
482 					curpart->initBigmama ();
483 				} else	// remove particles from list
484 					curpart = particles.erase(curpart)--;
485 			}
486 
487 			curpart++;
488 		}
489 	} else {
490 		// Only sort particles if they're not being updated (the camera could still be moving)
491 		std::list < particle >::iterator curpart = particles.begin ();
492 		while (curpart != particles.end ()) {
493 			curpart->findDepth ();
494 			curpart++;
495 		}
496 	}
497 
498 	// the world
499 	drawWorld ();
500 
501 	// draw particles
502 	glEnable (GL_BLEND);
503 	std::list < particle >::iterator drawer = particles.begin ();
504 	while (drawer != particles.end ()) {
505 		drawer->draw ();
506 		drawer++;
507 	}
508 
509 	// draw lens flares
510 	if (dFlare) {
511 		makeFlareList ();
512 		for (i = 0; i < numFlares; i++) {
513 			if (allflares[i][0] == 0)
514 				flare (allflares[i][1], allflares[i][2], allflares[i][3], allflares[i][4], allflares[i][5], allflares[i][6]);
515 			else
516 				superFlare (allflares[i][1], allflares[i][2], allflares[i][3], allflares[i][4], allflares[i][5], allflares[i][6]);
517 		}
518 		numFlares = 0;
519 	}
520 
521 #ifdef HAVE_OPENAL
522 	// do sound stuff
523 	if (dSound) {
524 		float listenerOri[6];
525 		listenerOri[0] = float (-modelMat[2]);
526 		listenerOri[1] = float (-modelMat[6]);
527 		listenerOri[2] = float (-modelMat[10]);
528 		listenerOri[3] = float (modelMat[1]);
529 		listenerOri[4] = float (modelMat[5]);
530 		listenerOri[5] = float (modelMat[9]);
531 
532 		updateSound (cameraPos.v, cameraVel.v, listenerOri);
533 	}
534 #endif
535 
536 	glFlush ();
537 }
538 
hack_draw(xstuff_t * XStuff,double currentTime,float frameTime)539 void hack_draw (xstuff_t * XStuff, double currentTime, float frameTime)
540 {
541 	static float times[5] = { 0.03f, 0.03f, 0.03f, 0.03f, 0.03f };
542 	static int timeindex = 0;
543 #ifdef BENCHMARK
544 	static int a = 1;
545 #endif
546 
547 	if (frameTime > 0) {
548 		times[timeindex] = frameTime;
549 	} else {		// else use elapsedTime from last frame
550 		times[timeindex] = elapsedTime;
551 	}
552 
553 #ifdef BENCHMARK
554 	times[timeindex] = 0.03f;
555 #endif
556 
557 	// average last 5 frame times together
558 	elapsedTime = 0.2f * (times[0] + times[1] + times[2] + times[3] + times[4]);
559 
560 	timeindex++;
561 	if (timeindex >= 5)
562 		timeindex = 0;
563 
564 	if (elapsedTime > 0.0f)
565 		draw ();
566 
567 #ifdef BENCHMARK
568 	if (a++ == 1000)
569 		exit(0);
570 #endif
571 }
572 
hack_reshape(xstuff_t * XStuff)573 void hack_reshape (xstuff_t * XStuff)
574 {
575 	xsize = XStuff->windowWidth;
576 	ysize = XStuff->windowHeight;
577 
578 	centerx = xsize / 2;
579 	centery = ysize / 2;
580 
581 	glViewport (0, 0, xsize, ysize);
582 	glGetIntegerv (GL_VIEWPORT, viewport);
583 	aspectRatio = float (xsize) / float (ysize);
584 }
585 
hack_init(xstuff_t * XStuff)586 void hack_init (xstuff_t * XStuff)
587 {
588 	hack_reshape (XStuff);
589 
590 	// Set OpenGL state
591 	glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
592 	glDisable (GL_DEPTH_TEST);
593 	glEnable (GL_TEXTURE_2D);
594 	glFrontFace (GL_CCW);
595 	glEnable (GL_CULL_FACE);
596 
597 	// Initialize data structures
598 	initFlares ();
599 	if (dSmoke)
600 		initSmoke ();
601 	initWorld ();
602 	initShockwave ();
603 
604 	lookFrom[1] = rsVec (rsRandf (3000.0f) - 1500.0f, 400.0f, rsRandf (3000.0f) - 1500.0f);
605 	lookFrom[2] = rsVec (rsRandf (1000.0f) + 5000.0f, 5.0f, rsRandf (4000.0f) - 2000.0f);
606 
607 #ifdef HAVE_OPENAL
608 	if (dSound)
609 		initSound ();
610 #endif
611 }
612 
hack_cleanup(xstuff_t * XStuff)613 void hack_cleanup (xstuff_t * XStuff)
614 {
615 	cleanupFlares ();
616 	cleanupSmoke ();
617 	cleanupWorld ();
618 #ifdef HAVE_OPENAL
619 	cleanupSound ();
620 #endif
621 }
622 
hack_handle_opts(int argc,char ** argv)623 void hack_handle_opts (int argc, char **argv)
624 {
625 	dMaxrockets = 8;
626 	dSmoke = 5;
627 	dExplosionsmoke = 0;
628 	dWind = 20;
629 	dAmbient = 10;
630 	dStardensity = 20;
631 	dFlare = 20;
632 	dMoonglow = 20;
633 	dSound = 0;
634 	dMoon = 1;
635 	dClouds = 1;
636 	dEarth = 1;
637 	dIllumination = 1;
638 
639 	while (1) {
640 		int c;
641 
642 #ifdef HAVE_GETOPT_H
643 		static struct option long_options[] = {
644 			{"help", 0, 0, 'h'},
645 			DRIVER_OPTIONS_LONG {"maxrockets", 1, 0, 'm'},
646 			{"smoke", 1, 0, 's'},
647 			{"explosionsmoke", 1, 0, 'S'},
648 			{"wind", 1, 0, 'w'},
649 			{"ambient", 1, 0, 'a'},
650 			{"stardensity", 1, 0, 'd'},
651 			{"flare", 1, 0, 'f'},
652 			{"moonglow", 1, 0, 'g'},
653 			{"volume", 1, 0, 'v'},
654 			{"moon", 0, 0, 'o'},
655 			{"no-moon", 0, 0, 'O'},
656 			{"clouds", 0, 0, 'c'},
657 			{"no-clouds", 0, 0, 'C'},
658 			{"earth", 0, 0, 'e'},
659 			{"no-earth", 0, 0, 'E'},
660 			{"illumination", 0, 0, 'i'},
661 			{"no-illumination", 0, 0, 'I'},
662 			{0, 0, 0, 0}
663 		};
664 
665 		c = getopt_long (argc, argv, DRIVER_OPTIONS_SHORT "hm:s:S:a:d:f:g:v:oOcCeEiI", long_options, NULL);
666 #else
667 		c = getopt (argc, argv, DRIVER_OPTIONS_SHORT "hm:s:S:a:d:f:g:v:oOcCeEiI");
668 #endif
669 		if (c == -1)
670 			break;
671 
672 		switch (c) {
673 			DRIVER_OPTIONS_CASES case 'h':printf ("%s:"
674 #ifndef HAVE_GETOPT_H
675 							      " Not built with GNU getopt.h, long options *NOT* enabled."
676 #endif
677 							      "\n" DRIVER_OPTIONS_HELP "\t--maxrockets/-m <arg>\n" "\t--smoke/-s <arg>\n" "\t--explosionsmoke/-S <arg>\n"
678 							      "\t--wind/-w <arg>\n" "\t--ambient/-a <arg>\n" "\t--stardensity/-d <arg>\n" "\t--flare/-f <arg>\n"
679 							      "\t--moonglow/-g <arg>\n" "\t--volume/-v <arg>\n" "\t--moon/-o\n" "\t--no-moon/-O\n" "\t--clouds/-c\n"
680 							      "\t--no-clouds/-C\n" "\t--earth/-e\n" "\t--no-earth/-E\n" "\t--illumination/-i\n" "\t--no-illumination/-I\n",
681 							      argv[0]);
682 			exit (1);
683 		case 'm':
684 			dMaxrockets = strtol_minmaxdef (optarg, 10, 1, 100, 1, 8, "--maxrockets: ");
685 			break;
686 		case 's':
687 			dSmoke = strtol_minmaxdef (optarg, 10, 0, 60, 1, 5, "--smoke: ");
688 			break;
689 		case 'S':
690 			dExplosionsmoke = strtol_minmaxdef (optarg, 10, 0, 100, 1, 0, "--explosionsmoke: ");
691 			break;
692 		case 'w':
693 			dWind = strtol_minmaxdef (optarg, 10, 0, 100, 1, 20, "--wind: ");
694 			break;
695 		case 'a':
696 			dAmbient = strtol_minmaxdef (optarg, 10, 0, 100, 1, 10, "--ambient: ");
697 			break;
698 		case 'd':
699 			dStardensity = strtol_minmaxdef (optarg, 10, 0, 100, 1, 20, "--stardensity: ");
700 			break;
701 		case 'f':
702 			dFlare = strtol_minmaxdef (optarg, 10, 0, 100, 1, 20, "--flare: ");
703 			break;
704 		case 'g':
705 			dMoonglow = strtol_minmaxdef (optarg, 10, 0, 100, 1, 20, "--moonglow: ");
706 			break;
707 		case 'v':
708 			dSound = strtol_minmaxdef (optarg, 10, 0, 100, 1, 0, "--volume: ");
709 			break;
710 		case 'o':
711 			dMoon = 1;
712 			break;
713 		case 'O':
714 			dMoon = 0;
715 			break;
716 		case 'c':
717 			dClouds = 1;
718 			break;
719 		case 'C':
720 			dClouds = 0;
721 			break;
722 		case 'e':
723 			dEarth = 1;
724 			break;
725 		case 'E':
726 			dEarth = 0;
727 			break;
728 		case 'i':
729 			dIllumination = 1;
730 			break;
731 		case 'I':
732 			dIllumination = 0;
733 			break;
734 		}
735 	}
736 }
737 
738 /*
739 LONG ScreenSaverProc(HWND hwnd, UINT msg, WPARAM wpm, LPARAM lpm){
740 	switch(msg){
741 	case WM_CREATE:
742 		readRegistry();
743 		initSaver(hwnd);
744 		switch(dPriority){
745 		case 0:
746 			SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
747 			break;
748 		case 1:
749 			SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
750 			break;
751 		case 2:
752 			SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
753 		}
754 		readyToDraw = 1;
755 		break;
756 	case WM_KEYDOWN:
757 		switch(int(wpm)){
758 		// pause the motion of the fireworks
759 		case 'a':
760 		case 'A':
761 			if(kAction)
762 				kAction = 0;
763 			else
764 				kAction = 1;
765 			if(kSlowMotion)
766 				kSlowMotion = 0;
767 			return(0);
768 		// pause the motion of the camera
769 		case 'c':
770 		case 'C':
771 			if(kCamera == 0)
772 				kCamera = 1;
773 			else{
774 				if(kCamera == 1)
775 					kCamera = 0;
776 				else{
777 					if(kCamera == 2)
778 						kCamera = 1;
779 				}
780 			}
781 			return(0);
782 		// toggle mouse camera control
783 		case 'm':
784 		case 'M':
785 			if(kCamera == 2)
786 				kCamera = 1;
787 			else{
788 				kCamera = 2;
789 				mouseSpeed = 0.0f;
790 				mouseIdleTime = 0.0f;
791 			}
792 			return(0);
793 		// new camera view
794 		case 'n':
795 		case 'N':
796 			kNewCamera = 1;
797 			return(0);
798 		// slow the motion of the fireworks
799 		case 's':
800 		case 'S':
801 			if(kSlowMotion)
802 				kSlowMotion = 0;
803 			else
804 				kSlowMotion = 1;
805 			if(!kAction)
806 				kAction = 1;
807 			return(0);
808 		// choose which type of rocket explosion
809 		case '1':
810 		case '2':
811 		case '3':
812 		case '4':
813 		case '5':
814 		case '6':
815 		case '7':
816 		case '8':
817 		case '9':
818 			userDefinedExplosion = int(wpm) - 49;  // explosions 0 - 8
819 			return(0);
820 		case '0':
821 			userDefinedExplosion = 9;
822 			return(0);
823 		case 'q':
824 		case 'Q':
825 			userDefinedExplosion = 10;
826 			return(0);
827 		case 'w':
828 		case 'W':
829 			userDefinedExplosion = 11;
830 			return(0);
831 		case 'e':
832 		case 'E':
833 			userDefinedExplosion = 12;
834 			return(0);
835 		case 'r':
836 		case 'R':
837 			userDefinedExplosion = 13;
838 			return(0);
839 		case 't':
840 		case 'T':
841 			userDefinedExplosion = 14;
842 			return(0);
843 		case 'y':
844 		case 'Y':
845 			userDefinedExplosion = 15;
846 			return(0);
847 		case 'u':
848 		case 'U':
849 			userDefinedExplosion = 16;
850 			return(0);
851 		case 'i':
852 		case 'I':
853 			userDefinedExplosion = 17;
854 			return(0);
855 		case 'o':
856 		case 'O':
857 			userDefinedExplosion = 18;  // spinner
858 			return(0);
859 		case 'b':
860 		case 'B':
861 		case 'd':
862 		case 'D':
863 		case 'f':
864 		case 'F':
865 		case 'g':
866 		case 'G':
867 		case 'h':
868 		case 'H':
869 		case 'j':
870 		case 'J':
871 		case 'k':
872 		case 'K':
873 		case 'l':
874 		case 'L':
875 		case 'p':
876 		case 'P':
877 		case 'v':
878 		case 'V':
879 		case 'x':
880 		case 'X':
881 		case 'z':
882 		case 'Z':
883 			// These letters do nothing
884 			// Disabling them makes it harder to make mistakes
885 			return(0);
886 		}
887 		break;
888 	case WM_MOUSEMOVE:
889 	case WM_LBUTTONDOWN:
890 	case WM_LBUTTONUP:
891 	case WM_MBUTTONDOWN:
892 	case WM_MBUTTONUP:
893 	case WM_RBUTTONDOWN:
894 	case WM_RBUTTONUP:
895 		if(kCamera == 2){
896 			mouseButtons = wpm;        // key flags
897 			mousex = LOWORD(lpm);  // horizontal position of cursor
898 			mousey = HIWORD(lpm);
899 			mouseIdleTime = 0.0f;
900 			return(0);
901 		}
902 		break;
903 	case WM_DESTROY:
904 		readyToDraw = 0;
905 		cleanup(hwnd);
906 		break;
907 	}
908 	return DefScreenSaverProc(hwnd, msg, wpm, lpm);
909 }
910 */
911