f2b74ff862230ed60029e682af73a7fc78797466
[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     glClearColor(0.0,0.0,0.0,1.0);
207     glClear(GL_COLOR_BUFFER_BIT);
208
209     glEnableClientState(GL_COLOR_ARRAY);        
210     glEnableClientState(GL_VERTEX_ARRAY);       
211     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
212     
213 #if 0
214 #ifdef __ppc__
215     global->optMode = OPT_MODE_SCALAR_FRSQRTE;
216
217 #ifdef __VEC__
218     if (IsAltiVecAvailable()) global->optMode = OPT_MODE_VECTOR_UNROLLED;
219 #endif
220
221 #else
222     global->optMode = OPT_MODE_SCALAR_BASE;
223 #endif
224 #endif /* 0 */
225 }
226
227 static
228 void GLRenderScene(global_info_t *global, flurry_info_t *flurry, double b)
229 {
230     int i;
231
232     flurry->dframe++;
233
234     flurry->fOldTime = flurry->fTime;
235     flurry->fTime = TimeInSecondsSinceStart() + flurry->flurryRandomSeed;
236     flurry->fDeltaTime = flurry->fTime - flurry->fOldTime;
237
238     flurry->drag = (float) pow(0.9965,flurry->fDeltaTime*85.0);
239
240     UpdateStar(global, flurry, flurry->star);
241
242 #ifdef DRAW_SPARKS
243     glShadeModel(GL_SMOOTH);
244     glEnable(GL_BLEND);
245     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
246 #endif
247
248     for (i=0;i<flurry->numStreams;i++) {
249         flurry->spark[i]->color[0]=1.0;
250         flurry->spark[i]->color[1]=1.0;
251         flurry->spark[i]->color[2]=1.0;
252         flurry->spark[i]->color[2]=1.0;
253         UpdateSpark(global, flurry, flurry->spark[i]);
254 #ifdef DRAW_SPARKS
255         DrawSpark(global, flurry, flurry->spark[i]);
256 #endif
257     }
258
259     switch(global->optMode) {
260         case OPT_MODE_SCALAR_BASE:
261             UpdateSmoke_ScalarBase(global, flurry, flurry->s);
262             break;
263 #if 0
264 #ifdef __ppc__
265         case OPT_MODE_SCALAR_FRSQRTE:
266             UpdateSmoke_ScalarFrsqrte(global, flurry, flurry->s);
267             break;
268 #endif
269 #ifdef __VEC__
270         case OPT_MODE_VECTOR_SIMPLE:
271             UpdateSmoke_VectorBase(global, flurry, flurry->s);
272             break;
273         case OPT_MODE_VECTOR_UNROLLED:
274             UpdateSmoke_VectorUnrolled(global, flurry, flurry->s);
275             break;
276 #endif
277 #endif /* 0 */
278
279         default:
280             break;
281     }
282
283     /* glDisable(GL_BLEND); */
284     glEnable(GL_BLEND);
285     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
286     glEnable(GL_TEXTURE_2D);
287
288     switch(global->optMode) {
289         case OPT_MODE_SCALAR_BASE:
290 #if 0
291 #ifdef __ppc__
292         case OPT_MODE_SCALAR_FRSQRTE:
293 #endif
294 #endif /* 0 */
295             DrawSmoke_Scalar(global, flurry, flurry->s, b);
296             break;
297 #if 0
298 #ifdef __VEC__
299         case OPT_MODE_VECTOR_SIMPLE:
300         case OPT_MODE_VECTOR_UNROLLED:
301             DrawSmoke_Vector(global, flurry, flurry->s, b);
302             break;
303 #endif
304 #endif /* 0 */
305         default:
306             break;
307     }    
308
309     glDisable(GL_TEXTURE_2D);
310 }
311
312 static
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 ENTRYPOINT 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 ENTRYPOINT 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.2, 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 ENTRYPOINT 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 ENTRYPOINT 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 XSCREENSAVER_MODULE ("Flurry", flurry)
571
572 #endif