eed2fd54f775fa0522bbb19a2fcf54ed88613b02
[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_JWXYZ
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 static Bool override;
76 static Bool rotate;
77 static Bool follow;
78 static Bool drawCarpet;
79 static Bool drawBlob;
80 static Bool drawNipples;
81 static GLfloat rotateSpeed;
82 static GLfloat camX;
83 static GLfloat camY;
84 static GLfloat camZ;
85 static GLfloat dropSpeed;
86 static int maxFalling;
87 static int maxColors;
88 static int size;
89 static int spawn;
90 static 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 static 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 (False);
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         /* Rotate the scene around a point that's a little higher up. */
323         glTranslatef (0, 0, -5);
324         gltrackball_rotate (tb->trackball);
325         glTranslatef (0, 0, 5);
326
327         /* rotate the world */
328         glRotatef(tb->rotation, 0.0, 0.0, 1.0);         
329
330 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
331         {
332           GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
333           int o = (int) current_device_rotation();
334           if (o != 0 && o != 180 && o != -180)
335             glScalef (1/h, 1/h, 1/h);
336         }
337 # endif
338
339         llCurrent = tb->blockNodeRoot;
340         if (drawCarpet) {
341                 /* center carpet */
342                 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
343                 glCallList(tb->carpet);
344                 mi->polygon_count += tb->carpet_polys;
345                 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
346                 glTranslatef(0.0,0.0,-0.55);
347         }
348         tb->highestFalling=0;
349         while (llCurrent != NULL) {     /* for each block */
350                 glPushMatrix(); /* save state */
351                 /* set color */
352                 switch (llCurrent->color) { 
353                         case 0:
354                                 color[0] = 1.0f;        
355                                 color[1] = 0.0f;        
356                                 color[2] = 0.0f;        
357                                 color[3] = 1.0f;        
358                                 break;
359                         case 1:
360                                 color[0] = 0.0f;        
361                                 color[1] = 1.0f;        
362                                 color[2] = 0.0f;        
363                                 color[3] = 1.0f;        
364                                 break;
365                         case 2:
366                                 color[0] = 0.0f;        
367                                 color[1] = 0.0f;        
368                                 color[2] = 1.0f;        
369                                 color[3] = 1.0f;        
370                                 break;
371                         case 3:
372                                 color[0] = 0.95f;       
373                                 color[1] = 0.95f;       
374                                 color[2] = 0.95f;       
375                                 color[3] = 1.0f;        
376                                 break;
377                         case 4:
378                                 color[0] = 1.0f;        
379                                 color[1] = 0.5f;        
380                                 color[2] = 0.0f;        
381                                 color[3] = 1.0f;        
382                                 break;
383                         case 5:
384                                 color[0] = 1.0f;        
385                                 color[1] = 1.0f;        
386                                 color[2] = 0.0f;        
387                                 color[3] = 1.0f;        
388                                 break;
389                         case 6: 
390                                 color[0] = 0.5f;        
391                                 color[1] = 0.5f;        
392                                 color[2] = 0.5f;        
393                                 color[3] = 1.0f;        
394                                 break;
395                         case 7:
396                                 color[0] = 0.05f;       
397                                 color[1] = 0.05f;       
398                                 color[2] = 0.05f;       
399                                 color[3] = 1.0f;        
400                                 break;
401                 } 
402                 if (wire) { glColor3fv(color); }
403                 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
404
405                 if (llCurrent->falling==1) {
406         spcC2x = 0;
407             spcC2y = 0;
408             spcN2x = 0;
409             spcN2y = 0;
410                         if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
411                         /* all blocks fall at the same rate to avoid mid air collisions */
412                         llCurrent->height-=dropSpeed;
413                         if (llCurrent->height<=0) {
414                                 llCurrent->falling=0;
415                                 if (tb->highest==0) { 
416                                         tb->highest+=blockHeight; 
417                                 }
418                         } 
419                         if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
420                                 /* check for collision */
421                                 llNode = tb->blockNodeRoot;
422                                 spcC1x = llCurrent->x;
423                                 spcC1y = llCurrent->y;
424                                 switch(llCurrent->rotation) {
425                                         case getOrientation(0):
426                                                 spcC2x = spcC1x;
427                                                 spcC2y = spcC1y-2;
428                                                 break;
429                                         case getOrientation(1):
430                                                 spcC2x = spcC1x+2;
431                                                 spcC2y = spcC1y;
432                                                 break;
433                                         case getOrientation(2):
434                                                 spcC2x = spcC1x;
435                                                 spcC2y = spcC1y+2;
436                                                 break;
437                                         case getOrientation(3):
438                                                 spcC2x = spcC1x-2;
439                                                 spcC2y = spcC1y;
440                                                 break;
441                                 }
442                                 while (llNode != NULL) {
443                                         if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
444                                                 spcN1x = llNode->x;
445                                                 spcN1y = llNode->y;
446                                                 switch(llNode->rotation) {
447                                                         case getOrientation(0):
448                                                                 spcN2x = spcN1x;
449                                                                 spcN2y = spcN1y-2;
450                                                                 break;
451                                                         case getOrientation(1):
452                                                                 spcN2x = spcN1x+2;
453                                                                 spcN2y = spcN1y;
454                                                                 break;
455                                                         case getOrientation(2):
456                                                                 spcN2x = spcN1x;
457                                                                 spcN2y = spcN1y+2;
458                                                                 break;
459                                                         case getOrientation(3):
460                                                                 spcN2x = spcN1x-2;
461                                                                 spcN2y = spcN1y;
462                                                                 break;
463                                                 }
464                                                 if ( 
465                                                         ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
466                                                         ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
467                                                         ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
468                                                         ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
469                                                 ){
470               if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) { 
471
472                                                     llCurrent->falling=0;
473                                                           llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
474                 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
475                  tb->highest+=blockHeight; 
476                                                           }
477                                                   }
478                                           }
479                                   }
480                                   llNode=llNode->next;
481                           }                             
482                   }
483           } 
484         /* set location in space */
485           glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
486           /* rotate */
487         glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
488         if ((tb->followMode==0) && (llCurrent->next==NULL)) {
489                 tb->blockNodeFollow = llCurrent;
490                 tb->followMode=1;
491         } 
492         llCurrent = llCurrent->next;
493         /* draw   */
494         glCallList(tb->block); 
495         mi->polygon_count += tb->block_polys;
496         glPopMatrix();  /* restore state */
497   } 
498   if (mi->fps_p) do_fps (mi);
499   glFinish();
500
501         if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
502   glXSwapBuffers(dpy, window);
503 }
504
505
506
507 /* camera is in follow mode, work out where we should be looking */
508 static void followBlock(ModeInfo *mi)
509 {
510         GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
511   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
512   cx=0;cy=0;
513         if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
514
515                 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100);   } 
516                   /*tb->blockNodeFollow->color=1;  only noticable if you set the colors to 1 */
517                 
518                         if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); } 
519                         if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); } 
520                 
521
522                 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
523                    (see http://www.jumpstation.co.uk/rotation/)
524                 */
525
526                 if (tb->followRadius==0) {              
527                         xLen = tb->blockNodeFollow->x-cx;
528                         yLen = tb->blockNodeFollow->y-cy;
529                         tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );     
530                         tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius); 
531                         tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
532                 }
533                 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
534                 xTarget = cos(rangle) * tb->followRadius + cx;
535                 yTarget = sin(rangle) * tb->followRadius + cy;
536                 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
537
538                 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
539                 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
540
541                 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
542                 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
543                 if (!tb->blockNodeFollow->falling) {  
544                         tb->followMode=0; 
545                         /*tb->blockNodeFollow->color=2;  only noticable if you set the colors to 1 */
546                         tb->followRadius=0;
547                 } 
548         }
549         gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
550 }
551
552 /* each quater of the circle has to be adjusted for */
553 static double quadrantCorrection(double angle,int cx,int cy,int x,int y)
554 {
555         if ((x>=cx) && (y>=cy)) {
556                 angle +=  (90-(angle-90) * 2); 
557         } else if ((x>=cx) && (y<=cy)) {
558                 angle +=  90; 
559         } else if ((x<=cx) && (y<=cy)) {
560                 angle +=  90; 
561         } else if ((x<=cx) && (y>=cy)) {
562                 angle += (90-(angle-90) * 2); 
563         }
564         return(angle-180);
565 }
566
567 /* if random chance then create a new falling block */
568 static void generateNewBlock(ModeInfo *mi)
569 {
570         NODE *llCurrent, *llTail;
571         GLfloat startOffx, startOffy;
572         int endOffx, endOffy;
573   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
574         if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {  
575                 startOffx=0;
576                 endOffx=0;
577                 startOffy=0;
578                 endOffy=0;
579                 tb->numFallingBlocks++;
580                 llTail = tb->blockNodeRoot; 
581                 if (llTail == NULL) {
582                   llCurrent = ((NODE*) malloc(sizeof(NODE)));
583                   if (!llCurrent) abort();
584                   llTail = llCurrent;
585                   tb->blockNodeRoot = llCurrent; 
586                 } else {
587                         if (tb->numFallingBlocks>=maxFalling) {
588                                 /* recycle */
589                                 llCurrent=llTail->next;
590                                 tb->blockNodeRoot=llCurrent->next;
591                         } else {
592                           llCurrent = ((NODE*) malloc(sizeof(NODE)));
593                           if (!llCurrent) abort();
594                         }
595                         while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
596                 }
597                 llCurrent->falling=1;
598                 llCurrent->rotation=getOrientation(random() % 4); 
599                 if (llCurrent->rotation==getOrientation(0)) {
600                         startOffx=1.0;
601                         endOffx=0;
602                         startOffy=3.0;
603                         endOffy=-1;
604                 } else if (llCurrent->rotation==getOrientation(1)) {
605                         startOffx=1.0;
606                         endOffx=-1;
607                         startOffy=1.0;
608                         endOffy=0;              
609                 } else if (llCurrent->rotation==getOrientation(2)) {
610                         startOffx=1.0;
611                         endOffx=0;
612                         startOffy=3.0;
613                         endOffy=-1;             
614                 } else if (llCurrent->rotation==getOrientation(3)) { 
615                         startOffx=5.0;
616                         endOffx=-1;
617                         startOffy=1.0;
618                         endOffy=0;              
619                 }
620
621                 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
622                 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
623                 llCurrent->color=(random() % maxColors);
624                 llCurrent->height=getHeight(tb->plusheight+tb->highest); 
625                 if (tb->numFallingBlocks>=maxFalling) {
626                         tb->numFallingBlocks--;
627                         tb->numFallingBlocks--;
628                 } 
629                 llTail->next = llCurrent;
630                 llTail = llCurrent;
631                 llTail->next = NULL;
632
633         }
634 }
635
636 /* called at init this creates the 'carpet' display list item */
637 static void buildCarpet(ModeInfo *mi)
638 {
639         int i,c,x,y;
640         GLfloat color[4];
641         int wire = MI_IS_WIREFRAME(mi);
642         topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
643                 color[0] = 0.0f;        
644                 color[1] = 1.0f;        
645                 color[2] = 0.0f;        
646                 color[3] = 1.0f;        
647         tb->carpet=glGenLists(1);       /* only one */
648         glNewList(tb->carpet,GL_COMPILE);
649         tb->carpet_polys=0;
650         glPushMatrix(); /* save state */
651         x=tb->carpetWidth;
652         y=tb->carpetLength;
653         if (wire) { glColor3fv(color); }
654         else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
655         /* draw carpet plane */
656         glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
657                 /* draw top */
658                 glNormal3f( 0, 0, -1 );
659                 glVertex3f(0.0,0.0,0.0);
660                 glVertex3f(x,0.0,0.0);
661                 glVertex3f(x,y,0.0);
662                 glVertex3f(0.0,y,0.0);
663                 tb->carpet_polys++;
664             if (!wire) {
665                 /* add edge pieces */
666                 /* side 1 */
667                 glNormal3f( 0, -1, 0 );
668                 glVertex3f(0.0,0.0,0.0);
669                 glVertex3f(x,0.0,0.0);
670                 glVertex3f(x,0,singleThick);
671                 glVertex3f(0.0,0,singleThick);
672                 tb->carpet_polys++;
673                 /* side 2 */
674                 glNormal3f( -1, 0, 0 );
675                 glVertex3f(0.0,0.0,0.0);
676                 glVertex3f(0,y,0.0);
677                 glVertex3f(0,y,singleThick);
678                 glVertex3f(0.0,0,singleThick);
679                 tb->carpet_polys++;
680                 /* side 3 */
681                 glNormal3f( 1, 0, 0 );
682                 glVertex3f(x,0.0,0.0);
683                 glVertex3f(x,y,0.0);
684                 glVertex3f(x,y,singleThick);
685                 glVertex3f(x,0,singleThick);
686                 tb->carpet_polys++;
687                 /* side 4 */
688                 glNormal3f( 0, 1, 0 );
689                 glVertex3f(0,y,0.0);
690                 glVertex3f(x,y,0.0);
691                 glVertex3f(x,y,singleThick);
692                 glVertex3f(0,y,singleThick);
693                 tb->carpet_polys++;
694                 }
695         glEnd();
696         /* nipples */
697         if (drawNipples) {
698                 glTranslatef(0.5f,0.5f,-.25);                   /* move to the cylinder center */
699                 for (c=0;c<x;c++) {
700                         glPushMatrix(); /* save state */
701                         for (i=0;i<y;i++) {
702                           tb->carpet_polys += tube(0, 0, -0.1,
703                                                    0, 0, 0.26,
704                                                    cylSize, 0,
705                                                    resolution, True, True,
706                                                    wire);
707                           glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
708                           glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
709                           glTranslatef(0.0f,1.0f,0.0f);                 /* move to the next cylinder center (backward) */
710                         }
711                         glPopMatrix();  /* save state */
712                         glTranslatef(1.0f,0.0f,0.0f);                   /* reset   */
713                 }
714         }
715         glPopMatrix();  /* restore state */
716         glEndList();    
717 }
718
719 /* using the verticies arrays builds the plane, now with normals */
720 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
721 {
722         GLfloat topBlockNormals[5][3] = { {0,0,-1},     {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
723         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} };
724         glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
725                 glNormal3fv(topBlockNormals[i] );
726                 glVertex3fv(topBlockVertices[a]);
727                 glVertex3fv(topBlockVertices[b]);
728                 glVertex3fv(topBlockVertices[c]);
729                 glVertex3fv(topBlockVertices[d]);
730         glEnd();
731 }
732
733 /* called at init this creates the 'block' display list item */
734 /* the spheres came about originaly as quick way to test the directional lighting/normals */
735 static void buildBlock(ModeInfo *mi)
736 {
737         int i,c;
738   int wire = MI_IS_WIREFRAME(mi);
739   topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
740         tb->block=glGenLists(1);        /* only one */
741         glNewList(tb->block,GL_COMPILE);
742         tb->block_polys=0;
743         glPushMatrix(); /* save state */
744         glRotatef(90, 0.0f, 1.0f, 0.0f);
745         /* base */
746         polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
747         polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
748         polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
749         polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
750         polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
751         if (drawNipples) {
752                 /* nipples */
753                 /* draw 8 cylinders each with a disk cap */
754                 glRotatef(90, 0.0f, 1.0f, 0.0f);                /* 'aim' the pointer ready for the cylinder */
755                 glTranslatef(0.5f,0.5f,0.99f);                  /* move to the cylinder center */
756                 for (c=0;c<2;c++) {
757                         for (i=0;i<4;i++) {
758                           tb->block_polys += tube(0, 0, 0,
759                                                   0, 0, 0.25,
760                                                   cylSize, 0,
761                                                   resolution, True, True, 
762                                                   wire);
763                                 glTranslatef(0.0f,0.0f,0.25f);                  /* move to the cylinder cap  */
764                                 glTranslatef(0.0f,0.0f,-0.25f);                 /* move back from the cylinder cap  */
765                                 if (c==0) {     
766                                         glTranslatef(0.0f,-1.0f,0.0f);                  /* move to the next cylinder center (forward) */
767                                 } else {
768                                         glTranslatef(0.0f,1.0f,0.0f);                   /* move to the next cylinder center (backward) */
769                                 }
770                         }
771                         glTranslatef(-1.0f,1.0f,0.0f);                  /* move to the cylinder center */
772                 }
773                 /* udders */
774                 /* 3 cylinders on the underside */
775                 glTranslatef(1.5f,-2.5f,-1.5f);         /* move to the center, under the top of the brick        */
776                 if (! wire)
777                 for (c=0;c<3;c++) {
778                   tb->block_polys += tube(0, 0, 0.1,
779                                           0, 0, 1.4,
780                                           uddSize, 0,
781                                           resolution, True, True, wire);
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
816   if (gltrackball_event_handler (event, tb->trackball,
817                                  MI_WIDTH (mi), MI_HEIGHT (mi),
818                                  &tb->button_down_p))
819     return True;
820   else if (event->xany.type == KeyPress) {
821     KeySym keysym;
822     char c = 0;
823     XLookupString (&event->xkey, &c, 1, &keysym, 0);
824     if (c == 'a') {
825                         tb->eyeX++;
826                         return True;
827                 } else if (c == 'z') {
828                         tb->eyeX--;
829                         return True;
830                 } else if (c == 's') {
831                         tb->eyeY--;
832                         return True;
833                 } else if (c == 'x') {
834                         tb->eyeY++;
835                         return True;
836                 } else if (c == 'd') {
837                         tb->eyeZ++;
838                         return True;
839                 } else if (c == 'c') {
840                         tb->eyeZ--;
841                         return True;
842                 } else if (c == 'f') {
843                         camX++;
844                         return True;
845                 } else if (c == 'v') {
846                         camX--;
847                         return True;
848                 } else if (c == 'g') {
849                         camY++;
850                         return True;
851                 } else if (c == 'b') {
852                         camY--;
853                         return True;
854                 } else if (c == 'h') {
855                         camZ++;
856                         return True;
857                 } else if (c == 'n') {
858                         camZ--;
859                         return True;
860                 } else if (c == 'r') {
861                         tb->rotation++;
862                         return True;
863                 }
864         }
865
866   return False;
867 }
868
869 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
870 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)
871
872 #endif /* USE_GL */