http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / topblock.c
1 /* topblock, Copyright (c) 2006-2008
2  *  rednuht <topblock.xscreensaver@jumpstation.co.uk>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  *
12  * 
13  *
14
15 topBlock - a simple openGL 3D hack of falling blocks
16 based on jwz's dangerball hack
17
18 The proporations of the blocks and their features is not even close to the commercial building block products offered by a variety companies.
19
20 information on this hack might be found at 
21 http://www.jumpstation.co.uk/xscreensaver/topblock/
22
23 History
24 25/02/2006 v1.0 release
25 29/04/2006 v1.11 updated to better fit with xscreensaver v5
26                  colors defaults to 7 (no black)
27 19/06/2006 v1.2  fixed dropSpeed = 7 bug, added gltrackball support and some code neatening, thanks to Valdis Kletnieks and JWZ for their input.
28 */
29
30 #include <math.h>
31
32 # define refresh_topBlock 0
33
34 #define DEFAULTS        "*delay:        10000       \n" \
35                         "*count:        30           \n" \
36                         "*showFPS:      False       \n" \
37                         "*wireframe:    False       \n" \
38
39 #undef countof
40 #define countof(x) (sizeof((x))/sizeof((*x)))
41
42 #include "xlockmore.h"
43 #include "topblock.h"
44 #include "sphere.h"
45 #include "gltrackball.h"
46 #include <ctype.h>
47
48 #ifdef USE_GL /* whole file */
49
50 #ifdef HAVE_COCOA
51 # include <OpenGL/glu.h>
52 #else
53 # include <GL/glu.h>
54 #endif
55
56 typedef struct
57 {
58   GLXContext *glx_context;
59   trackball_state *trackball;
60   Bool button_down_p;
61   int numFallingBlocks;
62   GLfloat highest,highestFalling;
63   GLfloat eyeLine,eyeX,eyeY,eyeZ;
64   int carpetWidth, carpetLength;
65   int followMode;
66   GLfloat followRadius,followAngle;
67   int plusheight;
68   GLuint        carpet;
69   GLuint        block;
70   NODE *blockNodeRoot;
71   NODE *blockNodeFollow;
72   GLfloat rotation;
73 } topBlockSTATE;
74
75 /* parameter vars */
76 Bool override;
77 Bool rotate;
78 Bool follow;
79 Bool drawCarpet;
80 Bool drawBlob;
81 Bool drawNipples;
82 GLfloat rotateSpeed;
83 GLfloat camX;
84 GLfloat camY;
85 GLfloat camZ;
86 GLfloat dropSpeed;
87 int maxFalling;
88 int maxColors;
89 int size;
90 int spawn;
91 int resolution;
92
93 static XrmOptionDescRec opts[] = {
94   { "-size",        ".size",        XrmoptionSepArg, 0 },
95   { "-spawn",       ".spawn",       XrmoptionSepArg, 0 },
96   { "-camX",        ".camX",        XrmoptionSepArg, 0 },
97   { "-camY",        ".camY",        XrmoptionSepArg, 0 },
98   { "-camZ",        ".camZ",        XrmoptionSepArg, 0 },
99   { "+rotate",      ".rotate",      XrmoptionNoArg, "False" },
100   { "-rotate",      ".rotate",      XrmoptionNoArg, "True" },
101   { "+carpet",      ".carpet",      XrmoptionNoArg, "False" },
102   { "+nipples",     ".nipples",     XrmoptionNoArg, "False" },
103   { "-blob",        ".blob",        XrmoptionNoArg, "True" },
104   { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
105   { "-follow",      ".follow",      XrmoptionNoArg, "True" },
106   { "-maxFalling",  ".maxFalling",  XrmoptionSepArg, 0 },
107   { "-resolution",  ".resolution",  XrmoptionSepArg, 0 },
108   { "-maxColors",   ".maxColors",   XrmoptionSepArg, 0 },
109   { "-dropSpeed",   ".dropSpeed",   XrmoptionSepArg, 0 },
110   { "-override",    ".override",    XrmoptionNoArg, "True" },
111 };
112
113 #define DEF_override      "False"
114 #define DEF_rotate        "True"
115 #define DEF_follow        "False"
116 #define DEF_drawCarpet    "True"
117 #define DEF_drawBlob      "False"
118 #define DEF_drawNipples   "True"
119 #define DEF_rotateSpeed   "10"
120 #define DEF_maxFalling    "500"
121 #define DEF_maxColors     "7"
122 #define DEF_size          "2"
123 #define DEF_spawn         "50"
124 #define DEF_resolution    "4"
125 #define DEF_camX          "1"
126 #define DEF_camY          "20"
127 #define DEF_camZ          "25"
128 #define DEF_dropSpeed     "4"
129
130 static argtype vars[] = {
131   {&override,     "override",     "Override",     DEF_override,     t_Bool},
132   {&rotate,       "rotate",       "Rotate",       DEF_rotate,       t_Bool},
133   {&drawCarpet,   "carpet",       "Carpet",       DEF_drawCarpet,   t_Bool},
134   {&drawNipples,  "nipples",      "Nipples",      DEF_drawNipples,  t_Bool},
135   {&drawBlob,     "blob",         "Blob",         DEF_drawBlob,     t_Bool},
136   {&rotateSpeed,  "rotateSpeed",  "RotateSpeed",  DEF_rotateSpeed,  t_Float},
137   {&follow,       "follow",       "Follow",       DEF_follow,       t_Bool},
138   {&camX,         "camX",         "camX",         DEF_camX,         t_Float},
139   {&camY,         "camY",         "camY",         DEF_camY,         t_Float},
140   {&camZ,         "camZ",         "camZ",         DEF_camZ,         t_Float},
141   {&size,         "size",         "size",         DEF_size,         t_Int},
142   {&spawn,        "spawn",        "spawn",        DEF_spawn,        t_Int},
143   {&maxFalling,   "maxFalling",   "maxFalling",   DEF_maxFalling,   t_Int},
144   {&resolution,   "resolution",   "resolution",   DEF_resolution,   t_Int},
145   {&maxColors,    "maxColors",    "maxColors",    DEF_maxColors,    t_Int},
146   {&dropSpeed,    "dropSpeed",    "DropSpeed",    DEF_dropSpeed,    t_Float},
147 };
148
149 static topBlockSTATE *tbs = NULL;
150
151 ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
152
153 /* Window management, etc */
154 ENTRYPOINT void
155 reshape_topBlock (ModeInfo *mi, int width, int height) {
156   GLfloat h = (GLfloat) height / (GLfloat) width;
157   glViewport (0, 0, (GLint) width, (GLint) height);
158   glMatrixMode(GL_PROJECTION);
159   glLoadIdentity();
160   gluPerspective (60.0, 1/h, 1.0, 1000.0);
161   glMatrixMode(GL_MODELVIEW);
162   glLoadIdentity();
163   glClear(GL_COLOR_BUFFER_BIT);
164 }
165
166 /* clean up on exit, not required ... */
167 ENTRYPOINT void
168   release_topBlock(ModeInfo *mi) {
169   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
170         NODE *llCurrent, *llOld;
171         llCurrent = tb->blockNodeRoot;
172         while (llCurrent != NULL) {
173                 llOld = llCurrent;
174                 llCurrent = llCurrent->next;
175                 free(llOld);
176         }
177 }
178
179 /* setup */
180 ENTRYPOINT void 
181 init_topBlock (ModeInfo *mi)
182 {
183   topBlockSTATE *tb;
184   int wire = MI_IS_WIREFRAME(mi);
185
186   if (!tbs) {
187     tbs = (topBlockSTATE *)
188       calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
189     if (!tbs) {
190       fprintf(stderr, "%s: out of memory\n", progname);
191       exit(1);
192     }
193   }
194
195   tb = &tbs[MI_SCREEN(mi)];
196
197   tb->glx_context = init_GL(mi);
198
199   reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
200
201         if (wire) { drawNipples=False; }
202   tb->numFallingBlocks=0;
203
204         if (size>10) { size = 10; }
205         if (size<1) { size = 2; }
206         tb->carpetWidth = 8 * size;
207         tb->carpetLength = tb->carpetWidth;
208   
209   maxFalling*=size;
210
211         if (spawn<4) { spawn=4; }
212         if (spawn>1000) { spawn=1000; }
213
214         if (rotateSpeed<1) {rotateSpeed=1; }
215         if (rotateSpeed>1000) {rotateSpeed=1000;}
216   rotateSpeed /= 100;
217
218         if (resolution<4) {resolution=4;}
219         if (resolution>20) {resolution=20;}
220   resolution*=2;
221
222         if (maxColors<1) {maxColors=1;}
223         if (maxColors>8) {maxColors=8;}
224
225         if (dropSpeed<1) {dropSpeed=1;}
226         if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
227   
228         dropSpeed = 80/dropSpeed;
229         dropSpeed = (blockHeight/dropSpeed); 
230
231   tb->glx_context = init_GL(mi);
232
233   reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
234   if (0==1) {
235       glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
236   } else {
237       glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
238   }
239   glClearDepth(1.0f);
240   if (!wire) {
241     GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
242     GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
243     GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
244     GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
245
246     glEnable(GL_LIGHTING);
247     glEnable(GL_LIGHT0);
248     glLightfv(GL_LIGHT0, GL_POSITION, pos);
249     glLightfv(GL_LIGHT0, GL_AMBIENT,  amb); 
250     glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
251     glLightfv(GL_LIGHT0, GL_SPECULAR, spc); 
252   }
253         glDepthFunc(GL_LEQUAL);
254   glEnable(GL_DEPTH_TEST);
255   glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
256   glCullFace(GL_BACK); 
257
258         if (drawBlob) {
259     buildBlobBlock(mi); 
260         } else {
261           buildBlock(mi);               /* build the display list holding the simple block */
262         }
263   buildCarpet(mi);              /* build the base */
264         tb->highest=0;
265         tb->highestFalling=0;
266         tb->eyeLine=tb->highest;
267         tb->eyeX=0;
268         tb->eyeY=0;
269         tb->eyeZ=0;
270         tb->followMode=0;
271   if (follow) {
272     tb->plusheight=100;
273     camZ=camZ-60;
274   } else {
275     tb->rotation=random() % 360;
276     tb->eyeY=10;
277     tb->plusheight=30;
278   }
279         tb->followRadius=0;
280   /* override camera settings */
281   if (override) {
282     tb->plusheight=100;
283     drawCarpet=False;
284     camX=0;
285     camY=1;
286     camZ=0;
287     tb->eyeX=-1;
288     tb->eyeY=20;
289     tb->eyeZ=0;
290   }
291   tb->trackball = gltrackball_init ();
292 }
293
294 /* provides the per frame entertainment */
295 ENTRYPOINT void
296 draw_topBlock (ModeInfo *mi) {
297         Display *dpy = MI_DISPLAY(mi);
298         Window window = MI_WINDOW(mi);
299         NODE *llCurrent;
300         NODE *llNode;
301         topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
302         GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
303         GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
304         int wire = MI_IS_WIREFRAME(mi);
305           GLfloat color[4];     
306
307   if (!tb->glx_context)
308     return;
309   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
310
311         generateNewBlock(mi);
312
313         if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; } 
314         if (tb->rotation>=360) { tb->rotation=tb->rotation-360; } 
315
316         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);             /* clear current */
317         glLoadIdentity();       /* resets directions, do it every time ! */
318         if (!follow) {
319                 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100);    } /* creates a smooth camera transition */
320                 gluLookAt(camX, camY+tb->eyeLine, camZ, tb->eyeX, tb->eyeY+tb->eyeLine, tb->eyeZ, 0.0, 1.0, 0.0);               /* setup viewer, xyz cam, xyz looking at and where is up normaly 0,1,0 */
321                 glRotatef(90, 1.0, 0.0, 0.0);                   /* x axis */
322         } else {
323                 glRotatef(90, 0.0, 0.0, 1.0);     /* z axis */
324                 followBlock(mi);
325         }
326         glRotatef(-90, 1.0, 0.0, 0.0);          
327   gltrackball_rotate (tb->trackball);
328         glRotatef(90, 1.0, 0.0, 0.0);           
329
330         /* rotate the world */
331         glRotatef(tb->rotation, 0.0, 0.0, 1.0);         
332
333         llCurrent = tb->blockNodeRoot;
334         if (drawCarpet) {
335                 /* center carpet */
336                 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
337                 glCallList(tb->carpet);
338                 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
339                 glTranslatef(0.0,0.0,-0.55);
340         }
341         tb->highestFalling=0;
342         while (llCurrent != NULL) {     /* for each block */
343                 glPushMatrix(); /* save state */
344                 /* set color */
345                 switch (llCurrent->color) { 
346                         case 0:
347                                 color[0] = 1.0f;        
348                                 color[1] = 0.0f;        
349                                 color[2] = 0.0f;        
350                                 color[3] = 1.0f;        
351                                 break;
352                         case 1:
353                                 color[0] = 0.0f;        
354                                 color[1] = 1.0f;        
355                                 color[2] = 0.0f;        
356                                 color[3] = 1.0f;        
357                                 break;
358                         case 2:
359                                 color[0] = 0.0f;        
360                                 color[1] = 0.0f;        
361                                 color[2] = 1.0f;        
362                                 color[3] = 1.0f;        
363                                 break;
364                         case 3:
365                                 color[0] = 0.95f;       
366                                 color[1] = 0.95f;       
367                                 color[2] = 0.95f;       
368                                 color[3] = 1.0f;        
369                                 break;
370                         case 4:
371                                 color[0] = 1.0f;        
372                                 color[1] = 0.5f;        
373                                 color[2] = 0.0f;        
374                                 color[3] = 1.0f;        
375                                 break;
376                         case 5:
377                                 color[0] = 1.0f;        
378                                 color[1] = 1.0f;        
379                                 color[2] = 0.0f;        
380                                 color[3] = 1.0f;        
381                                 break;
382                         case 6: 
383                                 color[0] = 0.5f;        
384                                 color[1] = 0.5f;        
385                                 color[2] = 0.5f;        
386                                 color[3] = 1.0f;        
387                                 break;
388                         case 7:
389                                 color[0] = 0.05f;       
390                                 color[1] = 0.05f;       
391                                 color[2] = 0.05f;       
392                                 color[3] = 1.0f;        
393                                 break;
394                 } 
395                 if (wire) { glColor3fv(color); }
396                 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
397
398                 if (llCurrent->falling==1) {
399         spcC2x = 0;
400             spcC2y = 0;
401             spcN2x = 0;
402             spcN2y = 0;
403                         if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
404                         /* all blocks fall at the same rate to avoid mid air collisions */
405                         llCurrent->height-=dropSpeed;
406                         if (llCurrent->height<=0) {
407                                 llCurrent->falling=0;
408                                 if (tb->highest==0) { 
409                                         tb->highest+=blockHeight; 
410                                 }
411                         } 
412                         if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
413                                 /* check for collision */
414                                 llNode = tb->blockNodeRoot;
415                                 spcC1x = llCurrent->x;
416                                 spcC1y = llCurrent->y;
417                                 switch(llCurrent->rotation) {
418                                         case getOrientation(0):
419                                                 spcC2x = spcC1x;
420                                                 spcC2y = spcC1y-2;
421                                                 break;
422                                         case getOrientation(1):
423                                                 spcC2x = spcC1x+2;
424                                                 spcC2y = spcC1y;
425                                                 break;
426                                         case getOrientation(2):
427                                                 spcC2x = spcC1x;
428                                                 spcC2y = spcC1y+2;
429                                                 break;
430                                         case getOrientation(3):
431                                                 spcC2x = spcC1x-2;
432                                                 spcC2y = spcC1y;
433                                                 break;
434                                 }
435                                 while (llNode != NULL) {
436                                         if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
437                                                 spcN1x = llNode->x;
438                                                 spcN1y = llNode->y;
439                                                 switch(llNode->rotation) {
440                                                         case getOrientation(0):
441                                                                 spcN2x = spcN1x;
442                                                                 spcN2y = spcN1y-2;
443                                                                 break;
444                                                         case getOrientation(1):
445                                                                 spcN2x = spcN1x+2;
446                                                                 spcN2y = spcN1y;
447                                                                 break;
448                                                         case getOrientation(2):
449                                                                 spcN2x = spcN1x;
450                                                                 spcN2y = spcN1y+2;
451                                                                 break;
452                                                         case getOrientation(3):
453                                                                 spcN2x = spcN1x-2;
454                                                                 spcN2y = spcN1y;
455                                                                 break;
456                                                 }
457                                                 if ( 
458                                                         ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
459                                                         ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
460                                                         ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
461                                                         ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
462                                                 ){
463               if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) { 
464
465                                                     llCurrent->falling=0;
466                                                           llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
467                 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
468                  tb->highest+=blockHeight; 
469                                                           }
470                                                   }
471                                           }
472                                   }
473                                   llNode=llNode->next;
474                           }                             
475                   }
476           } 
477         /* set location in space */
478           glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
479           /* rotate */
480         glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
481         if ((tb->followMode==0) && (llCurrent->next==NULL)) {
482                 tb->blockNodeFollow = llCurrent;
483                 tb->followMode=1;
484         } 
485         llCurrent = llCurrent->next;
486         /* draw   */
487         glCallList(tb->block); 
488         glPopMatrix();  /* restore state */
489   } 
490   if (mi->fps_p) do_fps (mi);
491   glFinish();
492
493         if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
494   glXSwapBuffers(dpy, window);
495 }
496
497
498
499 /* camera is in follow mode, work out where we should be looking */
500 static void followBlock(ModeInfo *mi) {
501         GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
502   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
503   cx=0;cy=0;
504         if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
505
506                 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100);   } 
507                   /*tb->blockNodeFollow->color=1;  only noticable if you set the colors to 1 */
508                 
509                         if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); } 
510                         if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); } 
511                 
512
513                 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
514                    (see http://www.jumpstation.co.uk/rotation/)
515                 */
516
517                 if (tb->followRadius==0) {              
518                         xLen = tb->blockNodeFollow->x-cx;
519                         yLen = tb->blockNodeFollow->y-cy;
520                         tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );     
521                         tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius); 
522                         tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
523                 }
524                 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
525                 xTarget = cos(rangle) * tb->followRadius + cx;
526                 yTarget = sin(rangle) * tb->followRadius + cy;
527                 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
528
529                 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
530                 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
531
532                 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
533                 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
534                 if (!tb->blockNodeFollow->falling) {  
535                         tb->followMode=0; 
536                         /*tb->blockNodeFollow->color=2;  only noticable if you set the colors to 1 */
537                         tb->followRadius=0;
538                 } 
539         }
540         gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
541 }
542
543 /* each quater of the circle has to be adjusted for */
544 static double quadrantCorrection(double angle,int cx,int cy,int x,int y) {
545         if ((x>=cx) && (y>=cy)) {
546                 angle +=  (90-(angle-90) * 2); 
547         } else if ((x>=cx) && (y<=cy)) {
548                 angle +=  90; 
549         } else if ((x<=cx) && (y<=cy)) {
550                 angle +=  90; 
551         } else if ((x<=cx) && (y>=cy)) {
552                 angle += (90-(angle-90) * 2); 
553         }
554         return(angle-180);
555 }
556
557 /* if random chance then create a new falling block */
558 static void generateNewBlock(ModeInfo *mi) {
559         NODE *llCurrent, *llTail;
560         GLfloat startOffx, startOffy;
561         int endOffx, endOffy;
562   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
563         if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {  
564                 startOffx=0;
565                 endOffx=0;
566                 startOffy=0;
567                 endOffy=0;
568                 tb->numFallingBlocks++;
569                 llTail = tb->blockNodeRoot; 
570                 if (llTail == NULL) {
571                         if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) {     fprintf(stderr, "%s: out of memory.\n", progname); }
572                         llTail = llCurrent;
573                         tb->blockNodeRoot = llCurrent; 
574                 } else {
575                         if (tb->numFallingBlocks>=maxFalling) {
576                                 /* recycle */
577                                 llCurrent=llTail->next;
578                                 tb->blockNodeRoot=llCurrent->next;
579                         } else {
580                                 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) {     fprintf(stderr, "%s: out of memory..\n", progname); }
581                         }
582                         while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
583                 }
584                 llCurrent->falling=1;
585                 llCurrent->rotation=getOrientation(random() % 4); 
586                 if (llCurrent->rotation==getOrientation(0)) {
587                         startOffx=1.0;
588                         endOffx=0;
589                         startOffy=3.0;
590                         endOffy=-1;
591                 } else if (llCurrent->rotation==getOrientation(1)) {
592                         startOffx=1.0;
593                         endOffx=-1;
594                         startOffy=1.0;
595                         endOffy=0;              
596                 } else if (llCurrent->rotation==getOrientation(2)) {
597                         startOffx=1.0;
598                         endOffx=0;
599                         startOffy=3.0;
600                         endOffy=-1;             
601                 } else if (llCurrent->rotation==getOrientation(3)) { 
602                         startOffx=5.0;
603                         endOffx=-1;
604                         startOffy=1.0;
605                         endOffy=0;              
606                 }
607
608                 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
609                 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
610                 llCurrent->color=(random() % maxColors);
611                 llCurrent->height=getHeight(tb->plusheight+tb->highest); 
612                 if (tb->numFallingBlocks>=maxFalling) {
613                         tb->numFallingBlocks--;
614                         tb->numFallingBlocks--;
615                 } 
616                 llTail->next = llCurrent;
617                 llTail = llCurrent;
618                 llTail->next = NULL;
619
620         }
621 }
622
623 /* called at init this creates the 'carpet' display list item */
624 static void buildCarpet(ModeInfo *mi) {
625         int i,c,x,y;
626         GLfloat color[4];
627         int wire = MI_IS_WIREFRAME(mi);
628         topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
629         GLUquadricObj *quadratic;
630                 color[0] = 0.0f;        
631                 color[1] = 1.0f;        
632                 color[2] = 0.0f;        
633                 color[3] = 1.0f;        
634         tb->carpet=glGenLists(1);       /* only one */
635         glNewList(tb->carpet,GL_COMPILE);
636         glPushMatrix(); /* save state */
637         x=tb->carpetWidth;
638         y=tb->carpetLength;
639         if (wire) { glColor3fv(color); }
640         else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
641         /* draw carpet plane */
642         glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
643                 /* draw top */
644                 glNormal3f( 0, 0, -1 );
645                 glVertex3f(0.0,0.0,0.0);
646                 glVertex3f(x,0.0,0.0);
647                 glVertex3f(x,y,0.0);
648                 glVertex3f(0.0,y,0.0);
649             if (wire) { glEnd(); } 
650                 else {
651                 /* add edge pieces */
652                 /* side 1 */
653                 glNormal3f( 0, -1, 0 );
654                 glVertex3f(0.0,0.0,0.0);
655                 glVertex3f(x,0.0,0.0);
656                 glVertex3f(x,0,singleThick);
657                 glVertex3f(0.0,0,singleThick);
658                 /* side 2 */
659                 glNormal3f( -1, 0, 0 );
660                 glVertex3f(0.0,0.0,0.0);
661                 glVertex3f(0,y,0.0);
662                 glVertex3f(0,y,singleThick);
663                 glVertex3f(0.0,0,singleThick);
664                 /* side 3 */
665                 glNormal3f( 1, 0, 0 );
666                 glVertex3f(x,0.0,0.0);
667                 glVertex3f(x,y,0.0);
668                 glVertex3f(x,y,singleThick);
669                 glVertex3f(x,0,singleThick);
670                 /* side 4 */
671                 glNormal3f( 0, 1, 0 );
672                 glVertex3f(0,y,0.0);
673                 glVertex3f(x,y,0.0);
674                 glVertex3f(x,y,singleThick);
675                 glVertex3f(0,y,singleThick);
676                 }
677         glEnd();
678         /* nipples */
679         if (drawNipples) {
680                 quadratic=gluNewQuadric();                      /* Create A Pointer To The Quadric Object */
681                 gluQuadricNormals(quadratic, GLU_SMOOTH);       /* Create Smooth Normals  */
682                 gluQuadricTexture(quadratic, GL_TRUE);          /* Create Texture Coords  */
683                 glTranslatef(0.5f,0.5f,-.25);                   /* move to the cylinder center */
684                 for (c=0;c<x;c++) {
685                         glPushMatrix(); /* save state */
686                         for (i=0;i<y;i++) {
687                                 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution);        /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
688                                 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
689                                 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution );      /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular  */
690                                 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
691                                 glTranslatef(0.0f,1.0f,0.0f);                   /* move to the next cylinder center (backward) */
692                         }
693                         glPopMatrix();  /* save state */
694                         glTranslatef(1.0f,0.0f,0.0f);                   /* reset   */
695                 }
696         }
697         glPopMatrix();  /* restore state */
698         glEndList();    
699 }
700
701 /* using the verticies arrays builds the plane, now with normals */
702 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
703 {
704         GLfloat topBlockNormals[5][3] = { {0,0,-1},     {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
705         GLfloat topBlockVertices[8][3] = { {-0.49,-2.97,-0.99}, {0.99,-2.97,-0.99}, {0.99,0.99,-0.99}  , {-0.49,0.99,-0.99}, {-0.49,-2.97,0.99} , {0.99,-2.97,0.99}, {0.99,0.99,0.99}   , {-0.49,0.99,0.99} };
706         glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
707                 glNormal3fv(topBlockNormals[i] );
708                 glVertex3fv(topBlockVertices[a]);
709                 glVertex3fv(topBlockVertices[b]);
710                 glVertex3fv(topBlockVertices[c]);
711                 glVertex3fv(topBlockVertices[d]);
712         glEnd();
713 }
714
715 /* called at init this creates the 'block' display list item */
716 /* the spheres came about originaly as quick way to test the directional lighting/normals */
717 static void buildBlock(ModeInfo *mi) {
718         int i,c;
719   int wire = MI_IS_WIREFRAME(mi);
720   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
721         GLUquadricObj *quadratic;
722         tb->block=glGenLists(1);        /* only one */
723         glNewList(tb->block,GL_COMPILE);
724         glPushMatrix(); /* save state */
725         glRotatef(90, 0.0f, 1.0f, 0.0f);
726         /* base */
727         polygonPlane(wire, 0,3,2,1,0);
728         polygonPlane(wire, 2,3,7,6,1);
729         polygonPlane(wire, 1,2,6,5,2); 
730         polygonPlane(wire, 4,5,6,7,3); 
731         polygonPlane(wire, 0,1,5,4,4);
732         if (drawNipples) {
733                 /* nipples */
734                 /* draw 8 cylinders each with a disk cap */
735                 quadratic=gluNewQuadric();                      /* Create A Pointer To The Quadric Object  */
736                 gluQuadricNormals(quadratic, GLU_SMOOTH);       /* Create Smooth Normals  */
737                 glRotatef(90, 0.0f, 1.0f, 0.0f);                /* 'aim' the pointer ready for the cylinder */
738                 glTranslatef(0.5f,0.5f,0.99f);                  /* move to the cylinder center */
739                 for (c=0;c<2;c++) {
740                         for (i=0;i<4;i++) {
741                                 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution);        /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
742                                 glTranslatef(0.0f,0.0f,0.25f);                  /* move to the cylinder cap  */
743                                 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution );      /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular  */
744                                 glTranslatef(0.0f,0.0f,-0.25f);                 /* move back from the cylinder cap  */
745                                 if (c==0) {     
746                                         glTranslatef(0.0f,-1.0f,0.0f);                  /* move to the next cylinder center (forward) */
747                                 } else {
748                                         glTranslatef(0.0f,1.0f,0.0f);                   /* move to the next cylinder center (backward) */
749                                 }
750                         }
751                         glTranslatef(-1.0f,1.0f,0.0f);                  /* move to the cylinder center */
752                 }
753                 /* udders */
754                 /* 3 cylinders on the underside */
755                 glTranslatef(1.5f,-2.5f,-1.5f);         /* move to the center, under the top of the brick        */
756                 for (c=0;c<3;c++) {
757                         gluCylinder(quadratic, uddSize, uddSize, 1.5f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
758                         glTranslatef(0.0f,-1.0f,0.0f);          /* move to the center */        
759                 }
760         }
761         glPopMatrix();  /* restore state */
762         glEndList();    
763 }
764
765 /* 
766         rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
767         spheres are created with unit_sphere from spheres.h to allow wire frame 
768 */
769 static void buildBlobBlock(ModeInfo *mi) {
770   int wire = MI_IS_WIREFRAME(mi);
771   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
772         tb->block=glGenLists(1);        /* only one */
773         glNewList(tb->block,GL_COMPILE);
774         glPushMatrix();
775   glScalef(1.4,1.4,1.4);
776   unit_sphere (resolution/2,resolution, wire);
777   glPopMatrix();
778   glTranslatef(0.0f,-2.0f,0.0f);
779   glScalef(1.4,1.4,1.4);
780   unit_sphere (resolution/2,resolution, wire);
781         glEndList();    
782 }
783
784
785 /* handle input events or not if daemon running the show */
786 ENTRYPOINT Bool 
787 topBlock_handle_event (ModeInfo *mi, XEvent *event) {
788   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
789         if (event->xany.type == KeyPress)    {
790     KeySym keysym;
791     char c = 0;
792     XLookupString (&event->xkey, &c, 1, &keysym, 0);
793     if (c == 'a') {
794                         tb->eyeX++;
795                         return True;
796                 } else if (c == 'z') {
797                         tb->eyeX--;
798                         return True;
799                 } else if (c == 's') {
800                         tb->eyeY--;
801                         return True;
802                 } else if (c == 'x') {
803                         tb->eyeY++;
804                         return True;
805                 } else if (c == 'd') {
806                         tb->eyeZ++;
807                         return True;
808                 } else if (c == 'c') {
809                         tb->eyeZ--;
810                         return True;
811                 } else if (c == 'f') {
812                         camX++;
813                         return True;
814                 } else if (c == 'v') {
815                         camX--;
816                         return True;
817                 } else if (c == 'g') {
818                         camY++;
819                         return True;
820                 } else if (c == 'b') {
821                         camY--;
822                         return True;
823                 } else if (c == 'h') {
824                         camZ++;
825                         return True;
826                 } else if (c == 'n') {
827                         camZ--;
828                         return True;
829                 } else if (c == 'r') {
830                         tb->rotation++;
831                         return True;
832                 }
833         }
834   if (event->xany.type == ButtonPress &&
835       event->xbutton.button == Button1)
836     {
837       tb->button_down_p = True;
838       gltrackball_start (tb->trackball,
839                          event->xbutton.x, event->xbutton.y,
840                          MI_WIDTH (mi), MI_HEIGHT (mi));
841       return True;
842     }
843   else if (event->xany.type == ButtonRelease &&
844            event->xbutton.button == Button1)
845     {
846       tb->button_down_p = False;
847       return True;
848     }
849   else if (event->xany.type == ButtonPress &&
850            (event->xbutton.button == Button4 ||
851             event->xbutton.button == Button5 ||
852             event->xbutton.button == Button6 ||
853             event->xbutton.button == Button7))
854     {
855       gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
856                               !!event->xbutton.state);
857       return True;
858     }
859   else if (event->xany.type == MotionNotify &&
860            tb->button_down_p)
861     {
862       gltrackball_track (tb->trackball,
863                          event->xmotion.x, event->xmotion.y,
864                          MI_WIDTH (mi), MI_HEIGHT (mi));
865       return True;
866     }
867         return False;
868 }
869
870 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
871 XSCREENSAVER_MODULE_2 ("topBlock", topblock, topBlock)
872
873 #endif /* USE_GL */