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