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