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