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