1aadee77de347218b97282688a1375577650b5af
[xscreensaver] / hacks / glx / flipflop.c
1 /* flipflop, Copyright (c) 2003 Kevin Ogden <kogden1@hotmail.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 #include <X11/Intrinsic.h>
13
14 #include <math.h>
15 #include <sys/time.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18
19 #define BOARDSIZE 9
20 #define NUMSQUARES 76
21 #define HALFTHICK 0.04
22
23 #ifdef STANDALONE
24 # define PROGCLASS        "Flipflop"
25 # define HACK_INIT        init_flipflop
26 # define HACK_DRAW        draw_flipflop
27 # define HACK_RESHAPE     reshape_flipflop
28 # define HACK_HANDLE_EVENT flipflop_handle_event
29 # define EVENT_MASK       PointerMotionMask
30 # define flipflop_opts  xlockmore_opts
31
32 #define DEFAULTS       "*delay:       20000       \n" \
33                        "*showFPS:       False       \n" \
34                        "*wireframe:     False     \n"
35
36 # include "xlockmore.h"
37
38 #else
39 # include "xlock.h"
40 #endif
41
42 #ifdef USE_GL
43
44 #include <GL/glu.h>
45 #include "gltrackball.h"
46
47 #undef countof
48 #define countof(x) (sizeof((x))/sizeof((*x)))
49
50 static XrmOptionDescRec opts[] = {
51   {"+rotate", ".flipflop.rotate", XrmoptionNoArg, "false" },
52   {"-rotate", ".flipflop.rotate", XrmoptionNoArg, "true" },
53 };
54
55
56
57 static int rotate, wire, clearbits;
58
59 static argtype vars[] = {
60   { &rotate, "rotate", "Rotate", "True", t_Bool},
61 };
62
63 ModeSpecOpt flipflop_opts = {countof(opts), opts, countof(vars), vars, NULL};
64
65 #ifdef USE_MODULES
66 ModStruct   flipflop_description =
67 {"flipflop", "init_flipflop", "draw_flipflop", "release_flipflop",
68  "draw_flipflop", "init_flipflop", NULL, &flipflop_opts,
69  1000, 1, 2, 1, 4, 1.0, "",
70  "Flipflop", 0, NULL};
71
72 #endif
73
74 typedef struct {
75   GLXContext *glx_context;
76   Window window;
77   trackball_state *trackball;
78   Bool button_down_p;
79 } Flipflopcreen;
80
81 static Flipflopcreen *qs = NULL;
82
83 typedef struct{
84   /* 2D array specifying which squares are where (to avoid collisions) */
85   /* -1 means empty otherwise integer represents square index 0 - n-1 */
86   int occupied[ BOARDSIZE ][ BOARDSIZE ];
87   /* an array of xpositions of the squares */
88   int xpos[ NUMSQUARES ];
89   /* array of y positions of the squares */
90   int ypos[ NUMSQUARES ];
91   /* integer representing the direction of movement of a square */
92   int direction[ NUMSQUARES ]; /* 0 not, 1 x+, 2 y+, 3 x-, 4 y-*/
93   /* angle of moving square (during a flip) */
94   float angle[ NUMSQUARES ];
95   /* array of colors for a square.  rgb */
96   /* eg. color[ 4 ][ 0 ] is the red component of square 4 */
97   /* eg. color[ 5 ][ 2 ] is the blue component of square 5  */
98   float color[ NUMSQUARES ][ 3 ];
99   /* n is the number of square */
100 } randsheet;
101
102
103 /*** ADDED RANDSHEET VARS ***/
104
105 static randsheet MyRandSheet;
106
107 static double theta = 0.0;
108 /* amount which the square flips.  1 is a entire flip */
109 static float flipspeed = 0.03;
110 /* relative distace of camera from center */
111 static float reldist = 1;
112 /* likelehood a square will attempt a move */
113 static float energy = 40;
114
115
116 static void randsheet_initialize( randsheet *rs );
117 static int randsheet_new_move( randsheet* rs );
118 static int randsheet_new_move( randsheet* rs );
119 static void randsheet_move( randsheet *rs, float rot );
120 static void randsheet_draw( randsheet *rs );
121 static void setup_lights(void);
122 static void drawBoard(void);
123 static void display(Flipflopcreen *c);
124 static void draw_sheet(void);
125
126
127 /* configure lighting */
128 static void
129 setup_lights(void)
130 {
131 /*   GLfloat position0[] = { BOARDSIZE*0.5, BOARDSIZE*0.1, BOARDSIZE*0.5, 1.0 }; */
132
133 /*   GLfloat position0[] = { -BOARDSIZE*0.5, 0.2*BOARDSIZE, -BOARDSIZE*0.5, 1.0 }; */
134   GLfloat position0[] = { 0, BOARDSIZE*0.3, 0, 1.0 };
135
136   if (wire) return;
137
138   glEnable(GL_LIGHTING);
139   glLightfv(GL_LIGHT0, GL_POSITION, position0);
140   glEnable(GL_LIGHT0);
141  }
142
143 Bool
144 flipflop_handle_event (ModeInfo *mi, XEvent *event)
145 {
146   Flipflopcreen *c = &qs[MI_SCREEN(mi)];
147
148   if (event->xany.type == ButtonPress &&
149       event->xbutton.button & Button1)
150     {
151       c->button_down_p = True;
152       gltrackball_start (c->trackball,
153                          event->xbutton.x, event->xbutton.y,
154                          MI_WIDTH (mi), MI_HEIGHT (mi));
155       return True;
156     }
157   else if (event->xany.type == ButtonRelease &&
158            event->xbutton.button & Button1)
159     {
160       c->button_down_p = False;
161       return True;
162     }
163   else if (event->xany.type == MotionNotify &&
164            c->button_down_p)
165     {
166       gltrackball_track (c->trackball,
167                          event->xmotion.x, event->xmotion.y,
168                          MI_WIDTH (mi), MI_HEIGHT (mi));
169       return True;
170     }
171
172   return False;
173 }
174
175 /* draw board */
176 static void
177 drawBoard(void)
178 {
179   int i;
180   for( i=0; i < (energy) ; i++ )
181     randsheet_new_move( &MyRandSheet );
182   randsheet_move( &MyRandSheet, flipspeed * 3.14159 );
183   randsheet_draw( &MyRandSheet );
184 }
185
186
187 static void
188 display(Flipflopcreen *c)
189 {
190    GLfloat amb[] = { 0.8, 0.8, 0.8, 1.0 };
191
192
193  glClear(clearbits);
194  glMatrixMode(GL_MODELVIEW);
195  glLoadIdentity();
196
197   glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.2);
198   glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.15/BOARDSIZE );
199   glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.15/BOARDSIZE );
200    glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
201
202
203   /** setup perspectif */
204   glTranslatef(0.0, 0.0, -reldist*BOARDSIZE);
205   glRotatef(22.5, 1.0, 0.0, 0.0);
206   gltrackball_rotate (c->trackball);
207   glRotatef(theta*100, 0.0, 1.0, 0.0);
208   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
209
210   drawBoard();
211
212   if (!c->button_down_p)
213     theta += .001;
214
215 }
216
217 void
218 reshape_flipflop(ModeInfo *mi, int width, int height)
219 {
220   GLfloat h = (GLfloat) height / (GLfloat) width;
221   glViewport(0,0, width, height);
222   glMatrixMode(GL_PROJECTION);
223   glLoadIdentity();
224   gluPerspective(45, 1/h, 1.0, 300.0);
225   glMatrixMode(GL_MODELVIEW);
226 }
227
228 void
229 init_flipflop(ModeInfo *mi)
230 {
231   int screen = MI_SCREEN(mi);
232   Flipflopcreen *c;
233   wire = MI_IS_WIREFRAME(mi);
234
235   if(!qs &&
236      !(qs = (Flipflopcreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Flipflopcreen))))
237     return;
238
239   c = &qs[screen];
240   c->window = MI_WINDOW(mi);
241   c->trackball = gltrackball_init ();
242
243   if((c->glx_context = init_GL(mi)))
244     reshape_flipflop(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
245   else
246     MI_CLEARWINDOW(mi);
247
248   glClearColor(0.0, 0.0, 0.0, 0.0);
249
250   clearbits = GL_COLOR_BUFFER_BIT;
251
252   glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
253   glEnable(GL_COLOR_MATERIAL);
254   setup_lights();
255
256   glEnable(GL_DEPTH_TEST);
257   clearbits |= GL_DEPTH_BUFFER_BIT;
258   glEnable(GL_CULL_FACE);
259   glCullFace(GL_BACK);
260   randsheet_initialize( &MyRandSheet );
261
262
263 }
264
265 void
266 draw_flipflop(ModeInfo *mi)
267 {
268   Flipflopcreen *c = &qs[MI_SCREEN(mi)];
269   Window w = MI_WINDOW(mi);
270   Display *disp = MI_DISPLAY(mi);
271
272   if(!c->glx_context)
273     return;
274
275   glXMakeCurrent(disp, w, *(c->glx_context));
276
277   display(c);
278
279   if(mi->fps_p){
280     do_fps(mi);
281   }
282
283   glFinish();
284   glXSwapBuffers(disp, w);
285
286
287 }
288
289 void
290 release_flipflop(ModeInfo *mi)
291 {
292   if(qs)
293     free((void *) qs);
294
295   FreeAllGL(MI);
296 }
297
298 /*** ADDED RANDSHEET FUNCTIONS ***/
299
300 static void
301 draw_sheet(void)
302 {
303   glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
304
305   glNormal3f( 0, -1, 0 );
306   glVertex3f( HALFTHICK,  -HALFTHICK,  HALFTHICK );
307   glVertex3f( 1-HALFTHICK,   -HALFTHICK, HALFTHICK );
308   glVertex3f( 1-HALFTHICK, -HALFTHICK,  1-HALFTHICK);
309   glVertex3f( HALFTHICK, -HALFTHICK, 1-HALFTHICK );
310
311   if (wire) { glEnd(); glBegin (GL_LINE_LOOP); }
312
313   /* back */
314   glNormal3f( 0, 1, 0 );
315   glVertex3f( HALFTHICK, HALFTHICK, 1-HALFTHICK );
316   glVertex3f( 1-HALFTHICK, HALFTHICK,  1-HALFTHICK);
317   glVertex3f( 1-HALFTHICK,   HALFTHICK, HALFTHICK );
318   glVertex3f( HALFTHICK,  HALFTHICK,  HALFTHICK );
319
320   if (wire) { glEnd(); return; }
321
322   /* 4 edges!!! weee.... */
323   glNormal3f( 0, 0, -1 );
324   glVertex3f( HALFTHICK, HALFTHICK, HALFTHICK );
325   glVertex3f( 1-HALFTHICK, HALFTHICK, HALFTHICK );
326   glVertex3f( 1-HALFTHICK, -HALFTHICK, HALFTHICK );
327   glVertex3f( HALFTHICK, -HALFTHICK, HALFTHICK );
328   glNormal3f( 0, 0, 1 );
329   glVertex3f( HALFTHICK, HALFTHICK, 1-HALFTHICK );
330   glVertex3f( HALFTHICK, -HALFTHICK, 1-HALFTHICK );
331   glVertex3f( 1-HALFTHICK, -HALFTHICK, 1-HALFTHICK );
332   glVertex3f( 1-HALFTHICK, HALFTHICK, 1-HALFTHICK );
333   glNormal3f( 1, 0, 0 );
334   glVertex3f( 1-HALFTHICK, HALFTHICK, 1-HALFTHICK );
335   glVertex3f( 1-HALFTHICK, -HALFTHICK, 1-HALFTHICK );
336   glVertex3f( 1-HALFTHICK, -HALFTHICK, HALFTHICK );
337   glVertex3f( 1-HALFTHICK, HALFTHICK, HALFTHICK );
338   glNormal3f( -1, 0, 0 );
339   glVertex3f( HALFTHICK, HALFTHICK, 1-HALFTHICK );
340   glVertex3f( HALFTHICK, HALFTHICK, HALFTHICK );
341   glVertex3f( HALFTHICK, -HALFTHICK, HALFTHICK );
342   glVertex3f( HALFTHICK, -HALFTHICK, 1-HALFTHICK );
343   glEnd();
344 }
345
346 static void
347 randsheet_initialize( randsheet *rs )
348 {
349   int i, j, index;
350       index = 0;
351       /* put the moving sheets on the board */
352       for( i = 0; i < BOARDSIZE; i++ )
353         {
354           for( j = 0; j < BOARDSIZE; j++ )
355             {
356               /* initially fill up a corner with the moving squares */
357               if( index < NUMSQUARES )
358                 {
359                   rs->occupied[ i ][ j ] = index;
360                   rs->xpos[ index ] = i;
361                   rs->ypos[ index ] = j;
362                   /* have the square colors start out as a pattern */
363                   rs->color[ index ][ 0 ] = ((i+j)%3 == 0)||((i+j+1)%3 == 0);
364                   rs->color[ index ][ 1 ] = ((i+j+1)%3 == 0);
365                   rs->color[ index ][ 2 ] = ((i+j+2)%3 == 0);
366                  ++index;
367                 }
368                 /* leave everything else empty*/
369               else
370                 {
371                   rs->occupied[ i ][ j ] = -1;
372                 }
373             }
374         }
375         /* initially everything is at rest */
376       for( i=0; i<NUMSQUARES; i++ )
377         {
378           rs->direction[ i ] = 0;
379           rs->angle[ i ] = 0;
380         }
381 }
382
383 /* Pick and random square and direction and try to move it. */
384 /* May not actually move anything, just attempt a random move. */
385 /* Returns true if move was sucessful. */
386 /* This could probably be implemented faster in a dequeue */
387 /* to avoid trying to move a square which is already moving */
388 /* but speed is most likely bottlenecked by rendering anyway... */
389 static int
390 randsheet_new_move( randsheet* rs )
391 {
392       int i, j;
393       int num, dir;
394       /* pick a random square */
395       num = random( ) % NUMSQUARES;
396       i = rs->xpos[ num ];
397       j = rs->ypos[ num ];
398       /* pick a random direction */
399       dir = ( random( )% 4 ) + 1;
400
401       if( rs->direction[ num ] == 0 )
402         {
403           switch( dir )
404             {
405             case 1:
406               /* move up in x */
407               if( ( i + 1 ) < BOARDSIZE )
408                 {
409                   if( rs->occupied[ i + 1 ][ j ] == -1 )
410                     {
411                       rs->direction[ num ] = dir;
412                       rs->occupied[ i + 1 ][ j ] = num;
413                       rs->occupied[ i ][ j ] = -1;
414                       return 1;
415                     }
416                 }
417               return 0;
418               break;
419             case 2:
420               /* move up in y */
421               if( ( j + 1 ) < BOARDSIZE )
422                 {
423                   if( rs->occupied[ i ][ j + 1 ] == -1 )
424                     {
425                       rs->direction[ num ] = dir;
426                       rs->occupied[ i ][ j + 1 ] = num;
427                       rs->occupied[ i ][ j ] = -1;
428                       return 1;
429                     }
430                 }
431               return 0;
432               break;
433             case 3:
434               /* move down in x */
435               if( ( i - 1 ) >= 0 )
436                 {
437                   if( rs->occupied[ i - 1][ j ] == -1 )
438                     {
439                       rs->direction[ num ] = dir;
440                       rs->occupied[ i - 1][ j ] = num;
441                       rs->occupied[ i ][ j ] = -1;
442                       return 1;
443                     }
444                 }
445               return 0;
446               break;
447             case 4:
448               /* move down in y */
449               if( ( j - 1 ) >= 0 )
450                 {
451                   if( rs->occupied[ i ][ j - 1 ] == -1 )
452                     {
453                       rs->direction[ num ] = dir;
454                       rs->occupied[ i ][ j - 1 ] = num;
455                       rs->occupied[ i ][ j ] = -1;
456                       return 1;
457                     }
458                 }
459               return 0;
460               break;
461             default:
462               break;
463             }
464         }
465       return 0;
466 }
467
468 /*   move a single frame.  */
469 /*   Pass in the angle in rads the square rotates in a frame. */
470 static void
471 randsheet_move( randsheet *rs, float rot )
472 {
473       int i, j, index;
474       for( index = 0 ; index < NUMSQUARES; index++ )
475         {
476           i = rs->xpos[ index ];
477           j = rs->ypos[ index ];
478           switch( rs->direction[ index ] )
479             {
480             case 0:
481               /* not moving */
482               break;
483             case 1:
484             /* move up in x */
485               rs->angle[ index ] += rot;
486               /* check to see if we have finished moving */
487               if( rs->angle[ index ] >= M_PI  )
488                 {
489                   rs->xpos[ index ] += 1;
490                   rs->direction[ index ] = 0;
491                   rs->angle[ index ] = 0;
492                 }
493               break;
494             case 2:
495             /* move up in y */
496               rs->angle[ index ] += rot;
497               /* check to see if we have finished moving */
498               if( rs->angle[ index ] >= M_PI  )
499                 {
500                   rs->ypos[ index ] += 1;
501                   rs->direction[ index ] = 0;
502                   rs->angle[ index ] = 0;
503                 }
504               break;
505             case 3:
506             /* down in x */
507               rs->angle[ index ] += rot;
508               /* check to see if we have finished moving */
509               if( rs->angle[ index ] >= M_PI  )
510                 {
511                   rs->xpos[ index ] -= 1;
512                   rs->direction[ index ] = 0;
513                   rs->angle[ index ] = 0;
514                 }
515               break;
516             case 4:
517             /* up in x */
518               rs->angle[ index ] += rot;
519               /* check to see if we have finished moving */
520               if( rs->angle[ index ] >= M_PI  )
521                 {
522                   rs->ypos[ index ] -= 1;
523                   rs->direction[ index ] = 0;
524                   rs->angle[ index ] = 0;
525                 }
526               break;
527             default:
528               break;
529             }
530         }
531 }
532
533
534     /* draw all the moving squares  */
535 static void
536 randsheet_draw( randsheet *rs )
537 {
538       int i, j;
539       int index;
540       /* for all moving squares ... */
541       for( index = 0; index < NUMSQUARES; index++ )
542         {
543           /* set color */
544           glColor3f( rs->color[ index ][ 0 ],
545                      rs->color[ index ][ 1 ],
546                      rs->color[ index ][ 2 ] );
547                      /* find x and y position */
548           i = rs->xpos[ index ];
549           j = rs->ypos[ index ];
550           glPushMatrix();
551           switch( rs->direction[ index ] )
552             {
553             case 0:
554
555             /* not moving */
556               /* front */
557               glTranslatef( i, 0, j );
558               break;
559             case 1:
560               glTranslatef( i+1, 0, j );
561               glRotatef( 180 - rs->angle[ index ]*180/M_PI, 0, 0, 1 );
562
563               break;
564             case 2:
565               glTranslatef( i, 0, j+1 );
566               glRotatef( 180 - rs->angle[ index ]*180/M_PI, -1, 0, 0 );
567
568               break;
569             case 3:
570               glTranslatef( i, 0, j );
571               glRotatef( rs->angle[ index ]*180/M_PI, 0, 0, 1 );
572               break;
573             case 4:
574               glTranslatef( i, 0, j );
575               glRotatef( rs->angle[ index ]*180/M_PI, -1, 0, 0 );
576               break;
577             default:
578               break;
579             }
580           draw_sheet();
581           glPopMatrix();
582
583         }
584 }
585
586 /**** END RANDSHEET FUNCTIONS ***/
587
588 #endif