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