http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[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     "random"
45 #define DEF_BRIGHTNESS "8"
46
47 # define DEFAULTS               "*delay:      10000 \n" \
48                                                 "*showFPS:    False \n"
49
50 # define refresh_flurry 0
51 # define flurry_handle_event 0
52 # include "xlockmore.h"         /* from the xscreensaver distribution */
53
54 #ifdef USE_GL
55
56 static char *preset_str;
57
58 static XrmOptionDescRec opts[] = {
59     { "-preset",     ".preset",     XrmoptionSepArg, 0 }
60 };
61
62 static argtype vars[] = {
63     {&preset_str, "preset",     "Preset",     DEF_PRESET,     t_String},
64 };
65
66 #define countof(x) (sizeof((x))/sizeof((*x)))
67
68 ENTRYPOINT ModeSpecOpt flurry_opts = {countof(opts), opts, countof(vars), vars, NULL};
69
70 #ifdef USE_MODULES
71 ModStruct   flurry_description = {
72     "flurry",
73     "init_flurry",
74     "draw_flurry",
75     "release_flurry",
76     "draw_flurry",
77     "init_flurry",
78     NULL,
79     &flurry_opts,
80     1000, 1, 2, 1, 4, 1.0,
81     "",
82     "Flurry",
83     0,
84     NULL
85 };
86
87 #endif
88
89 #include "flurry.h"
90
91 global_info_t *flurry_info = NULL;
92
93 static double gTimeCounter = 0.0;
94
95 static
96 double currentTime(void) {
97   struct timeval tv;
98 # ifdef GETTIMEOFDAY_TWO_ARGS
99   struct timezone tzp;
100   gettimeofday(&tv, &tzp);
101 # else
102   gettimeofday(&tv);
103 # endif
104
105   return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
106 }
107
108 void OTSetup (void) {
109     if (gTimeCounter == 0.0) {
110         gTimeCounter = currentTime();
111     }
112 }
113
114 double TimeInSecondsSinceStart (void) {
115     return currentTime() - gTimeCounter;
116 }
117
118 #if 0
119 #ifdef __ppc__
120 static int IsAltiVecAvailable(void)
121 {
122     return 0;
123 }
124 #endif
125 #endif
126
127
128 static
129 void delete_flurry_info(flurry_info_t *flurry)
130 {
131     int i;
132
133     free(flurry->s);
134     free(flurry->star);
135     for (i=0;i<MAX_SPARKS;i++)
136     {
137         free(flurry->spark[i]);
138     }
139     /* free(flurry); */
140 }
141
142 static
143 flurry_info_t *new_flurry_info(global_info_t *global, int streams, ColorModes colour, float thickness, float speed, double bf)
144 {
145     int i,k;
146     flurry_info_t *flurry = (flurry_info_t *)malloc(sizeof(flurry_info_t));
147
148     if (!flurry) return NULL;
149
150     flurry->flurryRandomSeed = RandFlt(0.0, 300.0);
151
152         flurry->fOldTime = 0;
153         flurry->fTime = TimeInSecondsSinceStart() + flurry->flurryRandomSeed;
154         flurry->fDeltaTime = flurry->fTime - flurry->fOldTime;
155
156     flurry->numStreams = streams;
157     flurry->streamExpansion = thickness;
158     flurry->currentColorMode = colour;
159     flurry->briteFactor = bf;
160
161     flurry->s = malloc(sizeof(SmokeV));
162     InitSmoke(flurry->s);
163
164     flurry->star = malloc(sizeof(Star));
165     InitStar(flurry->star);
166     flurry->star->rotSpeed = speed;
167
168     for (i = 0;i < MAX_SPARKS; i++)
169     {
170         flurry->spark[i] = malloc(sizeof(Spark));
171         InitSpark(flurry->spark[i]);
172         flurry->spark[i]->mystery = 1800 * (i + 1) / 13; /* 100 * (i + 1) / (flurry->numStreams + 1); */
173         UpdateSpark(global, flurry, flurry->spark[i]);
174     }
175
176     for (i=0;i<NUMSMOKEPARTICLES/4;i++) {
177         for(k=0;k<4;k++) {
178             flurry->s->p[i].dead.i[k] = 1;
179         }
180     }
181
182     flurry->next = NULL;
183
184     return flurry;
185 }
186
187 static
188 void GLSetupRC(global_info_t *global)
189 {
190     /* setup the defaults for OpenGL */
191     glDisable(GL_DEPTH_TEST);
192     glAlphaFunc(GL_GREATER,0.0f);
193     glEnable(GL_ALPHA_TEST);
194     glShadeModel(GL_FLAT);
195     glDisable(GL_LIGHTING);
196     glDisable(GL_CULL_FACE);
197     glEnable(GL_BLEND);
198
199     glViewport(0,0,(int) global->sys_glWidth,(int) global->sys_glHeight);
200     glMatrixMode(GL_PROJECTION);
201     glLoadIdentity();
202     gluOrtho2D(0,global->sys_glWidth,0,global->sys_glHeight);
203     glMatrixMode(GL_MODELVIEW);
204     glLoadIdentity();
205
206     glClear(GL_COLOR_BUFFER_BIT);
207
208     glEnableClientState(GL_COLOR_ARRAY);        
209     glEnableClientState(GL_VERTEX_ARRAY);       
210     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
211     
212 #if 0
213 #ifdef __ppc__
214     global->optMode = OPT_MODE_SCALAR_FRSQRTE;
215
216 #ifdef __VEC__
217     if (IsAltiVecAvailable()) global->optMode = OPT_MODE_VECTOR_UNROLLED;
218 #endif
219
220 #else
221     global->optMode = OPT_MODE_SCALAR_BASE;
222 #endif
223 #endif /* 0 */
224 }
225
226 static
227 void GLRenderScene(global_info_t *global, flurry_info_t *flurry, double b)
228 {
229     int i;
230
231     flurry->dframe++;
232
233     flurry->fOldTime = flurry->fTime;
234     flurry->fTime = TimeInSecondsSinceStart() + flurry->flurryRandomSeed;
235     flurry->fDeltaTime = flurry->fTime - flurry->fOldTime;
236
237     flurry->drag = (float) pow(0.9965,flurry->fDeltaTime*85.0);
238
239     UpdateStar(global, flurry, flurry->star);
240
241 #ifdef DRAW_SPARKS
242     glShadeModel(GL_SMOOTH);
243     glEnable(GL_BLEND);
244     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
245 #endif
246
247     for (i=0;i<flurry->numStreams;i++) {
248         flurry->spark[i]->color[0]=1.0;
249         flurry->spark[i]->color[1]=1.0;
250         flurry->spark[i]->color[2]=1.0;
251         flurry->spark[i]->color[2]=1.0;
252         UpdateSpark(global, flurry, flurry->spark[i]);
253 #ifdef DRAW_SPARKS
254         DrawSpark(global, flurry, flurry->spark[i]);
255 #endif
256     }
257
258     switch(global->optMode) {
259         case OPT_MODE_SCALAR_BASE:
260             UpdateSmoke_ScalarBase(global, flurry, flurry->s);
261             break;
262 #if 0
263 #ifdef __ppc__
264         case OPT_MODE_SCALAR_FRSQRTE:
265             UpdateSmoke_ScalarFrsqrte(global, flurry, flurry->s);
266             break;
267 #endif
268 #ifdef __VEC__
269         case OPT_MODE_VECTOR_SIMPLE:
270             UpdateSmoke_VectorBase(global, flurry, flurry->s);
271             break;
272         case OPT_MODE_VECTOR_UNROLLED:
273             UpdateSmoke_VectorUnrolled(global, flurry, flurry->s);
274             break;
275 #endif
276 #endif /* 0 */
277
278         default:
279             break;
280     }
281
282     /* glDisable(GL_BLEND); */
283     glEnable(GL_BLEND);
284     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
285     glEnable(GL_TEXTURE_2D);
286
287     switch(global->optMode) {
288         case OPT_MODE_SCALAR_BASE:
289 #if 0
290 #ifdef __ppc__
291         case OPT_MODE_SCALAR_FRSQRTE:
292 #endif
293 #endif /* 0 */
294             DrawSmoke_Scalar(global, flurry, flurry->s, b);
295             break;
296 #if 0
297 #ifdef __VEC__
298         case OPT_MODE_VECTOR_SIMPLE:
299         case OPT_MODE_VECTOR_UNROLLED:
300             DrawSmoke_Vector(global, flurry, flurry->s, b);
301             break;
302 #endif
303 #endif /* 0 */
304         default:
305             break;
306     }    
307
308     glDisable(GL_TEXTURE_2D);
309 }
310
311 static
312 void GLResize(global_info_t *global, float w, float h)
313 {
314     global->sys_glWidth = w;
315     global->sys_glHeight = h;
316 }
317
318 /* new window size or exposure */
319 ENTRYPOINT void reshape_flurry(ModeInfo *mi, int width, int height)
320 {
321     global_info_t *global = flurry_info + MI_SCREEN(mi);
322
323     glXMakeCurrent(MI_DISPLAY(mi), global->window, *(global->glx_context));
324
325     glViewport(0.0, 0.0, width, height);
326     glMatrixMode(GL_PROJECTION);
327     glLoadIdentity();
328     gluOrtho2D(0, width, 0, height);
329     glMatrixMode(GL_MODELVIEW);
330     glClear(GL_COLOR_BUFFER_BIT);
331     glFlush();
332     GLResize(global, (float)width, (float)height);
333 }
334
335 ENTRYPOINT void
336 init_flurry(ModeInfo * mi)
337 {
338     int screen = MI_SCREEN(mi);
339     int i;
340     global_info_t *global;
341     enum {
342         PRESET_INSANE = -1,
343         PRESET_WATER = 0,
344         PRESET_FIRE,
345         PRESET_PSYCHEDELIC,
346         PRESET_RGB,
347         PRESET_BINARY,
348         PRESET_CLASSIC,
349         PRESET_MAX
350     } preset_num;
351
352     if (flurry_info == NULL) {
353         OTSetup();
354         if ((flurry_info = (global_info_t *) calloc(MI_NUM_SCREENS(mi),
355                         sizeof (global_info_t))) == NULL)
356             return;
357     }
358
359     global = &flurry_info[screen];
360
361     global->window = MI_WINDOW(mi);
362
363     global->flurry = NULL;
364
365     if (!preset_str || !*preset_str) preset_str = DEF_PRESET;
366     if (!strcmp(preset_str, "random")) {
367         preset_num = random() % PRESET_MAX;
368     } else if (!strcmp(preset_str, "water")) {
369         preset_num = PRESET_WATER;
370     } else if (!strcmp(preset_str, "fire")) {
371         preset_num = PRESET_FIRE;
372     } else if (!strcmp(preset_str, "psychedelic")) {
373         preset_num = PRESET_PSYCHEDELIC;
374     } else if (!strcmp(preset_str, "rgb")) {
375         preset_num = PRESET_RGB;
376     } else if (!strcmp(preset_str, "binary")) {
377         preset_num = PRESET_BINARY;
378     } else if (!strcmp(preset_str, "classic")) {
379         preset_num = PRESET_CLASSIC;
380     } else if (!strcmp(preset_str, "insane")) {
381         preset_num = PRESET_INSANE;
382     } else {
383         fprintf(stderr, "%s: unknown preset %s\n", progname, preset_str);
384         exit(1);
385     }
386
387     switch (preset_num) {
388     case PRESET_WATER: {
389         for (i = 0; i < 9; i++) {
390             flurry_info_t *flurry;
391
392             flurry = new_flurry_info(global, 1, blueColorMode, 100.0, 2.0, 2.0);
393             flurry->next = global->flurry;
394             global->flurry = flurry;
395         }
396         break;
397     }
398     case PRESET_FIRE: {
399         flurry_info_t *flurry;
400
401         flurry = new_flurry_info(global, 12, slowCyclicColorMode, 10000.0, 0.2, 1.0);
402         flurry->next = global->flurry;
403         global->flurry = flurry;        
404         break;
405     }
406     case PRESET_PSYCHEDELIC: {
407         flurry_info_t *flurry;
408
409         flurry = new_flurry_info(global, 10, rainbowColorMode, 200.0, 2.0, 1.0);
410         flurry->next = global->flurry;
411         global->flurry = flurry;        
412         break;
413     }
414     case PRESET_RGB: {
415         flurry_info_t *flurry;
416
417         flurry = new_flurry_info(global, 3, redColorMode, 100.0, 0.8, 1.0);
418         flurry->next = global->flurry;
419         global->flurry = flurry;        
420
421         flurry = new_flurry_info(global, 3, greenColorMode, 100.0, 0.8, 1.0);
422         flurry->next = global->flurry;
423         global->flurry = flurry;        
424
425         flurry = new_flurry_info(global, 3, blueColorMode, 100.0, 0.8, 1.0);
426         flurry->next = global->flurry;
427         global->flurry = flurry;        
428         break;
429     }
430     case PRESET_BINARY: {
431         flurry_info_t *flurry;
432
433         flurry = new_flurry_info(global, 16, tiedyeColorMode, 1000.0, 0.5, 1.0);
434         flurry->next = global->flurry;
435         global->flurry = flurry;
436
437         flurry = new_flurry_info(global, 16, tiedyeColorMode, 1000.0, 1.5, 1.0);
438         flurry->next = global->flurry;
439         global->flurry = flurry;
440         break;
441     }
442     case PRESET_CLASSIC: {
443         flurry_info_t *flurry;
444
445         flurry = new_flurry_info(global, 5, tiedyeColorMode, 10000.0, 1.0, 1.0);
446         flurry->next = global->flurry;
447         global->flurry = flurry;
448         break;
449     }
450     case PRESET_INSANE: {
451         flurry_info_t *flurry;
452
453         flurry = new_flurry_info(global, 64, tiedyeColorMode, 1000.0, 0.5, 0.5);
454         flurry->next = global->flurry;
455         global->flurry = flurry;
456
457         break;
458     }
459     default: {
460         fprintf(stderr, "%s: unknown preset %s\n", progname, preset_str);
461         exit(1);
462     }
463     } 
464
465     if ((global->glx_context = init_GL(mi)) != NULL) {
466         reshape_flurry(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
467         GLSetupRC(global);
468     } else {
469         MI_CLEARWINDOW(mi);
470     }
471 }
472
473 ENTRYPOINT void
474 draw_flurry(ModeInfo * mi)
475 {
476     static int first = 1;
477     static double oldFrameTime = -1;
478     double newFrameTime;
479     double deltaFrameTime = 0;
480     double brite;
481     GLfloat alpha;
482
483     global_info_t *global = flurry_info + MI_SCREEN(mi);
484     flurry_info_t *flurry;
485     Display    *display = MI_DISPLAY(mi);
486     Window      window = MI_WINDOW(mi);
487
488     newFrameTime = currentTime();
489     if (oldFrameTime == -1) {
490         /* special case the first frame -- clear to black */
491         alpha = 1.0;
492     } else {
493         /* 
494          * this clamps the speed at below 60fps and, here
495          * at least, produces a reasonably accurate 50fps.
496          * (probably part CPU speed and part scheduler).
497          *
498          * Flurry is designed to run at this speed; much higher
499          * than that and the blending causes the display to
500          * saturate, which looks really ugly.
501          */
502         if (newFrameTime - oldFrameTime < 1/60.0) {
503             usleep(MAX_(1,(int)(20000 * (newFrameTime - oldFrameTime))));
504             return;
505
506         }
507         deltaFrameTime = newFrameTime - oldFrameTime;
508         alpha = 5.0 * deltaFrameTime;
509     }
510     oldFrameTime = newFrameTime;
511
512     if (alpha > 0.2) alpha = 0.2;
513
514     if (!global->glx_context)
515         return;
516
517     if (first) {
518         MakeTexture();
519         first = 0;
520     }
521     glDrawBuffer(GL_BACK);
522     glXMakeCurrent(display, window, *(global->glx_context));
523
524     glEnable(GL_BLEND);
525     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
526
527     glColor4f(0.0, 0.0, 0.0, alpha);
528     glRectd(0, 0, global->sys_glWidth, global->sys_glHeight);
529
530     brite = pow(deltaFrameTime,0.75) * 10;
531     for (flurry = global->flurry; flurry; flurry=flurry->next) {
532         GLRenderScene(global, flurry, brite * flurry->briteFactor);
533     }
534
535     if (mi->fps_p) do_fps (mi);
536
537     glFinish();
538     glXSwapBuffers(display, window);
539 }
540
541 ENTRYPOINT void
542 release_flurry(ModeInfo * mi)
543 {
544     if (flurry_info != NULL) {
545         int screen;
546
547         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
548             global_info_t *global = &flurry_info[screen];
549             flurry_info_t *flurry;
550
551             if (global->glx_context) {
552                 glXMakeCurrent(MI_DISPLAY(mi), global->window, *(global->glx_context));
553             }
554
555             for (flurry = global->flurry; flurry; flurry=flurry->next) {
556                 delete_flurry_info(flurry);
557             }
558         }
559         (void) free((void *) flurry_info);
560         flurry_info = NULL;
561     }
562     FreeAllGL(mi);
563 }
564
565 XSCREENSAVER_MODULE ("Flurry", flurry)
566
567 #endif