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