1 /*
2  * Copyright (C) 2002  Terence M. Welsh
3  * Ported to Linux by Tugrul Galatali <tugrul@galatali.com>
4  *
5  * Solar Winds 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  * Solar Winds 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 // Solar Winds screen saver
20 
21 #include <math.h>
22 #include <stdio.h>
23 #include <GL/gl.h>
24 #include <GL/glu.h>
25 
26 #include "driver.h"
27 #include "rsDefines.h"
28 #include "rsRand.h"
29 
30 const char *hack_name = "solarwinds";
31 
32 #define DEFAULTS1 1
33 #define DEFAULTS2 2
34 #define DEFAULTS3 3
35 #define DEFAULTS4 4
36 #define DEFAULTS5 5
37 #define DEFAULTS6 6
38 
39 #define NUMCONSTS 9
40 #define LIGHTSIZE 64
41 
42 class wind;
43 
44 // Global variables
45 wind *winds;
46 float lumdiff;
47 float cosCameraAngle, sinCameraAngle;
48 unsigned char lightTexture[LIGHTSIZE][LIGHTSIZE];
49 
50 // Parameters edited in the dialog box
51 int dWinds;
52 int dEmitters;
53 int dParticles;
54 int dGeometry;
55 int dSize;
56 int dParticlespeed;
57 int dEmitterspeed;
58 int dWindspeed;
59 int dBlur;
60 
61 class wind {
62       public:
63 	float **emitters;
64 	float **particles;
65 	int **linelist;
66 	int *lastparticle;
67 	int whichparticle;
68 	float c[NUMCONSTS];
69 	float ct[NUMCONSTS];
70 	float cv[NUMCONSTS];
71 
72 	  wind ();
73 	 ~wind ();
74 	void update ();
75 };
76 
wind()77 wind::wind ()
78 {
79 	int i;
80 
81 	emitters = new float *[dEmitters];
82 
83 	for (i = 0; i < dEmitters; i++) {
84 		emitters[i] = new float[3];
85 
86 		emitters[i][0] = rsRandf (60.0f) - 30.0f;
87 		emitters[i][1] = rsRandf (60.0f) - 30.0f;
88 		emitters[i][2] = rsRandf (30.0f) - 15.0f;
89 	}
90 
91 	particles = new float *[dParticles];
92 
93 	for (i = 0; i < dParticles; i++) {
94 		particles[i] = new float[6];	// 3 for pos, 3 for color
95 
96 		particles[i][2] = 100.0f;	// start particles behind viewer
97 	}
98 
99 	whichparticle = 0;
100 
101 	if (dGeometry == 2) {	// allocate memory for lines
102 		linelist = new int *[dParticles];
103 
104 		for (i = 0; i < dParticles; i++) {
105 			linelist[i] = new int[2];
106 
107 			linelist[i][0] = -1;
108 			linelist[i][1] = -1;
109 		}
110 		lastparticle = new int[dEmitters];
111 
112 		for (i = 0; i < dEmitters; i++)
113 			lastparticle[i] = i;
114 	}
115 
116 	for (i = 0; i < NUMCONSTS; i++) {
117 		ct[i] = rsRandf (PIx2);
118 		cv[i] = rsRandf (0.00005f * float (dWindspeed) * float (dWindspeed))
119 		+ 0.00001f * float (dWindspeed) * float (dWindspeed);
120 	}
121 }
122 
~wind()123 wind::~wind ()
124 {
125 	int i;
126 
127 	for (i = 0; i < dEmitters; i++)
128 		delete[]emitters[i];
129 	delete[]emitters;
130 
131 	for (i = 0; i < dParticles; i++)
132 		delete[]particles[i];
133 	delete[]particles;
134 
135 	if (dGeometry == 2) {
136 		for (i = 0; i < dParticles; i++)
137 			delete[]linelist[i];
138 		delete[]linelist;
139 		delete[]lastparticle;
140 	}
141 }
142 
143 void
update()144   wind::update ()
145 {
146 	int i;
147 	float x, y, z;
148 	float temp;
149 	static float evel = float (dEmitterspeed) * 0.01f;
150 	static float pvel = float (dParticlespeed) * 0.01f;
151 	static float pointsize = 0.04f * float (dSize);
152 	static float linesize = 0.005f * float (dSize);
153 
154 	// update constants
155 	for (i = 0; i < NUMCONSTS; i++) {
156 		ct[i] += cv[i];
157 		if (ct[i] > PIx2)
158 			ct[i] -= PIx2;
159 		c[i] = cos (ct[i]);
160 	}
161 
162 	// calculate emissions
163 	for (i = 0; i < dEmitters; i++) {
164 		emitters[i][2] += evel;	// emitter moves toward viewer
165 		if (emitters[i][2] > 15.0f) {	// reset emitter
166 			emitters[i][0] = rsRandf (60.0f) - 30.0f;
167 			emitters[i][1] = rsRandf (60.0f) - 30.0f;
168 			emitters[i][2] = -15.0f;
169 		}
170 		particles[whichparticle][0] = emitters[i][0];
171 		particles[whichparticle][1] = emitters[i][1];
172 		particles[whichparticle][2] = emitters[i][2];
173 		if (dGeometry == 2) {	// link particles to form lines
174 			if (linelist[whichparticle][0] >= 0)
175 				linelist[linelist[whichparticle][0]][1] = -1;
176 			linelist[whichparticle][0] = -1;
177 			if (emitters[i][2] == -15.0f)
178 				linelist[whichparticle][1] = -1;
179 			else
180 				linelist[whichparticle][1] = lastparticle[i];
181 			linelist[lastparticle[i]][0] = whichparticle;
182 			lastparticle[i] = whichparticle;
183 		}
184 		whichparticle++;
185 		if (whichparticle >= dParticles)
186 			whichparticle = 0;
187 	}
188 
189 	// calculate particle positions and colors
190 	// first modify constants that affect colors
191 	c[6] *= 9.0f / float (dParticlespeed);
192 	c[7] *= 9.0f / float (dParticlespeed);
193 	c[8] *= 9.0f / float (dParticlespeed);
194 
195 	// then update each particle
196 	for (i = 0; i < dParticles; i++) {
197 		// store old positions
198 		x = particles[i][0];
199 		y = particles[i][1];
200 		z = particles[i][2];
201 		// make new positions
202 		particles[i][0] = x + (c[0] * y + c[1] * z) * pvel;
203 		particles[i][1] = y + (c[2] * z + c[3] * x) * pvel;
204 		particles[i][2] = z + (c[4] * x + c[5] * y) * pvel;
205 		// calculate colors
206 		particles[i][3] = float (fabs ((particles[i][0] - x) * c[6]));
207 		particles[i][4] = float (fabs ((particles[i][1] - y) * c[7]));
208 		particles[i][5] = float (fabs ((particles[i][2] - z) * c[8]));
209 
210 		// clamp colors
211 		if (particles[i][3] > 1.0f)
212 			particles[i][3] = 1.0f;
213 		if (particles[i][4] > 1.0f)
214 			particles[i][4] = 1.0f;
215 		if (particles[i][5] > 1.0f)
216 			particles[i][5] = 1.0f;
217 	}
218 
219 	// draw particles
220 	switch (dGeometry) {
221 	case 0:		// lights
222 		for (i = 0; i < dParticles; i++) {
223 			glColor3fv (&particles[i][3]);
224 			glPushMatrix ();
225 			glTranslatef (particles[i][0], particles[i][1], particles[i][2]);
226 			glCallList (1);
227 			glPopMatrix ();
228 		}
229 		break;
230 	case 1:		// points
231 		for (i = 0; i < dParticles; i++) {
232 			temp = particles[i][2] + 40.0f;
233 			if (temp < 0.01f)
234 				temp = 0.01f;
235 			glPointSize (pointsize * temp);
236 			glBegin (GL_POINTS);
237 			glColor3fv (&particles[i][3]);
238 			glVertex3fv (particles[i]);
239 			glEnd ();
240 		}
241 		break;
242 	case 2:		// lines
243 		for (i = 0; i < dParticles; i++) {
244 			temp = particles[i][2] + 40.0f;
245 			if (temp < 0.01f)
246 				temp = 0.01f;
247 			glLineWidth (linesize * temp);
248 			glBegin (GL_LINES);
249 			if (linelist[i][1] >= 0) {
250 				glColor3fv (&particles[i][3]);
251 				if (linelist[i][0] == -1)
252 					glColor3f (0.0f, 0.0f, 0.0f);
253 				glVertex3fv (particles[i]);
254 				glColor3fv (&particles[linelist[i][1]][3]);
255 				if (linelist[linelist[i][1]][1] == -1)
256 					glColor3f (0.0f, 0.0f, 0.0f);
257 				glVertex3fv (particles[linelist[i][1]]);
258 			}
259 			glEnd ();
260 		}
261 	}
262 }
263 
hack_draw(xstuff_t * XStuff,double currentTime,float frameTime)264 void hack_draw (xstuff_t * XStuff, double currentTime, float frameTime)
265 {
266 	int i;
267 
268 	if (!dBlur) {
269 		glClear (GL_COLOR_BUFFER_BIT);
270 	} else {
271 		glMatrixMode(GL_PROJECTION);
272 		glPushMatrix();
273 			glLoadIdentity();
274 			glOrtho(0.0, 1.0, 0.0, 1.0, 1.0, -1.0);
275 			glMatrixMode(GL_MODELVIEW);
276 			glLoadIdentity();
277 				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
278 				glColor4f(0.0f, 0.0f, 0.0f, 0.5f - (float(dBlur) * 0.0049f));
279 				glBegin(GL_TRIANGLE_STRIP);
280 					glVertex3f(0.0f, 0.0f, 0.0f);
281 					glVertex3f(1.0f, 0.0f, 0.0f);
282 					glVertex3f(0.0f, 1.0f, 0.0f);
283 					glVertex3f(1.0f, 1.0f, 0.0f);
284 				glEnd();
285 				if(dGeometry == 0)
286 					glBlendFunc(GL_ONE, GL_ONE);
287 				else
288 					glBlendFunc(GL_SRC_ALPHA, GL_ONE);  // Necessary for point and line smoothing (I don't know why)
289 						// Maybe it's just my video card...
290 		glMatrixMode(GL_PROJECTION);
291 		glPopMatrix();
292 	}
293 
294 	glMatrixMode(GL_MODELVIEW);
295 	glLoadIdentity();
296 	glTranslatef(0.0, 0.0, -15.0);
297 
298 	// You should need to draw twice if using blur, once to each buffer.
299 	// But wglSwapLayerBuffers appears to copy the back to the
300 	// front instead of just switching the pointers to them.  It turns
301 	// out that both NVidia and 3dfx prefer to use PFD_SWAP_COPY instead
302 	// of PFD_SWAP_EXCHANGE in the PIXELFORMATDESCRIPTOR.  I don't know
303 	// why...
304 	// So this may not work right on other platforms or all video cards.
305 
306 	// Update surfaces
307 	for (i = 0; i < dWinds; i++)
308 		winds[i].update ();
309 
310 	glFlush ();
311 }
312 
hack_reshape(xstuff_t * XStuff)313 void hack_reshape (xstuff_t * XStuff)
314 {
315 	// Window initialization
316 	glViewport (0, 0, XStuff->windowWidth, XStuff->windowHeight);
317 
318 	glMatrixMode (GL_PROJECTION);
319 	glLoadIdentity ();
320 	gluPerspective (90.0, float (XStuff->windowWidth) / float (XStuff->windowHeight), 1.0, 10000);
321 
322 	glTranslatef (0.0, 0.0, -15.0);
323 	glMatrixMode (GL_MODELVIEW);
324 	glLoadIdentity ();
325 
326 	glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
327 	glClear (GL_COLOR_BUFFER_BIT);
328 }
329 
hack_init(xstuff_t * XStuff)330 void hack_init (xstuff_t * XStuff)
331 {
332 	int i, j;
333 	float x, y, temp;
334 
335 	hack_reshape (XStuff);
336 
337 	if (!dGeometry)
338 		glBlendFunc (GL_ONE, GL_ONE);
339 	else
340 		glBlendFunc (GL_SRC_ALPHA, GL_ONE);	// Necessary for point and line smoothing (I don't know why)
341 
342 	glEnable (GL_BLEND);
343 
344 	if (!dGeometry) {	// Init lights
345 		for (i = 0; i < LIGHTSIZE; i++) {
346 			for (j = 0; j < LIGHTSIZE; j++) {
347 				x = float (i - LIGHTSIZE / 2) / float (LIGHTSIZE / 2);
348 				y = float (j - LIGHTSIZE / 2) / float (LIGHTSIZE / 2);
349 				temp = 1.0f - float (sqrt ((x * x) + (y * y)));
350 
351 				if (temp > 1.0f)
352 					temp = 1.0f;
353 				if (temp < 0.0f)
354 					temp = 0.0f;
355 				lightTexture[i][j] = char (255.0f * temp);
356 			}
357 		}
358 		glEnable (GL_TEXTURE_2D);
359 		glBindTexture (GL_TEXTURE_2D, 1);
360 		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
361 		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
362 		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
363 		glTexImage2D (GL_TEXTURE_2D, 0, 1, LIGHTSIZE, LIGHTSIZE, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, lightTexture);
364 		temp = 0.02f * float (dSize);
365 
366 		glNewList (1, GL_COMPILE);
367 		glBindTexture (GL_TEXTURE_2D, 1);
368 		glBegin (GL_TRIANGLE_STRIP);
369 		glTexCoord2f (0.0f, 0.0f);
370 		glVertex3f (-temp, -temp, 0.0f);
371 		glTexCoord2f (1.0f, 0.0f);
372 		glVertex3f (temp, -temp, 0.0f);
373 		glTexCoord2f (0.0f, 1.0f);
374 		glVertex3f (-temp, temp, 0.0f);
375 		glTexCoord2f (1.0f, 1.0f);
376 		glVertex3f (temp, temp, 0.0f);
377 		glEnd ();
378 		glEndList ();
379 	}
380 
381 	if (dGeometry == 1) {	// init point smoothing
382 		glEnable (GL_POINT_SMOOTH);
383 		glHint (GL_POINT_SMOOTH_HINT, GL_NICEST);
384 	}
385 
386 	if (dGeometry == 2) {	// init line smoothing
387 		glEnable (GL_LINE_SMOOTH);
388 		glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
389 	}
390 	// Initialize surfaces
391 	winds = new wind[dWinds];
392 
393 	glFlush ();
394 }
395 
hack_cleanup(xstuff_t * XStuff)396 void hack_cleanup (xstuff_t * XStuff)
397 {
398 	glDeleteLists (1, 1);
399 }
400 
setDefaults(int which)401 void setDefaults (int which)
402 {
403 	switch (which) {
404 	case DEFAULTS1:	// Regular
405 		dWinds = 1;
406 		dEmitters = 30;
407 		dParticles = 2000;
408 		dGeometry = 0;
409 		dSize = 50;
410 		dWindspeed = 20;
411 		dEmitterspeed = 15;
412 		dParticlespeed = 10;
413 		dBlur = 40;
414 		break;
415 	case DEFAULTS2:	// Cosmic Strings
416 		dWinds = 1;
417 		dEmitters = 50;
418 		dParticles = 3000;
419 		dGeometry = 2;
420 		dSize = 20;
421 		dWindspeed = 10;
422 		dEmitterspeed = 10;
423 		dParticlespeed = 10;
424 		dBlur = 10;
425 		break;
426 	case DEFAULTS3:	// Cold Pricklies
427 		dWinds = 1;
428 		dEmitters = 300;
429 		dParticles = 3000;
430 		dGeometry = 2;
431 		dSize = 5;
432 		dWindspeed = 20;
433 		dEmitterspeed = 100;
434 		dParticlespeed = 15;
435 		dBlur = 70;
436 		break;
437 	case DEFAULTS4:	// Space Fur
438 		dWinds = 2;
439 		dEmitters = 400;
440 		dParticles = 1600;
441 		dGeometry = 2;
442 		dSize = 15;
443 		dWindspeed = 20;
444 		dEmitterspeed = 15;
445 		dParticlespeed = 10;
446 		dBlur = 0;
447 		break;
448 	case DEFAULTS5:	// Jiggly
449 		dWinds = 1;
450 		dEmitters = 40;
451 		dParticles = 1200;
452 		dGeometry = 1;
453 		dSize = 20;
454 		dWindspeed = 100;
455 		dEmitterspeed = 20;
456 		dParticlespeed = 4;
457 		dBlur = 50;
458 		break;
459 	case DEFAULTS6:	// Undertow
460 		dWinds = 1;
461 		dEmitters = 400;
462 		dParticles = 1200;
463 		dGeometry = 0;
464 		dSize = 40;
465 		dWindspeed = 20;
466 		dEmitterspeed = 1;
467 		dParticlespeed = 100;
468 		dBlur = 50;
469 	}
470 }
471 
hack_handle_opts(int argc,char ** argv)472 void hack_handle_opts (int argc, char **argv)
473 {
474 	int change_flag = 0;
475 
476 	setDefaults (DEFAULTS1);
477 
478 	while (1) {
479 		int c;
480 
481 #ifdef HAVE_GETOPT_H
482 		static struct option long_options[] = {
483 			{"help", 0, 0, 'h'},
484 			DRIVER_OPTIONS_LONG
485 			{"preset", 1, 0, 'R'},
486 			{"regular", 0, 0, 10},
487 			{"cosmicstrings", 0, 0, 11},
488 			{"coldpricklies", 0, 0, 12},
489 			{"spacefur", 0, 0, 13},
490 			{"jiggy", 0, 0, 14},
491 			{"undertow", 0, 0, 15},
492 			{"winds", 1, 0, 'w'},
493 			{"emitters", 1, 0, 'e'},
494 			{"particles", 1, 0, 'p'},
495 			{"geometry", 1, 0, 'g'},
496 			{"lights", 0, 0, 20},
497 			{"points", 0, 0, 21},
498 			{"lines", 0, 0, 22},
499 			{"size", 1, 0, 's'},
500 			{"windspeed", 1, 0, 'W'},
501 			{"emitterspeed", 1, 0, 'E'},
502 			{"particlespeed", 1, 0, 'P'},
503 			{"blur", 1, 0, 'b'},
504 			{0, 0, 0, 0}
505 		};
506 
507 		c = getopt_long (argc, argv, DRIVER_OPTIONS_SHORT "hR:w:e:p:s:g:W:E:P:b:", long_options, NULL);
508 #else
509 		c = getopt (argc, argv, DRIVER_OPTIONS_SHORT "hR:w:e:p:s:g:W:E:P:b:");
510 #endif
511 		if (c == -1)
512 			break;
513 
514 		switch (c) {
515 		DRIVER_OPTIONS_CASES case 'h':
516 			printf ("%s:"
517 #ifndef HAVE_GETOPT_H
518 				" Not built with GNU getopt.h, long options *NOT* enabled."
519 #endif
520 				"\n" DRIVER_OPTIONS_HELP
521 				"\t--preset/-R <arg>\n"
522 				"\t--regular\n"
523 				"\t--cosmicstrings\n"
524 				"\t--coldpricklies\n"
525 				"\t--spacefur\n"
526 				"\t--jiggy\n"
527 				"\t--undertow\n"
528 				"\t--winds/-w <arg>\n" "\t--emitters/-e <arg>\n" "\t--particles/-p <arg>\n"
529 				"\t--geometry/-g <arg>\n" "\t--lights\n" "\t--points\n" "\t--lines\n"
530 				"\t--size/-s <arg>\n"
531 				"\t--windspeed/-W <arg>\n" "\t--emitterspeed/-E <arg>\n" "\t--particlespeed/-P <arg>\n"
532 				"\t--blur/-b <arg>\n", argv[0]);
533 			exit (1);
534 		case 'R':
535 			change_flag = 1;
536 			setDefaults (strtol_minmaxdef(optarg, 10, 1, 6, 0, 1, "--preset: "));
537 			break;
538 		case 10:
539 		case 11:
540 		case 12:
541 		case 13:
542 		case 14:
543 		case 15:
544 			change_flag = 1;
545 			setDefaults (c - 9);
546 			break;
547 		case 'w':
548 			change_flag = 1;
549 			dWinds = strtol_minmaxdef(optarg, 10, 1, 10, 1, 1, "--winds: ");
550 			break;
551 		case 'e':
552 			change_flag = 1;
553 			dEmitters = strtol_minmaxdef(optarg, 10, 1, 1000, 1, 30, "--emitters: ");
554 			break;
555 		case 'p':
556 			change_flag = 1;
557 			dParticles = strtol_minmaxdef(optarg, 10, 0, 10000, 1, 2000, "--particles: ");
558 			break;
559 		case 's':
560 			change_flag = 1;
561 			dSize = strtol_minmaxdef(optarg, 10, 0, 100, 1, 50, "--size: ");
562 			break;
563 		case 'g':
564 			change_flag = 1;
565 			dGeometry = strtol_minmaxdef(optarg, 10, 0, 2, 0, 0, "--geometry: ");
566 			break;
567 		case 20:
568 		case 21:
569 		case 22:
570 			change_flag = 1;
571 			dGeometry = c - 20;
572 			break;
573 		case 'W':
574 			change_flag = 1;
575 			dWindspeed = strtol_minmaxdef(optarg, 10, 1, 100, 1, 20, "--windspeed: ");
576 			break;
577 		case 'E':
578 			change_flag = 1;
579 			dEmitterspeed = strtol_minmaxdef(optarg, 10, 1, 100, 1, 15, "--emitterspeed: ");
580 			break;
581 		case 'P':
582 			change_flag = 1;
583 			dParticlespeed = strtol_minmaxdef(optarg, 10, 1, 100, 1, 10, "--particlespeed: ");
584 			break;
585 		case 'b':
586 			change_flag = 1;
587 			dBlur = strtol_minmaxdef(optarg, 10, 0, 100, 1, 40, "--blur: ");
588 			break;
589 		}
590 	}
591 
592 	if (!change_flag) {
593 		setDefaults (rsRandi (6) + 1);
594 	}
595 }
596