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