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