ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / glx / flurry.c
1 /* -*- Mode: C; tab-width: 4 c-basic-offset: 4 indent-tabs-mode: t -*- */
2 /*
3  * vim: ts=8 sw=4 noet
4  */
5
6 /*
7
8 Copyright (c) 2002, Calum Robinson
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions are met:
13
14 * Redistributions of source code must retain the above copyright notice, this
15   list of conditions and the following disclaimer.
16
17 * Redistributions in binary form must reproduce the above copyright notice,
18   this list of conditions and the following disclaimer in the documentation
19   and/or other materials provided with the distribution.
20
21 * Neither the name of the author nor the names of its contributors may be used
22   to endorse or promote products derived from this software without specific
23   prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
29 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36 */
37
38 /* flurry */
39
40 #if 0
41 static const char sccsid[] = "@(#)flurry.c      4.07 97/11/24 xlockmore";
42 #endif
43
44 #define DEF_PRESET     "classic"
45 #define DEF_BRIGHTNESS "8"
46
47 #include <X11/Intrinsic.h>
48
49 # define PROGCLASS              "Flurry"
50 # define HACK_INIT              init_flurry
51 # define HACK_DRAW              draw_flurry
52 # define HACK_RESHAPE           reshape_flurry
53 # define flurry_opts            xlockmore_opts
54 # define DEFAULTS               "*showFPS:      False   \n" \
55                                 "*preset:       " DEF_PRESET "   \n"
56
57 # include "xlockmore.h"         /* from the xscreensaver distribution */
58
59 #ifdef USE_GL
60
61 static char *preset_str;
62
63 static XrmOptionDescRec opts[] = {
64     { "-preset",     ".preset",     XrmoptionSepArg, 0 }
65 };
66
67 static argtype vars[] = {
68     {&preset_str, "preset",     "Preset",     DEF_PRESET,     t_String},
69 };
70
71 #define countof(x) (sizeof((x))/sizeof((*x)))
72
73 ModeSpecOpt flurry_opts = {countof(opts), opts, countof(vars), vars, NULL};
74
75 #ifdef USE_MODULES
76 ModStruct   flurry_description = {
77     "flurry",
78     "init_flurry",
79     "draw_flurry",
80     "release_flurry",
81     "draw_flurry",
82     "init_flurry",
83     NULL,
84     &flurry_opts,
85     1000, 1, 2, 1, 4, 1.0,
86     "",
87     "Flurry",
88     0,
89     NULL
90 };
91
92 #endif
93
94 #include <sys/time.h>
95
96 #include "flurry.h"
97
98 global_info_t *flurry_info = NULL;
99
100 static double gTimeCounter = 0.0;
101
102 double currentTime(void) {
103   struct timeval tv;
104 # ifdef GETTIMEOFDAY_TWO_ARGS
105   struct timezone tzp;
106   gettimeofday(&tv, &tzp);
107 # else
108   gettimeofday(&tv);
109 # endif
110
111   return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
112 }
113
114 void OTSetup (void) {
115     if (gTimeCounter == 0.0) {
116         gTimeCounter = currentTime();
117     }
118 }
119
120 double TimeInSecondsSinceStart (void) {
121     return currentTime() - gTimeCounter;
122 }
123
124 #if 0
125 #ifdef __ppc__
126 static int IsAltiVecAvailable(void)
127 {
128     return 0;
129 }
130 #endif
131 #endif
132
133 void delete_flurry_info(flurry_info_t *flurry)
134 {
135     int i;
136
137     free(flurry->s);
138     free(flurry->star);
139     for (i=0;i<MAX_SPARKS;i++)
140     {
141         free(flurry->spark[i]);
142     }
143     free(flurry);
144 }
145
146 flurry_info_t *new_flurry_info(global_info_t *global, int streams, ColorModes colour, float thickness, float speed, double bf)
147 {
148     int i,k;
149     flurry_info_t *flurry = (flurry_info_t *)malloc(sizeof(flurry_info_t));
150
151     if (!flurry) return NULL;
152
153     flurry->flurryRandomSeed = RandFlt(0.0, 300.0);
154
155         flurry->fOldTime = 0;
156         flurry->fTime = TimeInSecondsSinceStart() + flurry->flurryRandomSeed;
157         flurry->fDeltaTime = flurry->fTime - flurry->fOldTime;
158
159     flurry->numStreams = streams;
160     flurry->streamExpansion = thickness;
161     flurry->currentColorMode = colour;
162     flurry->briteFactor = bf;
163
164     flurry->s = malloc(sizeof(SmokeV));
165     InitSmoke(flurry->s);
166
167     flurry->star = malloc(sizeof(Star));
168     InitStar(flurry->star);
169     flurry->star->rotSpeed = speed;
170
171     for (i = 0;i < MAX_SPARKS; i++)
172     {
173         flurry->spark[i] = malloc(sizeof(Spark));
174         InitSpark(flurry->spark[i]);
175         flurry->spark[i]->mystery = 1800 * (i + 1) / 13; /* 100 * (i + 1) / (flurry->numStreams + 1); */
176         UpdateSpark(global, flurry, flurry->spark[i]);
177     }
178
179     for (i=0;i<NUMSMOKEPARTICLES/4;i++) {
180         for(k=0;k<4;k++) {
181             flurry->s->p[i].dead.i[k] = 1;
182         }
183     }
184
185     flurry->next = NULL;
186
187     return flurry;
188 }
189
190 void GLSetupRC(global_info_t *global)
191 {
192     /* setup the defaults for OpenGL */
193     glDisable(GL_DEPTH_TEST);
194     glAlphaFunc(GL_GREATER,0.0f);
195     glEnable(GL_ALPHA_TEST);
196     glShadeModel(GL_FLAT);
197     glDisable(GL_LIGHTING);
198     glDisable(GL_CULL_FACE);
199     glEnable(GL_BLEND);
200
201     glViewport(0,0,(int) global->sys_glWidth,(int) global->sys_glHeight);
202     glMatrixMode(GL_PROJECTION);
203     glLoadIdentity();
204     gluOrtho2D(0,global->sys_glWidth,0,global->sys_glHeight);
205     glMatrixMode(GL_MODELVIEW);
206     glLoadIdentity();
207
208     glClearColor(0.0,0.0,0.0,1.0);
209     glClear(GL_COLOR_BUFFER_BIT);
210
211     glEnableClientState(GL_COLOR_ARRAY);        
212     glEnableClientState(GL_VERTEX_ARRAY);       
213     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
214     
215 #if 0
216 #ifdef __ppc__
217     global->optMode = OPT_MODE_SCALAR_FRSQRTE;
218
219 #ifdef __VEC__
220     if (IsAltiVecAvailable()) global->optMode = OPT_MODE_VECTOR_UNROLLED;
221 #endif
222
223 #else
224     global->optMode = OPT_MODE_SCALAR_BASE;
225 #endif
226 #endif /* 0 */
227 }
228
229 void GLRenderScene(global_info_t *global, flurry_info_t *flurry, double b)
230 {
231     int i;
232
233     flurry->dframe++;
234
235     flurry->fOldTime = flurry->fTime;
236     flurry->fTime = TimeInSecondsSinceStart() + flurry->flurryRandomSeed;
237     flurry->fDeltaTime = flurry->fTime - flurry->fOldTime;
238
239     flurry->drag = (float) pow(0.9965,flurry->fDeltaTime*85.0);
240
241     UpdateStar(global, flurry, flurry->star);
242
243 #ifdef DRAW_SPARKS
244     glShadeModel(GL_SMOOTH);
245     glEnable(GL_BLEND);
246     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
247 #endif
248
249     for (i=0;i<flurry->numStreams;i++) {
250         flurry->spark[i]->color[0]=1.0;
251         flurry->spark[i]->color[1]=1.0;
252         flurry->spark[i]->color[2]=1.0;
253         flurry->spark[i]->color[2]=1.0;
254         UpdateSpark(global, flurry, flurry->spark[i]);
255 #ifdef DRAW_SPARKS
256         DrawSpark(global, flurry, flurry->spark[i]);
257 #endif
258     }
259
260     switch(global->optMode) {
261         case OPT_MODE_SCALAR_BASE:
262             UpdateSmoke_ScalarBase(global, flurry, flurry->s);
263             break;
264 #if 0
265 #ifdef __ppc__
266         case OPT_MODE_SCALAR_FRSQRTE:
267             UpdateSmoke_ScalarFrsqrte(global, flurry, flurry->s);
268             break;
269 #endif
270 #ifdef __VEC__
271         case OPT_MODE_VECTOR_SIMPLE:
272             UpdateSmoke_VectorBase(global, flurry, flurry->s);
273             break;
274         case OPT_MODE_VECTOR_UNROLLED:
275             UpdateSmoke_VectorUnrolled(global, flurry, flurry->s);
276             break;
277 #endif
278 #endif /* 0 */
279
280         default:
281             break;
282     }
283
284     /* glDisable(GL_BLEND); */
285     glEnable(GL_BLEND);
286     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
287     glEnable(GL_TEXTURE_2D);
288
289     switch(global->optMode) {
290         case OPT_MODE_SCALAR_BASE:
291 #if 0
292 #ifdef __ppc__
293         case OPT_MODE_SCALAR_FRSQRTE:
294 #endif
295 #endif /* 0 */
296             DrawSmoke_Scalar(global, flurry, flurry->s, b);
297             break;
298 #if 0
299 #ifdef __VEC__
300         case OPT_MODE_VECTOR_SIMPLE:
301         case OPT_MODE_VECTOR_UNROLLED:
302             DrawSmoke_Vector(global, flurry, flurry->s, b);
303             break;
304 #endif
305 #endif /* 0 */
306         default:
307             break;
308     }    
309
310     glDisable(GL_TEXTURE_2D);
311 }
312
313 void GLResize(global_info_t *global, float w, float h)
314 {
315     global->sys_glWidth = w;
316     global->sys_glHeight = h;
317 }
318
319 /* new window size or exposure */
320 void reshape_flurry(ModeInfo *mi, int width, int height)
321 {
322     global_info_t *global = flurry_info + MI_SCREEN(mi);
323
324     glXMakeCurrent(MI_DISPLAY(mi), global->window, *(global->glx_context));
325
326     glViewport(0.0, 0.0, width, height);
327     glMatrixMode(GL_PROJECTION);
328     glLoadIdentity();
329     gluOrtho2D(0, width, 0, height);
330     glMatrixMode(GL_MODELVIEW);
331
332     glClearColor(0.0, 0.0, 0.0, 1.0);
333     glClear(GL_COLOR_BUFFER_BIT);
334
335     glFlush();
336
337     GLResize(global, (float)width, (float)height);
338 }
339
340 void
341 init_flurry(ModeInfo * mi)
342 {
343     int screen = MI_SCREEN(mi);
344     int i;
345     global_info_t *global;
346     enum {
347         PRESET_INSANE = -1,
348         PRESET_WATER = 0,
349         PRESET_FIRE,
350         PRESET_PSYCHEDELIC,
351         PRESET_RGB,
352         PRESET_BINARY,
353         PRESET_CLASSIC,
354         PRESET_MAX
355     } preset_num;
356
357     if (flurry_info == NULL) {
358         OTSetup();
359         if ((flurry_info = (global_info_t *) calloc(MI_NUM_SCREENS(mi),
360                         sizeof (global_info_t))) == NULL)
361             return;
362     }
363
364     global = &flurry_info[screen];
365
366     global->window = MI_WINDOW(mi);
367
368     global->flurry = NULL;
369
370     if (!preset_str || !*preset_str) preset_str = DEF_PRESET;
371     if (!strcmp(preset_str, "random")) {
372         preset_num = random() % PRESET_MAX;
373     } else if (!strcmp(preset_str, "water")) {
374         preset_num = PRESET_WATER;
375     } else if (!strcmp(preset_str, "fire")) {
376         preset_num = PRESET_FIRE;
377     } else if (!strcmp(preset_str, "psychedelic")) {
378         preset_num = PRESET_PSYCHEDELIC;
379     } else if (!strcmp(preset_str, "rgb")) {
380         preset_num = PRESET_RGB;
381     } else if (!strcmp(preset_str, "binary")) {
382         preset_num = PRESET_BINARY;
383     } else if (!strcmp(preset_str, "classic")) {
384         preset_num = PRESET_CLASSIC;
385     } else if (!strcmp(preset_str, "insane")) {
386         preset_num = PRESET_INSANE;
387     } else {
388         fprintf(stderr, "%s: unknown preset %s\n", progname, preset_str);
389         exit(1);
390     }
391
392     switch (preset_num) {
393     case PRESET_WATER: {
394         for (i = 0; i < 9; i++) {
395             flurry_info_t *flurry;
396
397             flurry = new_flurry_info(global, 1, blueColorMode, 100.0, 2.0, 2.0);
398             flurry->next = global->flurry;
399             global->flurry = flurry;
400         }
401         break;
402     }
403     case PRESET_FIRE: {
404         flurry_info_t *flurry;
405
406         flurry = new_flurry_info(global, 12, slowCyclicColorMode, 10000.0, 0.0, 1.0);
407         flurry->next = global->flurry;
408         global->flurry = flurry;        
409         break;
410     }
411     case PRESET_PSYCHEDELIC: {
412         flurry_info_t *flurry;
413
414         flurry = new_flurry_info(global, 10, rainbowColorMode, 200.0, 2.0, 1.0);
415         flurry->next = global->flurry;
416         global->flurry = flurry;        
417         break;
418     }
419     case PRESET_RGB: {
420         flurry_info_t *flurry;
421
422         flurry = new_flurry_info(global, 3, redColorMode, 100.0, 0.8, 1.0);
423         flurry->next = global->flurry;
424         global->flurry = flurry;        
425
426         flurry = new_flurry_info(global, 3, greenColorMode, 100.0, 0.8, 1.0);
427         flurry->next = global->flurry;
428         global->flurry = flurry;        
429
430         flurry = new_flurry_info(global, 3, blueColorMode, 100.0, 0.8, 1.0);
431         flurry->next = global->flurry;
432         global->flurry = flurry;        
433         break;
434     }
435     case PRESET_BINARY: {
436         flurry_info_t *flurry;
437
438         flurry = new_flurry_info(global, 16, tiedyeColorMode, 1000.0, 0.5, 1.0);
439         flurry->next = global->flurry;
440         global->flurry = flurry;
441
442         flurry = new_flurry_info(global, 16, tiedyeColorMode, 1000.0, 1.5, 1.0);
443         flurry->next = global->flurry;
444         global->flurry = flurry;
445         break;
446     }
447     case PRESET_CLASSIC: {
448         flurry_info_t *flurry;
449
450         flurry = new_flurry_info(global, 5, tiedyeColorMode, 10000.0, 1.0, 1.0);
451         flurry->next = global->flurry;
452         global->flurry = flurry;
453         break;
454     }
455     case PRESET_INSANE: {
456         flurry_info_t *flurry;
457
458         flurry = new_flurry_info(global, 64, tiedyeColorMode, 1000.0, 0.5, 0.5);
459         flurry->next = global->flurry;
460         global->flurry = flurry;
461
462         break;
463     }
464     default: {
465         fprintf(stderr, "%s: unknown preset %s\n", progname, preset_str);
466         exit(1);
467     }
468     } 
469
470     if ((global->glx_context = init_GL(mi)) != NULL) {
471         reshape_flurry(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
472         GLSetupRC(global);
473     } else {
474         MI_CLEARWINDOW(mi);
475     }
476 }
477
478 void
479 draw_flurry(ModeInfo * mi)
480 {
481     static int first = 1;
482     static double oldFrameTime = -1;
483     double newFrameTime;
484     double deltaFrameTime = 0;
485     double brite;
486     GLfloat alpha;
487
488     global_info_t *global = flurry_info + MI_SCREEN(mi);
489     flurry_info_t *flurry;
490     Display    *display = MI_DISPLAY(mi);
491     Window      window = MI_WINDOW(mi);
492
493     newFrameTime = currentTime();
494     if (oldFrameTime == -1) {
495         /* special case the first frame -- clear to black */
496         alpha = 1.0;
497     } else {
498         /* 
499          * this clamps the speed at below 60fps and, here
500          * at least, produces a reasonably accurate 50fps.
501          * (probably part CPU speed and part scheduler).
502          *
503          * Flurry is designed to run at this speed; much higher
504          * than that and the blending causes the display to
505          * saturate, which looks really ugly.
506          */
507         if (newFrameTime - oldFrameTime < 1/60.0) {
508             usleep(MAX_(1,(int)(20000 * (newFrameTime - oldFrameTime))));
509             return;
510
511         }
512         deltaFrameTime = newFrameTime - oldFrameTime;
513         alpha = 5.0 * deltaFrameTime;
514     }
515     oldFrameTime = newFrameTime;
516
517     if (alpha > 0.2) alpha = 0.2;
518
519     if (!global->glx_context)
520         return;
521
522     if (first) {
523         MakeTexture();
524         first = 0;
525     }
526     glDrawBuffer(GL_BACK);
527     glXMakeCurrent(display, window, *(global->glx_context));
528
529     glEnable(GL_BLEND);
530     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
531
532     glColor4f(0.0, 0.0, 0.0, alpha);
533     glRectd(0, 0, global->sys_glWidth, global->sys_glHeight);
534
535     brite = pow(deltaFrameTime,0.75) * 10;
536     for (flurry = global->flurry; flurry; flurry=flurry->next) {
537         GLRenderScene(global, flurry, brite * flurry->briteFactor);
538     }
539
540     if (mi->fps_p) do_fps (mi);
541
542     glFinish();
543     glXSwapBuffers(display, window);
544 }
545
546 void
547 release_flurry(ModeInfo * mi)
548 {
549     if (flurry_info != NULL) {
550         int screen;
551
552         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
553             global_info_t *global = &flurry_info[screen];
554             flurry_info_t *flurry;
555
556             if (global->glx_context) {
557                 glXMakeCurrent(MI_DISPLAY(mi), global->window, *(global->glx_context));
558             }
559
560             for (flurry = global->flurry; flurry; flurry=flurry->next) {
561                 delete_flurry_info(flurry);
562             }
563         }
564         (void) free((void *) flurry_info);
565         flurry_info = NULL;
566     }
567     FreeAllGL(mi);
568 }
569
570 #endif