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