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