de7d55e741b262ab40b0d2a2791bff964512f659
[xscreensaver] / hacks / glx / blinkbox.c
1 /* blinkbox, Copyright (c) 2003 Jeremy English <jenglish@myself.com>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  */
11
12 /* motion blur added March 2005 by John Boero <jlboero@cs.uwm.edu>
13  */
14
15 #define DEFAULTS        "*delay:        30000            \n" \
16                         "*wireframe:    False            \n" \
17
18 # define refresh_ball 0
19 # define release_ball 0
20 # define ball_handle_event 0
21 #undef countof
22 #define countof(x) (sizeof((x))/sizeof((*x)))
23
24 #include "xlockmore.h"
25 #include "sphere.h"
26 #include <ctype.h>
27
28 #ifdef USE_GL /* whole file */
29
30 #define MAX_COUNT 20
31 #define ALPHA_AMT 0.05
32
33 /* this should be between 1 and 8 */
34 #define DEF_BOXSIZE  "2"
35 #define DEF_DISSOLVE "False"
36 #define DEF_FADE     "True"
37 #define DEF_BLUR     "True"
38
39
40 typedef struct{
41   GLfloat x,y,z;
42 } Tdpos;
43
44 typedef struct{
45   int hit;
46   Tdpos pos;
47   int counter;
48   GLfloat color[3];
49   GLfloat rot[4];
50   int des_count;
51   int alpha_count;
52 }Side;
53
54 struct Bounding_box {
55   Tdpos top;
56   Tdpos bottom;
57 };
58
59 struct Ball {
60   GLfloat x;
61   GLfloat y;
62   GLfloat z;
63   int d;
64 };
65
66 struct bscale {
67   GLfloat wh; /*width Height*/
68   GLfloat d; /*depth*/
69 };
70
71 static const struct Bounding_box bbox = {{14,14,20},{-14,-14,-20}};
72
73 typedef struct {
74   GLXContext *glx_context;
75
76   struct Ball ball;
77
78   struct bscale bscale;
79
80   Tdpos mo;  /*motion*/
81   Tdpos moh; /*hold motion value*/
82
83   Tdpos bpos;
84
85   GLuint ballList;
86   GLuint boxList;
87   GLfloat des_amt;
88
89   /*sides*/
90   Side lside;/*Red*/
91   Side rside;/*Green*/
92   Side tside;/*Blue*/
93   Side bside;/*Orange*/
94   Side fside;/*Yellow*/
95   Side aside;/*Purple*/
96   Side *sp;
97
98 } blinkboxstruct;
99
100 static blinkboxstruct *blinkbox = (blinkboxstruct *) NULL;
101
102
103 /* lights */
104 static const float LightDiffuse[]=   { 1.0f, 1.0f, 1.0f, 1.0f };
105 static const float LightPosition[]=  { 20.0f, 100.0f, 20.0f, 1.0f };
106
107 static Bool do_dissolve;
108 static Bool do_fade;
109 static Bool do_blur;
110 static float bscale_wh;
111
112 static XrmOptionDescRec opts[] = {
113   { "-boxsize",  ".boxsize",  XrmoptionSepArg, 0      },
114   { "-dissolve", ".dissolve", XrmoptionNoArg, "True"  },
115   { "+dissolve", ".dissolve", XrmoptionNoArg, "False" },
116   { "-fade",     ".fade",     XrmoptionNoArg, "True"  },
117   { "+fade",     ".fade",     XrmoptionNoArg, "False" },
118   { "-blur",     ".blur",     XrmoptionNoArg, "True"  },
119   { "+blur",     ".blur",     XrmoptionNoArg, "False" }
120
121 };
122
123 static argtype vars[] = {
124   {&bscale_wh,   "boxsize",   "Boxsize",  DEF_BOXSIZE,  t_Float},
125   {&do_dissolve, "dissolve",  "Dissolve", DEF_DISSOLVE, t_Bool},
126   {&do_fade,     "fade",      "Fade",     DEF_FADE,     t_Bool},
127   {&do_blur,     "blur",      "Blur",     DEF_BLUR,     t_Bool},
128 };
129
130 ENTRYPOINT ModeSpecOpt ball_opts = {countof(opts), opts, countof(vars), vars, NULL};
131
132 static void
133 swap(GLfloat *a, GLfloat *b)
134 {
135   GLfloat t = *a;
136   *a = *b;
137   *b = t;
138 }
139
140 static float
141 get_rand(void)
142 {
143   GLfloat j = 1+(random() % 2);
144   return (j);
145 }
146
147 static void
148 swap_mov(GLfloat *a, GLfloat *b)
149 {
150   int j;
151   swap(a,b);
152   j = get_rand();
153   if (*a < 0)
154     *a = (j *= -1);
155   else
156     *a = j;
157 }
158
159 static void
160 cp_b_pos(blinkboxstruct *bp, Tdpos *s_pos)
161 {
162   s_pos->x = bp->ball.x;
163   s_pos->y = bp->ball.y;
164   s_pos->z = bp->ball.z;
165 }
166
167 static void
168 hit_side(blinkboxstruct *bp)
169 {
170   if ((bp->ball.x - bp->ball.d) <= bbox.bottom.x){
171     bp->lside.hit = 1;
172     bp->lside.counter   = MAX_COUNT;
173     bp->lside.des_count = 1;
174     bp->lside.alpha_count = 0;
175     cp_b_pos(bp, &bp->lside.pos);
176     swap_mov(&bp->mo.x,&bp->moh.x);
177   }else
178   if ((bp->ball.x + bp->ball.d) >= bbox.top.x){
179     bp->rside.hit = 1;
180     bp->rside.counter = MAX_COUNT;
181     bp->rside.des_count = 1;
182     bp->rside.alpha_count = 0;
183     cp_b_pos(bp, &bp->rside.pos);
184     swap_mov(&bp->mo.x,&bp->moh.x);
185   }
186 }
187
188 static void
189 hit_top_bottom(blinkboxstruct *bp)
190 {
191   if ((bp->ball.y - bp->ball.d) <= bbox.bottom.y){
192     bp->bside.hit = 1;
193     bp->bside.counter = MAX_COUNT;
194     bp->bside.des_count = 1;
195     bp->bside.alpha_count = 0;
196     cp_b_pos(bp, &bp->bside.pos);
197     swap_mov(&bp->mo.y,&bp->moh.y);
198   }else
199   if ((bp->ball.y + bp->ball.d) >= bbox.top.y){
200     bp->tside.hit = 1;
201     bp->tside.counter = MAX_COUNT;
202     bp->tside.des_count = 1;
203     bp->tside.alpha_count = 0;
204     cp_b_pos(bp, &bp->tside.pos);
205     swap_mov(&bp->mo.y,&bp->moh.y);
206   }
207 }
208
209 static void
210 hit_front_back(blinkboxstruct *bp)
211 {
212   if ((bp->ball.z - bp->ball.d) <= bbox.bottom.z){
213     bp->aside.hit = 1;
214     bp->aside.counter = MAX_COUNT;
215     bp->aside.des_count = 1;
216     bp->aside.alpha_count = 0;
217     cp_b_pos(bp, &bp->aside.pos);
218     swap_mov(&bp->mo.z,&bp->moh.z);
219   }else
220   if((bp->ball.z + bp->ball.d) >= bbox.top.z){
221     bp->fside.hit = 1;
222     bp->fside.counter = MAX_COUNT;
223     bp->fside.des_count = 1;
224     bp->fside.alpha_count = 0;
225     cp_b_pos(bp, &bp->fside.pos);
226     swap_mov(&bp->mo.z,&bp->moh.z);
227   }
228 }
229
230 ENTRYPOINT void
231 reshape_ball (ModeInfo *mi, int width, int height)
232 {
233   GLfloat h = (GLfloat) height / (GLfloat) width;
234
235   glViewport (0, 0, (GLint) width, (GLint) height);
236   glMatrixMode(GL_PROJECTION);
237   glLoadIdentity();
238   gluPerspective (30.0, 1/h, 1.0, 100.0);
239
240   glMatrixMode(GL_MODELVIEW);
241   glLoadIdentity();
242   gluLookAt( 0.0, 0.0, 40.0,
243              0.0, 0.0, 0.0,
244              0.0, 2.0,  10.0);
245
246 }
247
248 static void
249 unit_cube(int wire)
250 {
251   glBegin((wire)?GL_LINE_LOOP:GL_QUADS);
252   glNormal3f( 0.0f, -1.0f, 0.0f);
253   glVertex3f(-1.0f, -1.0f, -1.0f);
254   glVertex3f( 1.0f, -1.0f, -1.0f);
255   glVertex3f( 1.0f, -1.0f,  1.0f);
256   glVertex3f(-1.0f, -1.0f,  1.0f);
257   glNormal3f( 0.0f,  0.0f,  1.0f);
258   glVertex3f(-1.0f, -1.0f,  1.0f);
259   glVertex3f( 1.0f, -1.0f,  1.0f);
260   glVertex3f( 1.0f,  1.0f,  1.0f);
261   glVertex3f(-1.0f,  1.0f,  1.0f);
262   glNormal3f( 0.0f,  0.0f, -1.0f);
263   glVertex3f(-1.0f, -1.0f, -1.0f);
264   glVertex3f(-1.0f,  1.0f, -1.0f);
265   glVertex3f( 1.0f,  1.0f, -1.0f);
266   glVertex3f( 1.0f, -1.0f, -1.0f);
267   glNormal3f( 1.0f,  0.0f,  0.0f);
268   glVertex3f( 1.0f, -1.0f, -1.0f);
269   glVertex3f( 1.0f,  1.0f, -1.0f);
270   glVertex3f( 1.0f,  1.0f,  1.0f);
271   glVertex3f( 1.0f, -1.0f,  1.0f);
272   glNormal3f( -1.0f, 0.0f,  0.0f);
273   glVertex3f(-1.0f, -1.0f, -1.0f);
274   glVertex3f(-1.0f, -1.0f,  1.0f);
275   glVertex3f(-1.0f,  1.0f,  1.0f);
276   glVertex3f(-1.0f,  1.0f, -1.0f);
277   glNormal3f( 1.0f,  1.0f,  0.0f);
278   glVertex3f(-1.0f,  1.0f, -1.0f);
279   glVertex3f(-1.0f,  1.0f,  1.0f);
280   glVertex3f( 1.0f,  1.0f,  1.0f);
281   glVertex3f( 1.0f,  1.0f, -1.0f);
282   glEnd();
283 }
284
285 ENTRYPOINT void
286 init_ball (ModeInfo *mi)
287 {
288   int wire = MI_IS_WIREFRAME(mi);
289   blinkboxstruct *bp;
290   
291   if(blinkbox == NULL) {
292     if((blinkbox = (blinkboxstruct *) calloc(MI_NUM_SCREENS(mi),
293                                              sizeof (blinkboxstruct))) == NULL)
294       return;
295   }
296   bp = &blinkbox[MI_SCREEN(mi)];
297
298   if ((bp->glx_context = init_GL(mi)) != NULL) {
299     reshape_ball(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
300     glDrawBuffer(GL_BACK);
301   }
302   else
303     MI_CLEARWINDOW(mi);
304
305   bp->ball.d = 1;
306   bp->bscale.wh = bscale_wh;
307   bp->bscale.d = 0.25;
308
309   bp->mo.x = 1;
310   bp->mo.y = 1;
311   bp->mo.z = 1;
312
313   bp->moh.x = -1.0;
314   bp->moh.y = -1.5;
315   bp->moh.z = -1.5;
316
317   bp->bpos.x = 1;
318   bp->bpos.y = 1;
319   bp->bpos.z = 1;
320
321   bp->des_amt = 1;
322
323   bp->lside.counter = MAX_COUNT;
324   bp->rside.counter = MAX_COUNT;
325   bp->tside.counter = MAX_COUNT;
326   bp->bside.counter = MAX_COUNT;
327   bp->fside.counter = MAX_COUNT;
328   bp->aside.counter = MAX_COUNT;
329
330   bp->lside.color[0] = 1;
331   bp->rside.color[1] = 1;
332   bp->tside.color[2] = 1;
333
334   bp->bside.color[0] = 1;
335   bp->bside.color[1] = 0.5;
336
337   bp->fside.color[0] = 1;
338   bp->fside.color[1] = 1;
339
340   bp->aside.color[0] = 0.5;
341   bp->aside.color[2] = 1;
342
343   bp->lside.rot[0] = 90;
344   bp->rside.rot[0] = 90;
345   bp->tside.rot[0] = 90;
346   bp->bside.rot[0] = 90;
347   bp->fside.rot[0] = 90;
348   bp->aside.rot[0] = 90;
349
350   bp->lside.rot[2] = 1;
351   bp->rside.rot[2] = 1;
352   bp->tside.rot[1] = 1;
353   bp->bside.rot[1] = 1;
354   bp->fside.rot[3] = 1;
355   bp->aside.rot[3] = 1;
356
357   bp->lside.des_count = 1;
358   bp->rside.des_count = 1;
359   bp->tside.des_count = 1;
360   bp->bside.des_count = 1;
361   bp->fside.des_count = 1;
362   bp->aside.des_count = 1;
363
364   bp->lside.alpha_count = 1;
365   bp->rside.alpha_count = 1;
366   bp->tside.alpha_count = 1;
367   bp->bside.alpha_count = 1;
368   bp->fside.alpha_count = 1;
369   bp->aside.alpha_count = 1;
370
371
372 #define SPHERE_SLICES 12  /* how densely to render spheres */
373 #define SPHERE_STACKS 16
374
375   bp->sp = malloc(sizeof(*bp->sp));
376   if(bp->sp == NULL){
377     fprintf(stderr,"Could not allocate memory\n");
378     exit(1);
379   }
380   if( (bp->bscale.wh < 1) ||
381       (bp->bscale.wh > 8) ) {
382     fprintf(stderr,"Boxsize out of range. Using default\n");
383     bp->bscale.wh = 2;
384   }
385   if (do_dissolve){
386     bp->des_amt = bp->bscale.wh / MAX_COUNT;
387   }
388
389   bp->glx_context = init_GL(mi);
390
391   reshape_ball(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
392   bp->ballList = glGenLists(1);
393   glNewList(bp->ballList, GL_COMPILE);
394   unit_sphere (SPHERE_STACKS, SPHERE_SLICES, wire);
395   glEndList ();
396
397   bp->boxList = glGenLists(1);
398   glNewList(bp->boxList, GL_COMPILE);
399   unit_cube(wire);
400   glEndList();
401
402   if (wire) return;
403
404   glEnable(GL_COLOR_MATERIAL);
405   glShadeModel(GL_SMOOTH);
406   glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
407   glClearDepth(1.0f);
408   glEnable(GL_DEPTH_TEST);
409   glDepthFunc(GL_LEQUAL);
410   glEnable(GL_LIGHTING);
411   glClearDepth(1);
412   glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
413   glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
414   glEnable(GL_LIGHT1);
415   if (do_fade || do_blur) {
416     glEnable(GL_BLEND);
417     glDisable(GL_DEPTH_TEST);
418   }
419 }
420
421 static void
422 CheckBoxPos(blinkboxstruct *bp, 
423             GLfloat bot_x, GLfloat top_x, GLfloat bot_y, GLfloat top_y)
424 {
425   /*Make sure it's inside of the bounding box*/
426   bp->bpos.x = ((bp->bpos.x - bp->bscale.wh) < bot_x) ? bot_x + bp->bscale.wh : bp->bpos.x;
427   bp->bpos.x = ((bp->bpos.x + bp->bscale.wh) > top_x) ? top_x - bp->bscale.wh : bp->bpos.x;
428   bp->bpos.y = ((bp->bpos.y - bp->bscale.wh) < bot_y) ? bot_y + bp->bscale.wh : bp->bpos.y;
429   bp->bpos.y = ((bp->bpos.y + bp->bscale.wh) > top_y) ? top_y - bp->bscale.wh : bp->bpos.y;
430 }
431
432 ENTRYPOINT void
433 draw_ball (ModeInfo *mi)
434 {
435    blinkboxstruct *bp = &blinkbox[MI_SCREEN(mi)];
436
437    Display *dpy = MI_DISPLAY(mi);
438    Window window = MI_WINDOW(mi);
439    int i = 0;
440
441    if (! bp->glx_context)
442      return;
443    mi->polygon_count = 0;
444    glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
445
446    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
447
448    hit_top_bottom(bp);
449    hit_front_back(bp);
450    hit_side(bp);
451
452    glRotated(0.25,0,0,1);
453    glRotated(0.25,0,1,0);
454    glRotated(0.25,1,0,0);
455
456
457    glPushMatrix();
458    glScalef(0.5,0.5,0.5);
459
460    glColor3f(1,1,1);
461    glPushMatrix();
462
463    if (!do_blur || MI_IS_WIREFRAME(mi)) {
464      glTranslatef(bp->ball.x += bp->mo.x,
465                   bp->ball.y += bp->mo.y,
466                   bp->ball.z += bp->mo.z);
467
468      glScalef(2,2,2);
469      glCallList(bp->ballList);
470      mi->polygon_count += SPHERE_SLICES*SPHERE_STACKS;
471
472    } else {
473
474 #    define blur_detail 24.0
475      float ball_alpha = 1 / blur_detail;
476
477      glBlendFunc(GL_SRC_ALPHA,GL_ONE);
478      glTranslatef(bp->ball.x, bp->ball.y, bp->ball.z);
479    
480      for (i = 0; i < blur_detail; ++i) {
481        glTranslatef(bp->mo.x / blur_detail,
482                     bp->mo.y / blur_detail,
483                     bp->mo.z / blur_detail);
484
485        /* comment the following line for quick but boring linear blur */
486        ball_alpha = sin((M_PI / blur_detail) * i) / blur_detail;
487      
488        glColor4f(1, 1, 1, ball_alpha);
489
490        glScalef(2, 2, 2);
491        glCallList(bp->ballList);
492        mi->polygon_count += SPHERE_SLICES*SPHERE_STACKS;
493        glScalef(.5, .5, .5);
494      }
495      i = 0;
496    
497      bp->ball.x += bp->mo.x;
498      bp->ball.y += bp->mo.y;
499      bp->ball.z += bp->mo.z;
500    }
501    
502    glPopMatrix();
503
504    while(i < 6){
505     switch(i){
506       case 0:{
507                bp->sp = &bp->lside;
508                bp->bpos.x = bp->lside.pos.z*-1;
509                bp->bpos.y = bp->lside.pos.y;
510                bp->bpos.z = bbox.bottom.x - bp->bscale.d;
511                if (bp->sp->hit)
512                 CheckBoxPos(bp, bbox.bottom.z,bbox.top.z,bbox.bottom.y,bbox.top.y);
513                break;
514              }
515       case 1:{
516                bp->sp = &bp->rside;
517                bp->bpos.x = bp->rside.pos.z*-1;
518                bp->bpos.y = bp->rside.pos.y;
519                bp->bpos.z = bbox.top.x + bp->bscale.d;
520                if (bp->sp->hit)
521                 CheckBoxPos(bp, bbox.bottom.z,bbox.top.z,bbox.bottom.y,bbox.top.y);
522                break;
523              }
524       case 2:{
525                bp->sp = &bp->tside;
526                bp->bpos.x = bp->tside.pos.x;
527                bp->bpos.y = bp->tside.pos.z;
528                bp->bpos.z = bbox.bottom.y - bp->bscale.d;
529                if (bp->sp->hit)
530                 CheckBoxPos(bp, bbox.bottom.x,bbox.top.x,bbox.bottom.z,bbox.top.z);
531                break;
532              }
533       case 3:{
534                bp->sp = &bp->bside;
535                bp->bpos.x = bp->bside.pos.x;
536                bp->bpos.y = bp->bside.pos.z;
537                bp->bpos.z = bbox.top.y + bp->bscale.d;
538                if (bp->sp->hit)
539                 CheckBoxPos(bp, bbox.bottom.x,bbox.top.x,bbox.bottom.z,bbox.top.z);
540                break;
541              }
542       case 4:{
543                bp->sp = &bp->fside;
544                bp->bpos.x = bp->fside.pos.y;
545                bp->bpos.y = bp->fside.pos.x*-1;
546                bp->bpos.z = bbox.top.z + bp->bscale.d;
547                if (bp->sp->hit)
548                 CheckBoxPos(bp, bbox.bottom.y,bbox.top.y,bbox.bottom.x,bbox.top.x);
549                break;
550              }
551       case 5:{
552                bp->sp = &bp->aside;
553                bp->bpos.x = bp->aside.pos.y;
554                bp->bpos.y = bp->aside.pos.x*-1;
555                bp->bpos.z = bbox.bottom.z + bp->bscale.d;
556                if (bp->sp->hit)
557                 CheckBoxPos(bp, bbox.bottom.y,bbox.top.y,bbox.bottom.x,bbox.top.x);
558                break;
559              }
560     }
561     if(bp->sp->hit){
562       if(do_fade){
563         glColor4f(bp->sp->color[0],bp->sp->color[1],bp->sp->color[2],1-(ALPHA_AMT * bp->sp->alpha_count));
564       }else{
565         glColor3fv(bp->sp->color);
566       }
567       glBlendFunc(GL_SRC_ALPHA,GL_ONE);
568       glPushMatrix();
569       glRotatef(bp->sp->rot[0],bp->sp->rot[1],bp->sp->rot[2],bp->sp->rot[3]);
570       glTranslatef(bp->bpos.x,bp->bpos.y,bp->bpos.z);
571       if (do_dissolve) {
572          glScalef(bp->bscale.wh-(bp->des_amt*bp->sp->des_count),bp->bscale.wh-(bp->des_amt*bp->sp->des_count),bp->bscale.d);
573       }else{
574         glScalef(bp->bscale.wh,bp->bscale.wh,bp->bscale.d);
575       }
576       glCallList(bp->boxList);
577       mi->polygon_count += 6;
578       glPopMatrix();
579       bp->sp->counter--;
580       bp->sp->des_count++;
581       bp->sp->alpha_count++;
582       if(!bp->sp->counter)
583       {
584         bp->sp->hit = 0;
585       }
586     }
587     i++;
588   }
589
590
591    glPopMatrix();
592   if (mi->fps_p) do_fps (mi);
593    glFinish();
594    glXSwapBuffers(dpy, window);
595
596 }
597
598 XSCREENSAVER_MODULE_2 ("BlinkBox", blinkbox, ball)
599
600 #endif /* USE_GL */