From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / topblock.c
1 /* topblock, Copyright (c) 2006-2012 rednuht <topblock.xscreensaver@jumpstation.co.uk>
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  *
13
14 topBlock - a simple openGL 3D hack of falling blocks
15 based on jwz's dangerball hack
16
17 The proporations of the blocks and their features is not even close to the commercial building block products offered by a variety companies.
18
19 information on this hack might be found at 
20 http://www.jumpstation.co.uk/xscreensaver/topblock/
21
22 History
23 25/02/2006 v1.0 release
24 29/04/2006 v1.11 updated to better fit with xscreensaver v5
25                  colors defaults to 7 (no black)
26 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.
27 */
28
29 #include <math.h>
30
31 # define refresh_topBlock 0
32
33 #define DEFAULTS        "*delay:        10000       \n" \
34                         "*count:        30           \n" \
35                         "*showFPS:      False       \n" \
36                         "*wireframe:    False       \n" \
37
38 #undef countof
39 #define countof(x) (sizeof((x))/sizeof((*x)))
40
41 #include "xlockmore.h"
42 #include "topblock.h"
43 #include "sphere.h"
44 #include "tube.h"
45 #include "gltrackball.h"
46 #include <ctype.h>
47
48 #ifdef USE_GL /* whole file */
49
50 #ifndef HAVE_COCOA
51 # include <GL/glu.h>
52 #endif
53
54 typedef struct
55 {
56   GLXContext *glx_context;
57   trackball_state *trackball;
58   Bool button_down_p;
59   int numFallingBlocks;
60   GLfloat highest,highestFalling;
61   GLfloat eyeLine,eyeX,eyeY,eyeZ;
62   int carpetWidth, carpetLength;
63   int followMode;
64   GLfloat followRadius,followAngle;
65   int plusheight;
66   GLuint        carpet;
67   GLuint        block;
68   int carpet_polys, block_polys;
69   NODE *blockNodeRoot;
70   NODE *blockNodeFollow;
71   GLfloat rotation;
72 } topBlockSTATE;
73
74 /* parameter vars */
75 static Bool override;
76 static Bool rotate;
77 static Bool follow;
78 static Bool drawCarpet;
79 static Bool drawBlob;
80 static Bool drawNipples;
81 static GLfloat rotateSpeed;
82 static GLfloat camX;
83 static GLfloat camY;
84 static GLfloat camZ;
85 static GLfloat dropSpeed;
86 static int maxFalling;
87 static int maxColors;
88 static int size;
89 static int spawn;
90 static int resolution;
91
92 static XrmOptionDescRec opts[] = {
93   { "-size",        ".size",        XrmoptionSepArg, 0 },
94   { "-spawn",       ".spawn",       XrmoptionSepArg, 0 },
95   { "-camX",        ".camX",        XrmoptionSepArg, 0 },
96   { "-camY",        ".camY",        XrmoptionSepArg, 0 },
97   { "-camZ",        ".camZ",        XrmoptionSepArg, 0 },
98   { "+rotate",      ".rotate",      XrmoptionNoArg, "False" },
99   { "-rotate",      ".rotate",      XrmoptionNoArg, "True" },
100   { "+carpet",      ".carpet",      XrmoptionNoArg, "False" },
101   { "+nipples",     ".nipples",     XrmoptionNoArg, "False" },
102   { "-blob",        ".blob",        XrmoptionNoArg, "True" },
103   { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
104   { "-follow",      ".follow",      XrmoptionNoArg, "True" },
105   { "-maxFalling",  ".maxFalling",  XrmoptionSepArg, 0 },
106   { "-resolution",  ".resolution",  XrmoptionSepArg, 0 },
107   { "-maxColors",   ".maxColors",   XrmoptionSepArg, 0 },
108   { "-dropSpeed",   ".dropSpeed",   XrmoptionSepArg, 0 },
109   { "-override",    ".override",    XrmoptionNoArg, "True" },
110 };
111
112 #define DEF_OVERRIDE      "False"
113 #define DEF_ROTATE        "True"
114 #define DEF_FOLLOW        "False"
115 #define DEF_CARPET   "True"
116 #define DEF_BLOB     "False"
117 #define DEF_NIPPLES  "True"
118 #define DEF_ROTATE_SPEED  "10"
119 #define DEF_MAX_FALLING   "500"
120 #define DEF_MAX_COLORS    "7"
121 #define DEF_SIZE          "2"
122 #define DEF_SPAWN         "50"
123 #define DEF_RESOLUTION    "4"
124 #define DEF_CAM_X         "1"
125 #define DEF_CAM_Y         "20"
126 #define DEF_CAM_Z         "25"
127 #define DEF_DROP_SPEED    "4"
128
129 static argtype vars[] = {
130   {&override,     "override",     "Override",     DEF_OVERRIDE,     t_Bool},
131   {&rotate,       "rotate",       "Rotate",       DEF_ROTATE,       t_Bool},
132   {&drawCarpet,   "carpet",       "Carpet",       DEF_CARPET,   t_Bool},
133   {&drawNipples,  "nipples",      "Nipples",      DEF_NIPPLES,  t_Bool},
134   {&drawBlob,     "blob",         "Blob",         DEF_BLOB,     t_Bool},
135   {&rotateSpeed,  "rotateSpeed",  "RotateSpeed",  DEF_ROTATE_SPEED,  t_Float},
136   {&follow,       "follow",       "Follow",       DEF_FOLLOW,       t_Bool},
137   {&camX,         "camX",         "camX",         DEF_CAM_X,         t_Float},
138   {&camY,         "camY",         "camY",         DEF_CAM_Y,         t_Float},
139   {&camZ,         "camZ",         "camZ",         DEF_CAM_Z,         t_Float},
140   {&size,         "size",         "size",         DEF_SIZE,         t_Int},
141   {&spawn,        "spawn",        "spawn",        DEF_SPAWN,        t_Int},
142   {&maxFalling,   "maxFalling",   "maxFalling",   DEF_MAX_FALLING,   t_Int},
143   {&resolution,   "resolution",   "resolution",   DEF_RESOLUTION,   t_Int},
144   {&maxColors,    "maxColors",    "maxColors",    DEF_MAX_COLORS,    t_Int},
145   {&dropSpeed,    "dropSpeed",    "DropSpeed",    DEF_DROP_SPEED,    t_Float},
146 };
147
148 static topBlockSTATE *tbs = NULL;
149
150 static ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
151
152 /* Window management, etc */
153 ENTRYPOINT void
154 reshape_topBlock (ModeInfo *mi, int width, int height)
155 {
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 {
170   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
171         NODE *llCurrent, *llOld;
172         llCurrent = tb->blockNodeRoot;
173         while (llCurrent != NULL) {
174                 llOld = llCurrent;
175                 llCurrent = llCurrent->next;
176                 free(llOld);
177         }
178 }
179
180 /* setup */
181 ENTRYPOINT void 
182 init_topBlock (ModeInfo *mi)
183 {
184   topBlockSTATE *tb;
185   int wire = MI_IS_WIREFRAME(mi);
186
187   if (!tbs) {
188     tbs = (topBlockSTATE *)
189       calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
190     if (!tbs) abort();
191   }
192
193   tb = &tbs[MI_SCREEN(mi)];
194
195   tb->glx_context = init_GL(mi);
196
197   reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
198
199 /*      if (wire) { drawNipples=False; }*/
200   tb->numFallingBlocks=0;
201
202         if (size>10) { size = 10; }
203         if (size<1) { size = 2; }
204         tb->carpetWidth = 8 * size;
205         tb->carpetLength = tb->carpetWidth;
206   
207   maxFalling*=size;
208
209         if (spawn<4) { spawn=4; }
210         if (spawn>1000) { spawn=1000; }
211
212         if (rotateSpeed<1) {rotateSpeed=1; }
213         if (rotateSpeed>1000) {rotateSpeed=1000;}
214   rotateSpeed /= 100;
215
216         if (resolution<4) {resolution=4;}
217         if (resolution>20) {resolution=20;}
218   resolution*=2;
219
220         if (maxColors<1) {maxColors=1;}
221         if (maxColors>8) {maxColors=8;}
222
223         if (dropSpeed<1) {dropSpeed=1;}
224         if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
225   
226         dropSpeed = 80/dropSpeed;
227         dropSpeed = (blockHeight/dropSpeed); 
228
229   reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
230   glClearDepth(1.0f);
231   if (!wire) {
232     GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
233     GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
234     GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
235     GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
236
237     glEnable(GL_LIGHTING);
238     glEnable(GL_LIGHT0);
239     glLightfv(GL_LIGHT0, GL_POSITION, pos);
240     glLightfv(GL_LIGHT0, GL_AMBIENT,  amb); 
241     glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
242     glLightfv(GL_LIGHT0, GL_SPECULAR, spc); 
243   }
244         glDepthFunc(GL_LEQUAL);
245   glEnable(GL_DEPTH_TEST);
246   glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
247   glCullFace(GL_BACK); 
248
249         if (drawBlob) {
250     buildBlobBlock(mi); 
251         } else {
252           buildBlock(mi);               /* build the display list holding the simple block */
253         }
254   buildCarpet(mi);              /* build the base */
255         tb->highest=0;
256         tb->highestFalling=0;
257         tb->eyeLine=tb->highest;
258         tb->eyeX=0;
259         tb->eyeY=0;
260         tb->eyeZ=0;
261         tb->followMode=0;
262   if (follow) {
263     tb->plusheight=100;
264     camZ=camZ-60;
265   } else {
266     tb->rotation=random() % 360;
267     tb->eyeY=10;
268     tb->plusheight=30;
269   }
270         tb->followRadius=0;
271   /* override camera settings */
272   if (override) {
273     tb->plusheight=100;
274     drawCarpet=False;
275     camX=0;
276     camY=1;
277     camZ=0;
278     tb->eyeX=-1;
279     tb->eyeY=20;
280     tb->eyeZ=0;
281   }
282   tb->trackball = gltrackball_init (False);
283 }
284
285 /* provides the per frame entertainment */
286 ENTRYPOINT void
287 draw_topBlock (ModeInfo *mi)
288 {
289         Display *dpy = MI_DISPLAY(mi);
290         Window window = MI_WINDOW(mi);
291         NODE *llCurrent;
292         NODE *llNode;
293         topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
294         GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
295         GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
296         int wire = MI_IS_WIREFRAME(mi);
297           GLfloat color[4];     
298
299   if (!tb->glx_context)
300     return;
301   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
302   mi->polygon_count = 0;
303
304         generateNewBlock(mi);
305
306         if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; } 
307         if (tb->rotation>=360) { tb->rotation=tb->rotation-360; } 
308
309         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);             /* clear current */
310         glLoadIdentity();       /* resets directions, do it every time ! */
311         glRotatef(current_device_rotation(), 0, 0, 1);
312
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
322         /* Rotate the scene around a point that's a little higher up. */
323         glTranslatef (0, 0, -5);
324         gltrackball_rotate (tb->trackball);
325         glTranslatef (0, 0, 5);
326
327         /* rotate the world */
328         glRotatef(tb->rotation, 0.0, 0.0, 1.0);         
329
330         llCurrent = tb->blockNodeRoot;
331         if (drawCarpet) {
332                 /* center carpet */
333                 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
334                 glCallList(tb->carpet);
335                 mi->polygon_count += tb->carpet_polys;
336                 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
337                 glTranslatef(0.0,0.0,-0.55);
338         }
339         tb->highestFalling=0;
340         while (llCurrent != NULL) {     /* for each block */
341                 glPushMatrix(); /* save state */
342                 /* set color */
343                 switch (llCurrent->color) { 
344                         case 0:
345                                 color[0] = 1.0f;        
346                                 color[1] = 0.0f;        
347                                 color[2] = 0.0f;        
348                                 color[3] = 1.0f;        
349                                 break;
350                         case 1:
351                                 color[0] = 0.0f;        
352                                 color[1] = 1.0f;        
353                                 color[2] = 0.0f;        
354                                 color[3] = 1.0f;        
355                                 break;
356                         case 2:
357                                 color[0] = 0.0f;        
358                                 color[1] = 0.0f;        
359                                 color[2] = 1.0f;        
360                                 color[3] = 1.0f;        
361                                 break;
362                         case 3:
363                                 color[0] = 0.95f;       
364                                 color[1] = 0.95f;       
365                                 color[2] = 0.95f;       
366                                 color[3] = 1.0f;        
367                                 break;
368                         case 4:
369                                 color[0] = 1.0f;        
370                                 color[1] = 0.5f;        
371                                 color[2] = 0.0f;        
372                                 color[3] = 1.0f;        
373                                 break;
374                         case 5:
375                                 color[0] = 1.0f;        
376                                 color[1] = 1.0f;        
377                                 color[2] = 0.0f;        
378                                 color[3] = 1.0f;        
379                                 break;
380                         case 6: 
381                                 color[0] = 0.5f;        
382                                 color[1] = 0.5f;        
383                                 color[2] = 0.5f;        
384                                 color[3] = 1.0f;        
385                                 break;
386                         case 7:
387                                 color[0] = 0.05f;       
388                                 color[1] = 0.05f;       
389                                 color[2] = 0.05f;       
390                                 color[3] = 1.0f;        
391                                 break;
392                 } 
393                 if (wire) { glColor3fv(color); }
394                 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
395
396                 if (llCurrent->falling==1) {
397         spcC2x = 0;
398             spcC2y = 0;
399             spcN2x = 0;
400             spcN2y = 0;
401                         if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
402                         /* all blocks fall at the same rate to avoid mid air collisions */
403                         llCurrent->height-=dropSpeed;
404                         if (llCurrent->height<=0) {
405                                 llCurrent->falling=0;
406                                 if (tb->highest==0) { 
407                                         tb->highest+=blockHeight; 
408                                 }
409                         } 
410                         if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
411                                 /* check for collision */
412                                 llNode = tb->blockNodeRoot;
413                                 spcC1x = llCurrent->x;
414                                 spcC1y = llCurrent->y;
415                                 switch(llCurrent->rotation) {
416                                         case getOrientation(0):
417                                                 spcC2x = spcC1x;
418                                                 spcC2y = spcC1y-2;
419                                                 break;
420                                         case getOrientation(1):
421                                                 spcC2x = spcC1x+2;
422                                                 spcC2y = spcC1y;
423                                                 break;
424                                         case getOrientation(2):
425                                                 spcC2x = spcC1x;
426                                                 spcC2y = spcC1y+2;
427                                                 break;
428                                         case getOrientation(3):
429                                                 spcC2x = spcC1x-2;
430                                                 spcC2y = spcC1y;
431                                                 break;
432                                 }
433                                 while (llNode != NULL) {
434                                         if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
435                                                 spcN1x = llNode->x;
436                                                 spcN1y = llNode->y;
437                                                 switch(llNode->rotation) {
438                                                         case getOrientation(0):
439                                                                 spcN2x = spcN1x;
440                                                                 spcN2y = spcN1y-2;
441                                                                 break;
442                                                         case getOrientation(1):
443                                                                 spcN2x = spcN1x+2;
444                                                                 spcN2y = spcN1y;
445                                                                 break;
446                                                         case getOrientation(2):
447                                                                 spcN2x = spcN1x;
448                                                                 spcN2y = spcN1y+2;
449                                                                 break;
450                                                         case getOrientation(3):
451                                                                 spcN2x = spcN1x-2;
452                                                                 spcN2y = spcN1y;
453                                                                 break;
454                                                 }
455                                                 if ( 
456                                                         ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
457                                                         ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
458                                                         ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
459                                                         ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
460                                                 ){
461               if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) { 
462
463                                                     llCurrent->falling=0;
464                                                           llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
465                 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
466                  tb->highest+=blockHeight; 
467                                                           }
468                                                   }
469                                           }
470                                   }
471                                   llNode=llNode->next;
472                           }                             
473                   }
474           } 
475         /* set location in space */
476           glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
477           /* rotate */
478         glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
479         if ((tb->followMode==0) && (llCurrent->next==NULL)) {
480                 tb->blockNodeFollow = llCurrent;
481                 tb->followMode=1;
482         } 
483         llCurrent = llCurrent->next;
484         /* draw   */
485         glCallList(tb->block); 
486         mi->polygon_count += tb->block_polys;
487         glPopMatrix();  /* restore state */
488   } 
489   if (mi->fps_p) do_fps (mi);
490   glFinish();
491
492         if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
493   glXSwapBuffers(dpy, window);
494 }
495
496
497
498 /* camera is in follow mode, work out where we should be looking */
499 static void followBlock(ModeInfo *mi)
500 {
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 {
546         if ((x>=cx) && (y>=cy)) {
547                 angle +=  (90-(angle-90) * 2); 
548         } else if ((x>=cx) && (y<=cy)) {
549                 angle +=  90; 
550         } else if ((x<=cx) && (y<=cy)) {
551                 angle +=  90; 
552         } else if ((x<=cx) && (y>=cy)) {
553                 angle += (90-(angle-90) * 2); 
554         }
555         return(angle-180);
556 }
557
558 /* if random chance then create a new falling block */
559 static void generateNewBlock(ModeInfo *mi)
560 {
561         NODE *llCurrent, *llTail;
562         GLfloat startOffx, startOffy;
563         int endOffx, endOffy;
564   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
565         if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {  
566                 startOffx=0;
567                 endOffx=0;
568                 startOffy=0;
569                 endOffy=0;
570                 tb->numFallingBlocks++;
571                 llTail = tb->blockNodeRoot; 
572                 if (llTail == NULL) {
573                   llCurrent = ((NODE*) malloc(sizeof(NODE)));
574                   if (!llCurrent) abort();
575                   llTail = llCurrent;
576                   tb->blockNodeRoot = llCurrent; 
577                 } else {
578                         if (tb->numFallingBlocks>=maxFalling) {
579                                 /* recycle */
580                                 llCurrent=llTail->next;
581                                 tb->blockNodeRoot=llCurrent->next;
582                         } else {
583                           llCurrent = ((NODE*) malloc(sizeof(NODE)));
584                           if (!llCurrent) abort();
585                         }
586                         while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
587                 }
588                 llCurrent->falling=1;
589                 llCurrent->rotation=getOrientation(random() % 4); 
590                 if (llCurrent->rotation==getOrientation(0)) {
591                         startOffx=1.0;
592                         endOffx=0;
593                         startOffy=3.0;
594                         endOffy=-1;
595                 } else if (llCurrent->rotation==getOrientation(1)) {
596                         startOffx=1.0;
597                         endOffx=-1;
598                         startOffy=1.0;
599                         endOffy=0;              
600                 } else if (llCurrent->rotation==getOrientation(2)) {
601                         startOffx=1.0;
602                         endOffx=0;
603                         startOffy=3.0;
604                         endOffy=-1;             
605                 } else if (llCurrent->rotation==getOrientation(3)) { 
606                         startOffx=5.0;
607                         endOffx=-1;
608                         startOffy=1.0;
609                         endOffy=0;              
610                 }
611
612                 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
613                 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
614                 llCurrent->color=(random() % maxColors);
615                 llCurrent->height=getHeight(tb->plusheight+tb->highest); 
616                 if (tb->numFallingBlocks>=maxFalling) {
617                         tb->numFallingBlocks--;
618                         tb->numFallingBlocks--;
619                 } 
620                 llTail->next = llCurrent;
621                 llTail = llCurrent;
622                 llTail->next = NULL;
623
624         }
625 }
626
627 /* called at init this creates the 'carpet' display list item */
628 static void buildCarpet(ModeInfo *mi)
629 {
630         int i,c,x,y;
631         GLfloat color[4];
632         int wire = MI_IS_WIREFRAME(mi);
633         topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
634                 color[0] = 0.0f;        
635                 color[1] = 1.0f;        
636                 color[2] = 0.0f;        
637                 color[3] = 1.0f;        
638         tb->carpet=glGenLists(1);       /* only one */
639         glNewList(tb->carpet,GL_COMPILE);
640         tb->carpet_polys=0;
641         glPushMatrix(); /* save state */
642         x=tb->carpetWidth;
643         y=tb->carpetLength;
644         if (wire) { glColor3fv(color); }
645         else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
646         /* draw carpet plane */
647         glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
648                 /* draw top */
649                 glNormal3f( 0, 0, -1 );
650                 glVertex3f(0.0,0.0,0.0);
651                 glVertex3f(x,0.0,0.0);
652                 glVertex3f(x,y,0.0);
653                 glVertex3f(0.0,y,0.0);
654                 tb->carpet_polys++;
655             if (!wire) {
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                 glTranslatef(0.5f,0.5f,-.25);                   /* move to the cylinder center */
690                 for (c=0;c<x;c++) {
691                         glPushMatrix(); /* save state */
692                         for (i=0;i<y;i++) {
693                           tb->carpet_polys += tube(0, 0, -0.1,
694                                                    0, 0, 0.26,
695                                                    cylSize, 0,
696                                                    resolution, True, True,
697                                                    wire);
698                           glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
699                           glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
700                           glTranslatef(0.0f,1.0f,0.0f);                 /* move to the next cylinder center (backward) */
701                         }
702                         glPopMatrix();  /* save state */
703                         glTranslatef(1.0f,0.0f,0.0f);                   /* reset   */
704                 }
705         }
706         glPopMatrix();  /* restore state */
707         glEndList();    
708 }
709
710 /* using the verticies arrays builds the plane, now with normals */
711 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
712 {
713         GLfloat topBlockNormals[5][3] = { {0,0,-1},     {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
714         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} };
715         glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
716                 glNormal3fv(topBlockNormals[i] );
717                 glVertex3fv(topBlockVertices[a]);
718                 glVertex3fv(topBlockVertices[b]);
719                 glVertex3fv(topBlockVertices[c]);
720                 glVertex3fv(topBlockVertices[d]);
721         glEnd();
722 }
723
724 /* called at init this creates the 'block' display list item */
725 /* the spheres came about originaly as quick way to test the directional lighting/normals */
726 static void buildBlock(ModeInfo *mi)
727 {
728         int i,c;
729   int wire = MI_IS_WIREFRAME(mi);
730   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
731         tb->block=glGenLists(1);        /* only one */
732         glNewList(tb->block,GL_COMPILE);
733         tb->block_polys=0;
734         glPushMatrix(); /* save state */
735         glRotatef(90, 0.0f, 1.0f, 0.0f);
736         /* base */
737         polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
738         polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
739         polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
740         polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
741         polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
742         if (drawNipples) {
743                 /* nipples */
744                 /* draw 8 cylinders each with a disk cap */
745                 glRotatef(90, 0.0f, 1.0f, 0.0f);                /* 'aim' the pointer ready for the cylinder */
746                 glTranslatef(0.5f,0.5f,0.99f);                  /* move to the cylinder center */
747                 for (c=0;c<2;c++) {
748                         for (i=0;i<4;i++) {
749                           tb->block_polys += tube(0, 0, 0,
750                                                   0, 0, 0.25,
751                                                   cylSize, 0,
752                                                   resolution, True, True, 
753                                                   wire);
754                                 glTranslatef(0.0f,0.0f,0.25f);                  /* move to the cylinder cap  */
755                                 glTranslatef(0.0f,0.0f,-0.25f);                 /* move back from the cylinder cap  */
756                                 if (c==0) {     
757                                         glTranslatef(0.0f,-1.0f,0.0f);                  /* move to the next cylinder center (forward) */
758                                 } else {
759                                         glTranslatef(0.0f,1.0f,0.0f);                   /* move to the next cylinder center (backward) */
760                                 }
761                         }
762                         glTranslatef(-1.0f,1.0f,0.0f);                  /* move to the cylinder center */
763                 }
764                 /* udders */
765                 /* 3 cylinders on the underside */
766                 glTranslatef(1.5f,-2.5f,-1.5f);         /* move to the center, under the top of the brick        */
767                 if (! wire)
768                 for (c=0;c<3;c++) {
769                   tb->block_polys += tube(0, 0, 0.1,
770                                           0, 0, 1.4,
771                                           uddSize, 0,
772                                           resolution, True, True, wire);
773                   glTranslatef(0.0f,-1.0f,0.0f);                /* move to the center */        
774                 }
775         }
776         glPopMatrix();  /* restore state */
777         glEndList();    
778 }
779
780 /* 
781         rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
782         spheres are created with unit_sphere from spheres.h to allow wire frame 
783 */
784 static void buildBlobBlock(ModeInfo *mi)
785 {
786   int wire = MI_IS_WIREFRAME(mi);
787   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
788         tb->block=glGenLists(1);        /* only one */
789         glNewList(tb->block,GL_COMPILE);
790         glPushMatrix();
791   glScalef(1.4,1.4,1.4);
792   unit_sphere (resolution/2,resolution, wire);
793   glPopMatrix();
794   glTranslatef(0.0f,-2.0f,0.0f);
795   glScalef(1.4,1.4,1.4);
796   unit_sphere (resolution/2,resolution, wire);
797         glEndList();    
798 }
799
800
801 /* handle input events or not if daemon running the show */
802 ENTRYPOINT Bool 
803 topBlock_handle_event (ModeInfo *mi, XEvent *event)
804 {
805   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
806
807   if (gltrackball_event_handler (event, tb->trackball,
808                                  MI_WIDTH (mi), MI_HEIGHT (mi),
809                                  &tb->button_down_p))
810     return True;
811   else if (event->xany.type == KeyPress) {
812     KeySym keysym;
813     char c = 0;
814     XLookupString (&event->xkey, &c, 1, &keysym, 0);
815     if (c == 'a') {
816                         tb->eyeX++;
817                         return True;
818                 } else if (c == 'z') {
819                         tb->eyeX--;
820                         return True;
821                 } else if (c == 's') {
822                         tb->eyeY--;
823                         return True;
824                 } else if (c == 'x') {
825                         tb->eyeY++;
826                         return True;
827                 } else if (c == 'd') {
828                         tb->eyeZ++;
829                         return True;
830                 } else if (c == 'c') {
831                         tb->eyeZ--;
832                         return True;
833                 } else if (c == 'f') {
834                         camX++;
835                         return True;
836                 } else if (c == 'v') {
837                         camX--;
838                         return True;
839                 } else if (c == 'g') {
840                         camY++;
841                         return True;
842                 } else if (c == 'b') {
843                         camY--;
844                         return True;
845                 } else if (c == 'h') {
846                         camZ++;
847                         return True;
848                 } else if (c == 'n') {
849                         camZ--;
850                         return True;
851                 } else if (c == 'r') {
852                         tb->rotation++;
853                         return True;
854                 }
855         }
856
857   return False;
858 }
859
860 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
861 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)
862
863 #endif /* USE_GL */