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