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