From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / glx / topblock.c
1 /* topblock, Copyright (c) 2006-2012 rednuht <topblock.xscreensaver@jumpstation.co.uk>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * 
12  *
13
14 topBlock - a simple openGL 3D hack of falling blocks
15 based on jwz's dangerball hack
16
17 The proporations of the blocks and their features is not even close to the commercial building block products offered by a variety companies.
18
19 information on this hack might be found at 
20 http://www.jumpstation.co.uk/xscreensaver/topblock/
21
22 History
23 25/02/2006 v1.0 release
24 29/04/2006 v1.11 updated to better fit with xscreensaver v5
25                  colors defaults to 7 (no black)
26 19/06/2006 v1.2  fixed dropSpeed = 7 bug, added gltrackball support and some code neatening, thanks to Valdis Kletnieks and JWZ for their input.
27 */
28
29 #include <math.h>
30
31 # define refresh_topBlock 0
32
33 #define DEFAULTS        "*delay:        10000       \n" \
34                         "*count:        30           \n" \
35                         "*showFPS:      False       \n" \
36                         "*wireframe:    False       \n" \
37
38 #undef countof
39 #define countof(x) (sizeof((x))/sizeof((*x)))
40
41 #include "xlockmore.h"
42 #include "topblock.h"
43 #include "sphere.h"
44 #include "tube.h"
45 #include "gltrackball.h"
46 #include <ctype.h>
47
48 #ifdef USE_GL /* whole file */
49
50 #ifndef HAVE_COCOA
51 # include <GL/glu.h>
52 #endif
53
54 typedef struct
55 {
56   GLXContext *glx_context;
57   trackball_state *trackball;
58   Bool button_down_p;
59   int numFallingBlocks;
60   GLfloat highest,highestFalling;
61   GLfloat eyeLine,eyeX,eyeY,eyeZ;
62   int carpetWidth, carpetLength;
63   int followMode;
64   GLfloat followRadius,followAngle;
65   int plusheight;
66   GLuint        carpet;
67   GLuint        block;
68   int carpet_polys, block_polys;
69   NODE *blockNodeRoot;
70   NODE *blockNodeFollow;
71   GLfloat rotation;
72 } topBlockSTATE;
73
74 /* parameter vars */
75 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_CARPET   "True"
116 #define DEF_BLOB     "False"
117 #define DEF_NIPPLES  "True"
118 #define DEF_ROTATE_SPEED  "10"
119 #define DEF_MAX_FALLING   "500"
120 #define DEF_MAX_COLORS    "7"
121 #define DEF_SIZE          "2"
122 #define DEF_SPAWN         "50"
123 #define DEF_RESOLUTION    "4"
124 #define DEF_CAM_X         "1"
125 #define DEF_CAM_Y         "20"
126 #define DEF_CAM_Z         "25"
127 #define DEF_DROP_SPEED    "4"
128
129 static argtype vars[] = {
130   {&override,     "override",     "Override",     DEF_OVERRIDE,     t_Bool},
131   {&rotate,       "rotate",       "Rotate",       DEF_ROTATE,       t_Bool},
132   {&drawCarpet,   "carpet",       "Carpet",       DEF_CARPET,   t_Bool},
133   {&drawNipples,  "nipples",      "Nipples",      DEF_NIPPLES,  t_Bool},
134   {&drawBlob,     "blob",         "Blob",         DEF_BLOB,     t_Bool},
135   {&rotateSpeed,  "rotateSpeed",  "RotateSpeed",  DEF_ROTATE_SPEED,  t_Float},
136   {&follow,       "follow",       "Follow",       DEF_FOLLOW,       t_Bool},
137   {&camX,         "camX",         "camX",         DEF_CAM_X,         t_Float},
138   {&camY,         "camY",         "camY",         DEF_CAM_Y,         t_Float},
139   {&camZ,         "camZ",         "camZ",         DEF_CAM_Z,         t_Float},
140   {&size,         "size",         "size",         DEF_SIZE,         t_Int},
141   {&spawn,        "spawn",        "spawn",        DEF_SPAWN,        t_Int},
142   {&maxFalling,   "maxFalling",   "maxFalling",   DEF_MAX_FALLING,   t_Int},
143   {&resolution,   "resolution",   "resolution",   DEF_RESOLUTION,   t_Int},
144   {&maxColors,    "maxColors",    "maxColors",    DEF_MAX_COLORS,    t_Int},
145   {&dropSpeed,    "dropSpeed",    "DropSpeed",    DEF_DROP_SPEED,    t_Float},
146 };
147
148 static topBlockSTATE *tbs = NULL;
149
150 ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
151
152 /* Window management, etc */
153 ENTRYPOINT void
154 reshape_topBlock (ModeInfo *mi, int width, int height)
155 {
156   GLfloat h = (GLfloat) height / (GLfloat) width;
157   glViewport (0, 0, (GLint) width, (GLint) height);
158   glMatrixMode(GL_PROJECTION);
159   glLoadIdentity();
160   gluPerspective (60.0, 1/h, 1.0, 1000.0);
161   glMatrixMode(GL_MODELVIEW);
162   glLoadIdentity();
163   glClear(GL_COLOR_BUFFER_BIT);
164 }
165
166 /* clean up on exit, not required ... */
167 ENTRYPOINT void
168 release_topBlock(ModeInfo *mi)
169 {
170   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
171         NODE *llCurrent, *llOld;
172         llCurrent = tb->blockNodeRoot;
173         while (llCurrent != NULL) {
174                 llOld = llCurrent;
175                 llCurrent = llCurrent->next;
176                 free(llOld);
177         }
178 }
179
180 /* setup */
181 ENTRYPOINT void 
182 init_topBlock (ModeInfo *mi)
183 {
184   topBlockSTATE *tb;
185   int wire = MI_IS_WIREFRAME(mi);
186
187   if (!tbs) {
188     tbs = (topBlockSTATE *)
189       calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
190     if (!tbs) abort();
191   }
192
193   tb = &tbs[MI_SCREEN(mi)];
194
195   tb->glx_context = init_GL(mi);
196
197   reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
198
199 /*      if (wire) { drawNipples=False; }*/
200   tb->numFallingBlocks=0;
201
202         if (size>10) { size = 10; }
203         if (size<1) { size = 2; }
204         tb->carpetWidth = 8 * size;
205         tb->carpetLength = tb->carpetWidth;
206   
207   maxFalling*=size;
208
209         if (spawn<4) { spawn=4; }
210         if (spawn>1000) { spawn=1000; }
211
212         if (rotateSpeed<1) {rotateSpeed=1; }
213         if (rotateSpeed>1000) {rotateSpeed=1000;}
214   rotateSpeed /= 100;
215
216         if (resolution<4) {resolution=4;}
217         if (resolution>20) {resolution=20;}
218   resolution*=2;
219
220         if (maxColors<1) {maxColors=1;}
221         if (maxColors>8) {maxColors=8;}
222
223         if (dropSpeed<1) {dropSpeed=1;}
224         if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
225   
226         dropSpeed = 80/dropSpeed;
227         dropSpeed = (blockHeight/dropSpeed); 
228
229   reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
230   glClearDepth(1.0f);
231   if (!wire) {
232     GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
233     GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
234     GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
235     GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
236
237     glEnable(GL_LIGHTING);
238     glEnable(GL_LIGHT0);
239     glLightfv(GL_LIGHT0, GL_POSITION, pos);
240     glLightfv(GL_LIGHT0, GL_AMBIENT,  amb); 
241     glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
242     glLightfv(GL_LIGHT0, GL_SPECULAR, spc); 
243   }
244         glDepthFunc(GL_LEQUAL);
245   glEnable(GL_DEPTH_TEST);
246   glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
247   glCullFace(GL_BACK); 
248
249         if (drawBlob) {
250     buildBlobBlock(mi); 
251         } else {
252           buildBlock(mi);               /* build the display list holding the simple block */
253         }
254   buildCarpet(mi);              /* build the base */
255         tb->highest=0;
256         tb->highestFalling=0;
257         tb->eyeLine=tb->highest;
258         tb->eyeX=0;
259         tb->eyeY=0;
260         tb->eyeZ=0;
261         tb->followMode=0;
262   if (follow) {
263     tb->plusheight=100;
264     camZ=camZ-60;
265   } else {
266     tb->rotation=random() % 360;
267     tb->eyeY=10;
268     tb->plusheight=30;
269   }
270         tb->followRadius=0;
271   /* override camera settings */
272   if (override) {
273     tb->plusheight=100;
274     drawCarpet=False;
275     camX=0;
276     camY=1;
277     camZ=0;
278     tb->eyeX=-1;
279     tb->eyeY=20;
280     tb->eyeZ=0;
281   }
282   tb->trackball = gltrackball_init ();
283 }
284
285 /* provides the per frame entertainment */
286 ENTRYPOINT void
287 draw_topBlock (ModeInfo *mi)
288 {
289         Display *dpy = MI_DISPLAY(mi);
290         Window window = MI_WINDOW(mi);
291         NODE *llCurrent;
292         NODE *llNode;
293         topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
294         GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
295         GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
296         int wire = MI_IS_WIREFRAME(mi);
297           GLfloat color[4];     
298
299   if (!tb->glx_context)
300     return;
301   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
302   mi->polygon_count = 0;
303
304         generateNewBlock(mi);
305
306         if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; } 
307         if (tb->rotation>=360) { tb->rotation=tb->rotation-360; } 
308
309         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);             /* clear current */
310         glLoadIdentity();       /* resets directions, do it every time ! */
311         glRotatef(current_device_rotation(), 0, 0, 1);
312
313         if (!follow) {
314                 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100);    } /* creates a smooth camera transition */
315                 gluLookAt(camX, camY+tb->eyeLine, camZ, tb->eyeX, tb->eyeY+tb->eyeLine, tb->eyeZ, 0.0, 1.0, 0.0);               /* setup viewer, xyz cam, xyz looking at and where is up normaly 0,1,0 */
316                 glRotatef(90, 1.0, 0.0, 0.0);                   /* x axis */
317         } else {
318                 glRotatef(90, 0.0, 0.0, 1.0);     /* z axis */
319                 followBlock(mi);
320         }
321
322         glScalef (-1, 1, 1);
323         glTranslatef (0, 0, -10);
324         glRotatef(-current_device_rotation(), 0, 0, 1);
325         gltrackball_rotate (tb->trackball);
326         glRotatef(current_device_rotation(), 0, 0, 1);
327         glTranslatef (0, 0, 10);
328         glScalef (-1, 1, 1);
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                 color[0] = 0.0f;        
638                 color[1] = 1.0f;        
639                 color[2] = 0.0f;        
640                 color[3] = 1.0f;        
641         tb->carpet=glGenLists(1);       /* only one */
642         glNewList(tb->carpet,GL_COMPILE);
643         tb->carpet_polys=0;
644         glPushMatrix(); /* save state */
645         x=tb->carpetWidth;
646         y=tb->carpetLength;
647         if (wire) { glColor3fv(color); }
648         else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
649         /* draw carpet plane */
650         glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
651                 /* draw top */
652                 glNormal3f( 0, 0, -1 );
653                 glVertex3f(0.0,0.0,0.0);
654                 glVertex3f(x,0.0,0.0);
655                 glVertex3f(x,y,0.0);
656                 glVertex3f(0.0,y,0.0);
657                 tb->carpet_polys++;
658             if (!wire) {
659                 /* add edge pieces */
660                 /* side 1 */
661                 glNormal3f( 0, -1, 0 );
662                 glVertex3f(0.0,0.0,0.0);
663                 glVertex3f(x,0.0,0.0);
664                 glVertex3f(x,0,singleThick);
665                 glVertex3f(0.0,0,singleThick);
666                 tb->carpet_polys++;
667                 /* side 2 */
668                 glNormal3f( -1, 0, 0 );
669                 glVertex3f(0.0,0.0,0.0);
670                 glVertex3f(0,y,0.0);
671                 glVertex3f(0,y,singleThick);
672                 glVertex3f(0.0,0,singleThick);
673                 tb->carpet_polys++;
674                 /* side 3 */
675                 glNormal3f( 1, 0, 0 );
676                 glVertex3f(x,0.0,0.0);
677                 glVertex3f(x,y,0.0);
678                 glVertex3f(x,y,singleThick);
679                 glVertex3f(x,0,singleThick);
680                 tb->carpet_polys++;
681                 /* side 4 */
682                 glNormal3f( 0, 1, 0 );
683                 glVertex3f(0,y,0.0);
684                 glVertex3f(x,y,0.0);
685                 glVertex3f(x,y,singleThick);
686                 glVertex3f(0,y,singleThick);
687                 tb->carpet_polys++;
688                 }
689         glEnd();
690         /* nipples */
691         if (drawNipples) {
692                 glTranslatef(0.5f,0.5f,-.25);                   /* move to the cylinder center */
693                 for (c=0;c<x;c++) {
694                         glPushMatrix(); /* save state */
695                         for (i=0;i<y;i++) {
696                           tb->carpet_polys += tube(0, 0, -0.1,
697                                                    0, 0, 0.26,
698                                                    cylSize, 0,
699                                                    resolution, True, True,
700                                                    wire);
701                           glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
702                           glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
703                           glTranslatef(0.0f,1.0f,0.0f);                 /* move to the next cylinder center (backward) */
704                         }
705                         glPopMatrix();  /* save state */
706                         glTranslatef(1.0f,0.0f,0.0f);                   /* reset   */
707                 }
708         }
709         glPopMatrix();  /* restore state */
710         glEndList();    
711 }
712
713 /* using the verticies arrays builds the plane, now with normals */
714 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
715 {
716         GLfloat topBlockNormals[5][3] = { {0,0,-1},     {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
717         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} };
718         glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
719                 glNormal3fv(topBlockNormals[i] );
720                 glVertex3fv(topBlockVertices[a]);
721                 glVertex3fv(topBlockVertices[b]);
722                 glVertex3fv(topBlockVertices[c]);
723                 glVertex3fv(topBlockVertices[d]);
724         glEnd();
725 }
726
727 /* called at init this creates the 'block' display list item */
728 /* the spheres came about originaly as quick way to test the directional lighting/normals */
729 static void buildBlock(ModeInfo *mi)
730 {
731         int i,c;
732   int wire = MI_IS_WIREFRAME(mi);
733   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
734         tb->block=glGenLists(1);        /* only one */
735         glNewList(tb->block,GL_COMPILE);
736         tb->block_polys=0;
737         glPushMatrix(); /* save state */
738         glRotatef(90, 0.0f, 1.0f, 0.0f);
739         /* base */
740         polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
741         polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
742         polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
743         polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
744         polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
745         if (drawNipples) {
746                 /* nipples */
747                 /* draw 8 cylinders each with a disk cap */
748                 glRotatef(90, 0.0f, 1.0f, 0.0f);                /* 'aim' the pointer ready for the cylinder */
749                 glTranslatef(0.5f,0.5f,0.99f);                  /* move to the cylinder center */
750                 for (c=0;c<2;c++) {
751                         for (i=0;i<4;i++) {
752                           tb->block_polys += tube(0, 0, 0,
753                                                   0, 0, 0.25,
754                                                   cylSize, 0,
755                                                   resolution, True, True, 
756                                                   wire);
757                                 glTranslatef(0.0f,0.0f,0.25f);                  /* move to the cylinder cap  */
758                                 glTranslatef(0.0f,0.0f,-0.25f);                 /* move back from the cylinder cap  */
759                                 if (c==0) {     
760                                         glTranslatef(0.0f,-1.0f,0.0f);                  /* move to the next cylinder center (forward) */
761                                 } else {
762                                         glTranslatef(0.0f,1.0f,0.0f);                   /* move to the next cylinder center (backward) */
763                                 }
764                         }
765                         glTranslatef(-1.0f,1.0f,0.0f);                  /* move to the cylinder center */
766                 }
767                 /* udders */
768                 /* 3 cylinders on the underside */
769                 glTranslatef(1.5f,-2.5f,-1.5f);         /* move to the center, under the top of the brick        */
770                 if (! wire)
771                 for (c=0;c<3;c++) {
772                   tb->block_polys += tube(0, 0, 0.1,
773                                           0, 0, 1.4,
774                                           uddSize, 0,
775                                           resolution, True, True, wire);
776                   glTranslatef(0.0f,-1.0f,0.0f);                /* move to the center */        
777                 }
778         }
779         glPopMatrix();  /* restore state */
780         glEndList();    
781 }
782
783 /* 
784         rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
785         spheres are created with unit_sphere from spheres.h to allow wire frame 
786 */
787 static void buildBlobBlock(ModeInfo *mi)
788 {
789   int wire = MI_IS_WIREFRAME(mi);
790   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
791         tb->block=glGenLists(1);        /* only one */
792         glNewList(tb->block,GL_COMPILE);
793         glPushMatrix();
794   glScalef(1.4,1.4,1.4);
795   unit_sphere (resolution/2,resolution, wire);
796   glPopMatrix();
797   glTranslatef(0.0f,-2.0f,0.0f);
798   glScalef(1.4,1.4,1.4);
799   unit_sphere (resolution/2,resolution, wire);
800         glEndList();    
801 }
802
803
804 /* handle input events or not if daemon running the show */
805 ENTRYPOINT Bool 
806 topBlock_handle_event (ModeInfo *mi, XEvent *event)
807 {
808   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
809         if (event->xany.type == KeyPress)    {
810     KeySym keysym;
811     char c = 0;
812     XLookupString (&event->xkey, &c, 1, &keysym, 0);
813     if (c == 'a') {
814                         tb->eyeX++;
815                         return True;
816                 } else if (c == 'z') {
817                         tb->eyeX--;
818                         return True;
819                 } else if (c == 's') {
820                         tb->eyeY--;
821                         return True;
822                 } else if (c == 'x') {
823                         tb->eyeY++;
824                         return True;
825                 } else if (c == 'd') {
826                         tb->eyeZ++;
827                         return True;
828                 } else if (c == 'c') {
829                         tb->eyeZ--;
830                         return True;
831                 } else if (c == 'f') {
832                         camX++;
833                         return True;
834                 } else if (c == 'v') {
835                         camX--;
836                         return True;
837                 } else if (c == 'g') {
838                         camY++;
839                         return True;
840                 } else if (c == 'b') {
841                         camY--;
842                         return True;
843                 } else if (c == 'h') {
844                         camZ++;
845                         return True;
846                 } else if (c == 'n') {
847                         camZ--;
848                         return True;
849                 } else if (c == 'r') {
850                         tb->rotation++;
851                         return True;
852                 }
853         }
854   if (event->xany.type == ButtonPress &&
855       event->xbutton.button == Button1)
856     {
857       tb->button_down_p = True;
858       gltrackball_start (tb->trackball,
859                          event->xbutton.x, event->xbutton.y,
860                          MI_WIDTH (mi), MI_HEIGHT (mi));
861       return True;
862     }
863   else if (event->xany.type == ButtonRelease &&
864            event->xbutton.button == Button1)
865     {
866       tb->button_down_p = False;
867       return True;
868     }
869   else if (event->xany.type == ButtonPress &&
870            (event->xbutton.button == Button4 ||
871             event->xbutton.button == Button5 ||
872             event->xbutton.button == Button6 ||
873             event->xbutton.button == Button7))
874     {
875       gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
876                               !!event->xbutton.state);
877       return True;
878     }
879   else if (event->xany.type == MotionNotify &&
880            tb->button_down_p)
881     {
882       gltrackball_track (tb->trackball,
883                          event->xmotion.x, event->xmotion.y,
884                          MI_WIDTH (mi), MI_HEIGHT (mi));
885       return True;
886     }
887         return False;
888 }
889
890 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
891 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)
892
893 #endif /* USE_GL */