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