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