http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.23.tar.gz
[xscreensaver] / hacks / glx / glhanoi.c
1 /* glhanoi, Copyright (c) 2005 Dave Atkinson <dave.atkinson@uwe.ac.uk>
2  * except noise function code Copyright (c) 2002 Ken Perlin
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 #include <assert.h>
14 #include <X11/Intrinsic.h>
15
16 #include <rotator.h>
17
18 #define PROGCLASS         "glhanoi"
19 #define HACK_INIT         init_glhanoi
20 #define HACK_DRAW         draw_glhanoi
21 #define HACK_RESHAPE      reshape_glhanoi
22 #define HACK_HANDLE_EVENT glhanoi_handle_event
23 #define EVENT_MASK        PointerMotionMask
24 #define glh_opts          xlockmore_opts
25
26 #define DEF_DELAY     "15000"
27 #define DEF_DISKS     "7"
28 #define DEF_WIRE      "False"
29 #define DEF_LIGHT     "True"
30 #define DEF_FPS       "False"
31 #define DEF_FOG       "False"
32 #define DEF_TEXTURE   "True"
33
34 #define DEFAULTS "*delay:     " DEF_DELAY     "\n" \
35                                  "*count:     " DEF_DISKS     "\n" \
36                                  "*showFPS:   " DEF_FPS       "\n" \
37                                  "*wireframe: " DEF_WIRE      "\n"
38
39 #define NSLICE 32
40 #define NLOOPS 1
41 #define START_DURATION 1.0
42 #define FINISH_DURATION 1.0
43 #define BASE_LENGTH 30.0
44 #define BOARD_SQUARES 8
45
46 #define MAX_CAMERA_RADIUS 250.0
47 #define MIN_CAMERA_RADIUS 75.0
48
49 #define MARBLE_SCALE 1.01
50
51 enum {
52         MARBLE_TEXURE,
53         N_TEXTURES
54 };
55
56 #define MARBLE_TEXTURE_SIZE 256
57
58 #undef countof
59 #define countof(x) (sizeof((x))/sizeof((*x)))
60
61 #include <math.h>
62 #include "xlockmore.h"
63
64 #ifdef USE_GL                                   /* whole file */
65
66 #include <GL/glu.h>
67
68 #ifdef HAVE_GETTIMEOFDAY
69 #ifdef GETTIMEOFDAY_TWO_ARGS
70 # include <sys/time.h>
71 # include <time.h>
72 typedef struct timeval glhtime;
73 #else                                                   /* GETTIMEOFDAY_TWO_ARGS */
74 # include <sys/time.h>
75 # include <time.h>
76 typedef struct timeval glhtime;
77 #endif
78 #else                                                   /* HAVE_GETTIMEOFDAY */
79 #ifdef HAVE_FTIME
80 # include <sys/timeb.h>
81 typedef struct timeb glhtime;
82 #endif                                                  /* HAVE_FTIME */
83 #endif                                                  /* HAVE_GETTIMEOFDAY */
84
85 double getTime(void)
86 {
87 #ifdef HAVE_GETTIMEOFDAY
88         struct timeval t;
89 #ifdef GETTIMEOFDAY_TWO_ARGS
90         gettimeofday(&t, NULL);
91 #else                                                   /* !GETTIMEOFDAY_TWO_ARGS */
92         gettimeofday(&t);
93 #endif                                                  /* !GETTIMEOFDAY_TWO_ARGS */
94 #else                                                   /* !HAVE_GETTIMEOFDAY */
95 #ifdef HAVE_FTIME
96         ftime(&t);
97 #endif                                                  /* HAVE_FTIME */
98 #endif                                                  /* !HAVE_GETTIMEOFDAY */
99         return t.tv_sec + t.tv_usec / 1000000.0;
100 }
101
102 typedef enum {
103         START,
104         MOVE_DISK,
105         MOVE_FINISHED,
106         FINISHED,
107         MONEY_SHOT,
108         INVALID = -1
109 } State;
110
111 typedef struct {
112         int id;
113         GLuint displayList;
114         GLfloat position[3];
115         GLfloat rotation[3];
116         GLfloat color[4];
117         GLfloat base0;
118         GLfloat base1;
119         GLfloat height;
120         GLfloat xmin, xmax, ymin;
121         GLfloat u1, u2;
122         GLfloat t1, t2;
123         GLfloat ucostheta, usintheta;
124         GLdouble rotAngle;
125 } Disk;
126
127 typedef struct {
128         Disk **data;
129         int count;
130         int size;
131 } Stack;
132
133 typedef struct {
134         GLXContext *glx_context;
135         State state;
136         Bool wire;
137         Bool fog;
138         Bool light;
139         double startTime;
140         double lastTime;
141         double duration;
142         int numberOfDisks;
143         int numberOfMoves;
144         int maxDiskIdx;
145         int magicNumber;
146         Disk *currentDisk;
147         int move;
148         int src;
149         int tmp;
150         int dst;
151         int oldsrc;
152         int oldtmp;
153         int olddst;
154         Stack pole[3];
155         float boardSize;
156         float baseLength;
157         float baseWidth;
158         float baseHeight;
159         float poleRadius;
160         float poleHeight;
161         float poleOffset;
162         float diskHeight;
163         float *diskPos;                         /* pre-computed disk positions on rods */
164         Disk *disk;
165         float speed;
166         GLint floorList;
167         GLint baseList;
168         GLint poleList;
169         GLfloat camera[3];
170         GLfloat centre[3];
171         rotator *the_rotator;
172         Bool button_down_p;
173         Bool texture;
174         GLuint textureNames[N_TEXTURES];
175         int drag_x;
176         int drag_y;
177 } glhcfg;
178
179 static glhcfg *glhanoi_cfg = NULL;
180 static glhcfg *glhanoi = NULL;
181 static Bool fog;
182 static Bool light;
183 static Bool texture;
184
185 static XrmOptionDescRec opts[] = {
186         {"-light", ".glhanoi.light", XrmoptionNoArg, (caddr_t) "true"},
187         {"+light", ".glhanoi.light", XrmoptionNoArg, (caddr_t) "false"},
188         {"-fog", ".glhanoi.fog", XrmoptionNoArg, (caddr_t) "true"},
189         {"+fog", ".glhanoi.fog", XrmoptionNoArg, (caddr_t) "false"},
190         {"-texture", ".glhanoi.texture", XrmoptionNoArg, (caddr_t) "true"},
191         {"+texture", ".glhanoi.texture", XrmoptionNoArg, (caddr_t) "false"}
192 };
193
194 static argtype vars[] = {
195         {(caddr_t *) (void *)&light, "light", "Light", DEF_LIGHT, t_Bool},
196         {(caddr_t *) (void *)&fog, "fog", "Fog", DEF_FOG, t_Bool},
197         {(caddr_t *) (void *)&texture, "texture", "Texture", DEF_TEXTURE,
198          t_Bool}
199 };
200
201 static OptionStruct desc[] = {
202         {"+/-light", "whether to light the scene"},
203         {"+/-fog", "whether to apply fog to the scene"},
204         {"+/-texture", "whether to apply texture to the scene"}
205 };
206
207 ModeSpecOpt glh_opts = { countof(opts), opts, countof(vars), vars, desc };
208
209 #ifdef USE_MODULES
210
211 ModStruct glhanoi_description = {
212         "glhanoi", "init_glhanoi", "draw_glhanoi", "release_glhanoi",
213         "draw_glhanoi", "init_glhanoi", NULL, &glhanoi_opts,
214         1000, 1, 2, 1, 4, 1.0, "",
215         "Towers of Hanoi", 0, NULL
216 };
217
218 #endif
219
220 static GLfloat cBlack[] = { 0.0, 0.0, 0.0, 1.0 };
221 static GLfloat cWhite[] = { 1.0, 1.0, 1.0, 1.0 };
222 static GLfloat poleColor[] = { 0.545, 0.137, 0.137 };
223 static GLfloat baseColor[] = { 0.34, 0.34, 0.48 };
224 static GLfloat fogcolor[] = { 0.5, 0.5, 0.5 };
225
226 static float left[] = { 1.0, 0.0, 0.0 };
227 static float up[] = { 0.0, 1.0, 0.0 };
228 static float front[] = { 0.0, 0.0, 1.0 };
229 static float right[] = { -1.0, 0.0, 0.0 };
230 static float down[] = { 0.0, -1.0, 0.0 };
231 static float back[] = { 0.0, 0.0, -1.0 };
232
233 GLfloat pos0[4] = { 50.0, 50.0, 50.0, 0.0 };
234 GLfloat amb0[4] = { 0.0, 0.0, 0.0, 1.0 };
235 GLfloat dif0[4] = { 1.0, 1.0, 1.0, 1.0 };
236 GLfloat spc0[4] = { 0.0, 1.0, 1.0, 1.0 };
237
238 GLfloat pos1[4] = { -50.0, 50.0, -50.0, 0.0 };
239 GLfloat amb1[4] = { 0.0, 0.0, 0.0, 1.0 };
240 GLfloat dif1[4] = { 1.0, 1.0, 1.0, 1.0 };
241 GLfloat spc1[4] = { 1.0, 1.0, 1.0, 1.0 };
242
243 float g = 3.0 * 9.80665;                /* hmm, looks like we need more gravity, Scotty... */
244
245 #define DOPUSH(X, Y)    (((X)->count) >= ((X)->size)) ? NULL : ((X)->data[(X)->count++] = (Y))
246 #define DOPOP(X)        (X)->count <= 0 ? NULL : ((X)->data[--((X)->count)])
247
248 Disk *push(int idx, Disk * d)
249 {
250         return DOPUSH(&glhanoi->pole[idx], d);
251 }
252
253 Disk *pop(int idx)
254 {
255         return DOPOP(&glhanoi->pole[idx]);
256 }
257
258 inline void swap(int *x, int *y)
259 {
260         *x = *x ^ *y;
261         *y = *x ^ *y;
262         *x = *x ^ *y;
263 }
264
265 /*
266  * magic - it's magic...
267  */
268 int magic(int i)
269 {
270         int count = 0;
271         if(i <= 1)
272                 return 0;
273         while((i & 0x01) == 0) {
274                 i >>= 1;
275                 count++;
276         }
277         return count % 2 == 0;
278 }
279
280 float distance(float *p0, float *p1)
281 {
282         float x, y, z;
283         x = p1[0] - p0[0];
284         y = p1[1] - p0[1];
285         z = p1[2] - p0[2];
286         return (float)sqrt(x * x + y * y + z * z);
287 }
288
289 GLfloat A(GLfloat a, GLfloat b, GLfloat c)
290 {
291         GLfloat sum = a + b;
292         return c / (a * b - 0.25 * sum * sum);
293 }
294
295 void moveSetup(Disk * disk)
296 {
297         float h, ymax;
298         float u;
299         int src = glhanoi->src;
300         int dst = glhanoi->dst;
301         GLfloat theta;
302         GLfloat sintheta, costheta;
303
304         if(glhanoi->state != FINISHED && random() % 6 == 0) {
305                 disk->rotAngle =
306                         -180.0 * (2 - 2 * random() % 2) * (random() % 3 + 1);
307         } else {
308                 disk->rotAngle = -180.0;
309         }
310
311         disk->base0 = glhanoi->diskPos[glhanoi->pole[src].count];
312         disk->base1 =
313                 glhanoi->state ==
314                 FINISHED ? disk->base0 : glhanoi->diskPos[glhanoi->pole[dst].
315                                                                                                   count];
316
317         disk->xmin = glhanoi->poleOffset * (src - 1);
318         disk->xmax = glhanoi->poleOffset * (dst - 1);
319         disk->ymin = glhanoi->poleHeight;
320         ymax =
321                 glhanoi->poleHeight + fabs(disk->xmax -
322                                                                    disk->xmin) * (glhanoi->state ==
323                                                                                                   FINISHED ? 1.0 +
324                                                                                                   (double)(glhanoi->
325                                                                                                                    numberOfDisks -
326                                                                                                                    disk->id) /
327                                                                                                   (double)glhanoi->
328                                                                                                   numberOfDisks : 1.0);
329
330         h = ymax - disk->ymin;
331         theta = atan((disk->xmin - disk->xmax) * A(disk->xmin, disk->xmax, h));
332         if(theta < 0.0)
333                 theta += M_PI;
334         costheta = cos(theta);
335         sintheta = sin(theta);
336         u = (float)
337                 sqrt(fabs
338                          (-g /
339                           (2.0 * A(disk->xmin, disk->xmax, h) * costheta * costheta)));
340         disk->usintheta = u * sintheta;
341         disk->ucostheta = u * costheta;
342         disk->t1 =
343                 (-u + sqrt(u * u + 2.0 * g * fabs(disk->ymin - disk->base0))) / g;
344         disk->u1 = u + g * disk->t1;
345         disk->t2 = 2.0 * disk->usintheta / g;
346         disk->u2 = disk->usintheta - g * disk->t2;
347 }
348
349 void makeMove(void)
350 {
351         int fudge = glhanoi->move + 2;
352         int magicNumber = magic(fudge);
353
354         glhanoi->currentDisk = pop(glhanoi->src);
355         moveSetup(glhanoi->currentDisk);
356         push(glhanoi->dst, glhanoi->currentDisk);
357         fudge = fudge % 2;
358
359         if(fudge == 1 || magicNumber) {
360                 swap(&glhanoi->src, &glhanoi->tmp);
361         }
362         if(fudge == 0 || glhanoi->magicNumber) {
363                 swap(&glhanoi->dst, &glhanoi->tmp);
364         }
365         glhanoi->magicNumber = magicNumber;
366 }
367
368 double lerp(double alpha, double start, double end)
369 {
370         return start + alpha * (end - start);
371 }
372
373 void upfunc(GLdouble t, Disk * d)
374 {
375         d->position[0] = d->xmin;
376         d->position[1] = d->base0 + (d->u1 - 0.5 * g * t) * t;
377
378         d->rotation[1] = 0.0;
379 }
380
381 void parafunc(GLdouble t, Disk * d)
382 {
383         d->position[0] = d->xmin + d->ucostheta * t;
384         d->position[1] = d->ymin + (d->usintheta - 0.5 * g * t) * t;
385
386         d->rotation[1] =
387                 d->rotAngle * (d->position[0] - d->xmin) / (d->xmax - d->xmin);
388 }
389
390 void downfunc(GLdouble t, Disk * d)
391 {
392         d->position[0] = d->xmax;
393         d->position[1] = d->ymin + (d->u2 - 0.5 * g * t) * t;
394
395         d->rotation[1] = 0.0;
396 }
397
398 Bool computePosition(GLfloat t, Disk * d)
399 {
400         Bool finished = FALSE;
401
402         if(t < d->t1) {
403                 upfunc(t, d);
404         } else if(t < d->t1 + d->t2) {
405                 parafunc(t - d->t1, d);
406         } else {
407                 downfunc(t - d->t1 - d->t2, d);
408                 if(d->position[1] <= d->base1) {
409                         d->position[1] = d->base1;
410                         finished = TRUE;
411                 }
412         }
413         return finished;
414 }
415
416 void updateView(void)
417 {
418         double longitude, latitude, radius;
419         double a, b, c, A, B;
420
421         get_position(glhanoi->the_rotator, NULL, NULL, &radius,
422                                  !glhanoi->button_down_p);
423         get_rotation(glhanoi->the_rotator, &longitude, &latitude, NULL,
424                                  !glhanoi->button_down_p);
425         longitude += glhanoi->camera[0];
426         latitude += glhanoi->camera[1];
427         radius += glhanoi->camera[2];
428         longitude = longitude - floor(longitude);
429         latitude = latitude - floor(latitude);
430         radius = radius - floor(radius);
431         if(latitude > 0.5) {
432                 latitude = 1.0 - latitude;
433         }
434         if(radius > 0.5) {
435                 radius = 1.0 - radius;
436         }
437
438         b = glhanoi->centre[1];
439         c = (MIN_CAMERA_RADIUS +
440                  radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS));
441         A = M_PI / 4.0 * (1.0 - latitude);
442         a = sqrt(b * b + c * c - 2.0 * b * c * cos(A));
443         B = asin(sin(A) * b / a);
444         glRotatef(-B * 180 / M_PI, 1.0, 0.0, 0.0);
445
446         glTranslatef(0.0f, 0.0f,
447                                  -(MIN_CAMERA_RADIUS +
448                                    radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS)));
449         glRotatef(longitude * 360.0, 0.0f, 1.0f, 0.0f);
450         glRotatef(latitude * 180.0, cos(longitude * 2.0 * M_PI), 0.0,
451                           sin(longitude * 2.0 * M_PI));
452 }
453
454 void changeState(State state)
455 {
456         glhanoi->state = state;
457         glhanoi->startTime = getTime();
458 }
459
460 void update_glhanoi(void)
461 {
462         double t = getTime() - glhanoi->startTime;
463         int i;
464         Bool done;
465
466         switch (glhanoi->state) {
467         case START:
468                 if(t < glhanoi->duration) {
469                         break;
470                 }
471                 glhanoi->move = 0;
472                 if(glhanoi->numberOfDisks % 2 == 0) {
473                         swap(&glhanoi->tmp, &glhanoi->dst);
474                 }
475                 glhanoi->magicNumber = 1;
476                 makeMove();
477                 changeState(MOVE_DISK);
478                 break;
479
480         case MOVE_DISK:
481                 if(computePosition(t, glhanoi->currentDisk)) {
482                         changeState(MOVE_FINISHED);
483                 }
484                 break;
485
486         case MOVE_FINISHED:
487                 if(++glhanoi->move < glhanoi->numberOfMoves) {
488                         makeMove();
489                         changeState(MOVE_DISK);
490                 } else {
491                         glhanoi->duration = FINISH_DURATION;
492                         changeState(FINISHED);
493                 }
494                 break;
495
496         case FINISHED:
497                 while(t < glhanoi->duration) {
498                         break;
499                 }
500                 glhanoi->src = glhanoi->olddst;
501                 glhanoi->dst = glhanoi->oldsrc;
502                 for(i = 0; i < glhanoi->numberOfDisks; ++i) {
503                         Disk *disk = pop(glhanoi->src);
504                         assert(disk != NULL);
505                         moveSetup(disk);
506                 }
507                 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
508                         push(glhanoi->dst, &glhanoi->disk[i]);
509                 }
510                 changeState(MONEY_SHOT);
511                 break;
512
513         case MONEY_SHOT:
514                 done = True;
515                 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
516                         double delay = 0.25 * i;
517                         int finished;
518
519                         if(t - delay < 0) {
520                                 done = False;
521                                 continue;
522                         }
523
524                         finished = computePosition(t - delay, &glhanoi->disk[i]);
525                         glhanoi->disk[i].rotation[1] = 0.0;
526
527                         if(!finished) {
528                                 done = False;
529                         }
530                 }
531                 if(done) {
532                         glhanoi->src = glhanoi->oldsrc;
533                         glhanoi->tmp = glhanoi->oldtmp;
534                         glhanoi->dst = glhanoi->olddst;
535                         changeState(START);
536                 }
537                 break;
538
539         case INVALID:
540         default:
541                 fprintf(stderr, "Invalid state\n");
542                 break;
543         }
544 }
545
546 void HSVtoRGBf(GLfloat h, GLfloat s, GLfloat v,
547                            GLfloat * r, GLfloat * g, GLfloat * b)
548 {
549         if(s == 0.0) {
550                 *r = v;
551                 *g = v;
552                 *b = v;
553         } else {
554                 GLfloat i, f, p, q, t;
555                 if(h >= 360.0) {
556                         h = 0.0;
557                 }
558                 h /= 60.0;                              /* h now in [0,6). */
559                 i = floor((double)h);   /* i now largest integer <= h */
560                 f = h - i;                              /* f is no fractional part of h */
561                 p = v * (1.0 - s);
562                 q = v * (1.0 - (s * f));
563                 t = v * (1.0 - (s * (1.0 - f)));
564                 switch ((int)i) {
565                 case 0:
566                         *r = v;
567                         *g = t;
568                         *b = p;
569                         break;
570                 case 1:
571                         *r = q;
572                         *g = v;
573                         *b = p;
574                         break;
575                 case 2:
576                         *r = p;
577                         *g = v;
578                         *b = t;
579                         break;
580                 case 3:
581                         *r = p;
582                         *g = q;
583                         *b = v;
584                         break;
585                 case 4:
586                         *r = t;
587                         *g = p;
588                         *b = v;
589                         break;
590                 case 5:
591                         *r = v;
592                         *g = p;
593                         *b = q;
594                         break;
595                 }
596         }
597 }
598
599 void HSVtoRGBv(GLfloat * hsv, GLfloat * rgb)
600 {
601         HSVtoRGBf(hsv[0], hsv[1], hsv[2], &rgb[0], &rgb[1], &rgb[2]);
602 }
603
604 void setMaterial(GLfloat color[3], GLfloat hlite[3], int shininess)
605 {
606         glColor3fv(color);
607         glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
608         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
609         glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
610 }
611
612 /*
613  * drawTube: I know all this stuff is available in gluQuadrics
614  * but I'd originally intended to texture the poles with a 3D wood
615  * texture, but I was having difficulty getting wood... what? Why
616  * are all you Amercians laughing..? Anyway, I don't know if enough
617  * people's hardware supports 3D textures, so I didn't bother (xorg
618  * ATI server doesn't :-( )
619  */
620 void drawTube(GLdouble bottomRadius, GLdouble topRadius,
621                           GLdouble bottomThickness, GLdouble topThickness,
622                           GLdouble height, GLuint nSlice, GLuint nLoop)
623 {
624         GLfloat y;
625         GLfloat *cosCache = malloc(sizeof(GLfloat) * nSlice);
626         GLfloat *sinCache = malloc(sizeof(GLfloat) * nSlice);
627         GLint slice;
628         GLuint loop;
629         GLint lastSlice = nSlice - 1;
630         GLfloat radius;
631         GLfloat innerRadius;
632         GLfloat maxRadius;
633
634         if(bottomThickness > bottomRadius) {
635                 bottomThickness = bottomRadius;
636         }
637         if(topThickness > topRadius) {
638                 topThickness = topRadius;
639         }
640         if(bottomThickness < 0.0) {
641                 bottomThickness = 0.0;
642         }
643         if(topThickness < 0.0) {
644                 topThickness = 0.0;
645         }
646         if(topRadius >= bottomRadius) {
647                 maxRadius = topRadius;
648         } else {
649                 maxRadius = bottomRadius;
650         }
651
652         /* bottom */
653         y = 0.0;
654         radius = bottomRadius;
655         innerRadius = bottomRadius - bottomThickness;
656         /* innerTexCoordSize = texCoordSize * innerRadius / maxRadius; */
657         /* outerTexCoordSize = texCoordSize * radius / maxRadius; */
658         /* yTexCoord = minTexCoord; */
659
660         glBegin(GL_QUAD_STRIP);
661
662         glNormal3f(0.0, -1.0, 0.0);
663
664         /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + innerTexCoordSize); */
665         glVertex3f(0.0, y, innerRadius);
666
667         /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + outerTexCoordSize); */
668         glVertex3f(0.0, y, radius);
669
670         for(slice = lastSlice; slice >= 0; --slice) {
671                 GLfloat theta = 2.0 * M_PI * (double)slice / nSlice;
672
673                 cosCache[slice] = cos(theta);
674                 sinCache[slice] = sin(theta);
675
676                 /* glTexCoord3f(midTexCoord + sinCache[slice] * innerTexCoordSize, */
677                 /* yTexCoord, */
678                 /* midTexCoord + cosCache[slice] * innerTexCoordSize); */
679                 glVertex3f(innerRadius * sinCache[slice], y,
680                                    innerRadius * cosCache[slice]);
681                 /* glTexCoord3f(midTexCoord + sinCache[slice] * outerTexCoordSize, */
682                 /* yTexCoord, */
683                 /* midTexCoord + cosCache[slice] * outerTexCoordSize); */
684                 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
685         }
686         glEnd();
687
688         /* middle */
689         for(loop = 0; loop < nLoop; ++loop) {
690                 GLfloat lowerRadius =
691                         bottomRadius + (topRadius -
692                                                         bottomRadius) * (float)loop / (nLoop);
693                 GLfloat upperRadius =
694                         bottomRadius + (topRadius - bottomRadius) * (float)(loop +
695                                                                                                                                 1) /
696                         (nLoop);
697                 GLfloat lowerY = height * (float)loop / (nLoop);
698                 GLfloat upperY = height * (float)(loop + 1) / (nLoop);
699                 GLfloat factor = (topRadius - topThickness) -
700                         (bottomRadius - bottomThickness);
701
702                 /* outside */
703                 glBegin(GL_QUAD_STRIP);
704                 for(slice = 0; slice < nSlice; ++slice) {
705                         glNormal3f(sinCache[slice], 0.0, cosCache[slice]);
706                         glVertex3f(upperRadius * sinCache[slice], upperY,
707                                            upperRadius * cosCache[slice]);
708                         glVertex3f(lowerRadius * sinCache[slice], lowerY,
709                                            lowerRadius * cosCache[slice]);
710                 }
711                 glNormal3f(0.0, 0.0, 1.0);
712                 glVertex3f(0.0, upperY, upperRadius);
713                 glVertex3f(0.0, lowerY, lowerRadius);
714                 glEnd();
715
716                 /* inside */
717                 lowerRadius = bottomRadius - bottomThickness +
718                         factor * (float)loop / (nLoop);
719                 upperRadius = bottomRadius - bottomThickness +
720                         factor * (float)(loop + 1) / (nLoop);
721
722                 glBegin(GL_QUAD_STRIP);
723                 glNormal3f(0.0, 0.0, -1.0);
724                 glVertex3f(0.0, upperY, upperRadius);
725                 glVertex3f(0.0, lowerY, lowerRadius);
726                 for(slice = lastSlice; slice >= 0; --slice) {
727                         glNormal3f(-sinCache[slice], 0.0, -cosCache[slice]);
728                         glVertex3f(upperRadius * sinCache[slice], upperY,
729                                            upperRadius * cosCache[slice]);
730                         glVertex3f(lowerRadius * sinCache[slice], lowerY,
731                                            lowerRadius * cosCache[slice]);
732                 }
733                 glEnd();
734         }
735
736         /* top */
737         y = height;
738         radius = topRadius;
739         innerRadius = topRadius - topThickness;
740
741         glBegin(GL_QUAD_STRIP);
742         glNormal3f(0.0, 1.0, 0.0);
743         for(slice = 0; slice < nSlice; ++slice) {
744                 glVertex3f(innerRadius * sinCache[slice], y,
745                                    innerRadius * cosCache[slice]);
746
747                 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
748         }
749         glVertex3f(0.0, y, innerRadius);
750         glVertex3f(0.0, y, radius);
751         glEnd();
752 }
753
754 void drawPole(GLfloat radius, GLfloat length)
755 {
756         drawTube(radius, radius, radius, radius, length, NSLICE, NLOOPS);
757 }
758
759 void drawDisk3D(GLdouble inner_radius, GLdouble outer_radius,
760                                 GLdouble height)
761 {
762         drawTube(outer_radius, outer_radius, outer_radius - inner_radius,
763                          outer_radius - inner_radius, height, NSLICE, NLOOPS);
764 }
765
766 void drawCuboid(GLfloat length, GLfloat width, GLfloat height)
767 {
768         GLfloat xmin = -length / 2.0f;
769         GLfloat xmax = length / 2.0f;
770         GLfloat zmin = -width / 2.0f;
771         GLfloat zmax = width / 2.0f;
772         GLfloat ymin = 0.0f;
773         GLfloat ymax = height;
774
775         glBegin(GL_QUADS);
776         /* front */
777         glNormal3fv(front);
778         glVertex3f(xmin, ymin, zmax);   /* 0 */
779         glVertex3f(xmax, ymin, zmax);   /* 1 */
780         glVertex3f(xmax, ymax, zmax);   /* 2 */
781         glVertex3f(xmin, ymax, zmax);   /* 3 */
782         /* right */
783         glNormal3fv(right);
784         glVertex3f(xmax, ymin, zmax);   /* 1 */
785         glVertex3f(xmax, ymin, zmin);   /* 5 */
786         glVertex3f(xmax, ymax, zmin);   /* 6 */
787         glVertex3f(xmax, ymax, zmax);   /* 2 */
788         /* back */
789         glNormal3fv(back);
790         glVertex3f(xmax, ymin, zmin);   /* 5 */
791         glVertex3f(xmin, ymin, zmin);   /* 4 */
792         glVertex3f(xmin, ymax, zmin);   /* 7 */
793         glVertex3f(xmax, ymax, zmin);   /* 6 */
794         /* left */
795         glNormal3fv(left);
796         glVertex3f(xmin, ymin, zmin);   /* 4 */
797         glVertex3f(xmin, ymin, zmax);   /* 0 */
798         glVertex3f(xmin, ymax, zmax);   /* 3 */
799         glVertex3f(xmin, ymax, zmin);   /* 7 */
800         /* top */
801         glNormal3fv(up);
802         glVertex3f(xmin, ymax, zmax);   /* 3 */
803         glVertex3f(xmax, ymax, zmax);   /* 2 */
804         glVertex3f(xmax, ymax, zmin);   /* 6 */
805         glVertex3f(xmin, ymax, zmin);   /* 7 */
806         /* bottom */
807         glNormal3fv(down);
808         glVertex3f(xmin, ymin, zmin);   /* 4 */
809         glVertex3f(xmax, ymin, zmin);   /* 5 */
810         glVertex3f(xmax, ymin, zmax);   /* 1 */
811         glVertex3f(xmin, ymin, zmax);   /* 0 */
812         glEnd();
813 }
814
815 void drawDisks(void)
816 {
817         int i;
818
819         glPushMatrix();
820         glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
821         for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
822                 Disk *disk = &glhanoi->disk[i];
823                 GLfloat *pos = disk->position;
824                 GLfloat *rot = disk->rotation;
825
826                 glPushMatrix();
827                 glTranslatef(pos[0], pos[1], pos[2]);
828                 if(rot[1] != 0.0) {
829                         glTranslatef(0.0, glhanoi->diskHeight / 2.0, 0.0);
830                         glRotatef(rot[1], 0.0, 0.0, 1.0);
831                         glTranslatef(0.0, -glhanoi->diskHeight / 2.0, 0.0);
832                 }
833                 glCallList(disk->displayList);
834                 glPopMatrix();
835         }
836         glPopMatrix();
837 }
838
839 GLfloat getDiskRadius(int i)
840 {
841         return ((GLfloat) i + 3.0) * glhanoi->poleRadius;
842 }
843
844 void initData(void)
845 {
846         GLfloat maxDiskRadius;
847         int i;
848
849         glhanoi->baseLength = BASE_LENGTH;
850         glhanoi->poleRadius = glhanoi->baseLength /
851                 (2.0 * (3 * glhanoi->numberOfDisks + 7.0));
852         maxDiskRadius = getDiskRadius(glhanoi->numberOfDisks);
853         glhanoi->baseWidth = 2.0 * maxDiskRadius;
854         glhanoi->poleOffset = 2.0 * getDiskRadius(glhanoi->maxDiskIdx);
855         glhanoi->diskHeight = 2.0 * glhanoi->poleRadius;
856         glhanoi->baseHeight = 2.0 * glhanoi->poleRadius;
857         glhanoi->poleHeight = glhanoi->numberOfDisks *
858                 glhanoi->diskHeight + glhanoi->poleRadius;
859         glhanoi->numberOfMoves = (1 << glhanoi->numberOfDisks) - 1;
860         glhanoi->boardSize = glhanoi->baseLength * 0.5 * (1.0 + sqrt(5.0));
861
862         for(i = 0; i < 3; i++) {
863                 if((glhanoi->pole[i].data =
864                         calloc(glhanoi->numberOfDisks, sizeof(Disk *))) == NULL) {
865                         fprintf(stderr, "%s: out of memory creating stack %d\n",
866                                         progname, i);
867                         exit(1);
868                 }
869                 glhanoi->pole[i].size = glhanoi->numberOfDisks;
870         }
871         if((glhanoi->diskPos =
872                 calloc(glhanoi->numberOfDisks, sizeof(double))) == NULL) {
873                 fprintf(stderr, "%s: out of memory creating diskPos\n", progname);
874                 exit(1);
875         }
876
877         glhanoi->the_rotator = make_rotator(0.1, 0.025, 0, 1, 0.005, False);
878         glhanoi->button_down_p = False;
879
880         glhanoi->src = glhanoi->oldsrc = 0;
881         glhanoi->tmp = glhanoi->oldtmp = 1;
882         glhanoi->dst = glhanoi->olddst = 2;
883 }
884
885 void initView(void)
886 {
887         glhanoi->camera[0] = 0.0;
888         glhanoi->camera[1] = 0.0;
889         glhanoi->camera[2] = 0.0;
890         glhanoi->centre[0] = 0.0;
891         glhanoi->centre[1] = glhanoi->poleHeight * 3.0;
892         glhanoi->centre[2] = 0.0;
893 }
894
895 /*
896  * noise_improved.c - based on ImprovedNoise.java
897  * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
898  */
899 static double fade(double t)
900 {
901         return t * t * t * (t * (t * 6 - 15) + 10);
902 }
903
904 static double grad(int hash, double x, double y, double z)
905 {
906         int h = hash & 15;                      /* CONVERT LO 4 BITS OF HASH CODE */
907         double u = h < 8 ? x : y,       /* INTO 12 GRADIENT DIRECTIONS. */
908                 v = h < 4 ? y : h == 12 || h == 14 ? x : z;
909         return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
910 }
911
912 static int p[512], permutation[] = { 151, 160, 137, 91, 90, 15,
913         131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
914         8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26,
915         197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
916         149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
917         134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60,
918         211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143,
919         54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
920         18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198,
921         173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118,
922         126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
923         28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
924         221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108,
925         110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251,
926         34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145,
927         235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
928         84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
929         93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
930         156, 180
931 };
932
933 static void initNoise(void)
934 {
935         int i;
936         for(i = 0; i < 256; i++)
937                 p[256 + i] = p[i] = permutation[i];
938 }
939
940 double improved_noise(double x, double y, double z)
941 {
942         double u, v, w;
943         int A, AA, AB, B, BA, BB;
944         int X = (int)floor(x) & 255,    /* FIND UNIT CUBE THAT */
945                 Y = (int)floor(y) & 255,        /* CONTAINS POINT. */
946                 Z = (int)floor(z) & 255;
947         static int start = 0;
948         if(start == 0) {
949                 initNoise();
950                 start = 1;
951         }
952         x -= floor(x);                          /* FIND RELATIVE X,Y,Z */
953         y -= floor(y);                          /* OF POINT IN CUBE. */
954         z -= floor(z);
955         u = fade(x),                            /* COMPUTE FADE CURVES */
956                 v = fade(y),                    /* FOR EACH OF X,Y,Z. */
957                 w = fade(z);
958         A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, /* HASH COORDINATES OF */
959                 B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;     /* THE 8 CUBE CORNERS, */
960         return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),    /* AND ADD */
961                                                                 grad(p[BA], x - 1, y, z)),      /* BLENDED */
962                                                 lerp(u, grad(p[AB], x, y - 1, z),       /* RESULTS */
963                                                          grad(p[BB], x - 1, y - 1, z))),        /* FROM 8 CORNERS */
964                                 lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)),        /* OF CUBE */
965                                          lerp(u, grad(p[AB + 1], x, y - 1, z - 1),
966                                                   grad(p[BB + 1], x - 1, y - 1, z - 1))));
967 }
968
969 /*
970  * end noise_improved.c - based on ImprovedNoise.java
971  */
972
973 struct tex_col_t {
974         GLuint *colours;
975         /* GLfloat *points; */
976         unsigned int ncols;
977 };
978 typedef struct tex_col_t tex_col_t;
979
980 GLubyte *makeTexture(int x_size, int y_size, int z_size,
981                                          GLuint(*texFunc) (double, double, double,
982                                                                            tex_col_t *), tex_col_t * colours)
983 {
984         int i, j, k;
985         GLubyte *textureData;
986         GLuint *texturePtr;
987         double x, y, z;
988         double xi, yi, zi;
989
990         if((textureData =
991                 calloc(x_size * y_size * z_size, sizeof(GLuint))) == NULL) {
992                 return NULL;
993         }
994
995         xi = 1.0 / x_size;
996         yi = 1.0 / y_size;
997         zi = 1.0 / z_size;
998
999         z = 0.0;
1000         texturePtr = (void *)textureData;
1001         for(k = 0; k < z_size; k++, z += zi) {
1002                 y = 0.0;
1003                 for(j = 0; j < y_size; j++, y += yi) {
1004                         x = 0.0;
1005                         for(i = 0; i < x_size; i++, x += xi) {
1006                                 *texturePtr = texFunc(x, y, z, colours);
1007                                 ++texturePtr;
1008                         }
1009                 }
1010         }
1011         return textureData;
1012 }
1013
1014 tex_col_t makeMarbleColours(void)
1015 {
1016         tex_col_t marbleColours;
1017         int ncols = 2;
1018
1019         marbleColours.colours = calloc(sizeof(GLuint), ncols);
1020         marbleColours.ncols = ncols;
1021
1022         marbleColours.colours[0] = 0x3f3f3f3f;
1023         marbleColours.colours[1] = 0xffffffff;
1024
1025         return marbleColours;
1026 }
1027
1028 double turb(double x, double y, double z, int octaves)
1029 {
1030         int oct, freq = 1;
1031         double r = 0.0;
1032
1033         for(oct = 0; oct < octaves; ++oct) {
1034                 r += fabs(improved_noise(freq * x, freq * y, freq * z)) / freq;
1035                 freq <<= 1;
1036         }
1037         return r / 2.0;
1038 }
1039
1040 void perturb(double *x, double *y, double *z, double scale)
1041 {
1042         double t = scale * turb(*x, *y, *z, 4);
1043         *x += t;
1044         *y += t;
1045         *z += t;
1046 }
1047
1048 double f_m(double x, double y, double z)
1049 {
1050         return sin(3.0 * M_PI * x);
1051 }
1052
1053 GLuint C_m(double x, const tex_col_t * tex_cols)
1054 {
1055         int r = tex_cols->colours[0] & 0xff;
1056         int g = tex_cols->colours[0] >> 8 & 0xff;
1057         int b = tex_cols->colours[0] >> 16 & 0xff;
1058         double factor;
1059         int r1, g1, b1;
1060         x = x - floor(x);
1061
1062         factor = (1.0 + sin(2.0 * M_PI * x)) / 2.0;
1063
1064         r1 = (tex_cols->colours[1] & 0xff);
1065         g1 = (tex_cols->colours[1] >> 8 & 0xff);
1066         b1 = (tex_cols->colours[1] >> 16 & 0xff);
1067
1068         r += (int)(factor * (r1 - r));
1069         g += (int)(factor * (g1 - g));
1070         b += (int)(factor * (b1 - b));
1071
1072         return 0xff000000 | (b << 16) | (g << 8) | r;
1073 }
1074
1075
1076 GLuint makeMarbleTexture(double x, double y, double z, tex_col_t * colours)
1077 {
1078         perturb(&x, &y, &z, MARBLE_SCALE);
1079         return C_m(f_m(x, y, z), colours);
1080 }
1081
1082 void setTexture(int n)
1083 {
1084         glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[n]);
1085 }
1086
1087 int makeTextures(void)
1088 {
1089         GLubyte *marbleTexture;
1090         tex_col_t marbleColours;
1091
1092         glGenTextures(N_TEXTURES, glhanoi->textureNames);
1093
1094         marbleColours = makeMarbleColours();
1095         if((marbleTexture =
1096                 makeTexture(MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 1,
1097                                         makeMarbleTexture, &marbleColours)) == NULL) {
1098                 return 1;
1099         }
1100
1101         glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[0]);
1102         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1103         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1104         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1105         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1106         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1107                                  MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 0,
1108                                  GL_RGBA, GL_UNSIGNED_BYTE, marbleTexture);
1109         free(marbleTexture);
1110
1111         return 0;
1112 }
1113
1114 void initFloor(void)
1115 {
1116         int i, j;
1117         float tileSize = glhanoi->boardSize / BOARD_SQUARES;
1118         float x0, x1, z0, z1;
1119         float tx0, tx1, tz0, tz1;
1120         float *col = cWhite;
1121         float texIncr = 1.0 / BOARD_SQUARES;
1122
1123         if((glhanoi->floorList = glGenLists(1)) == 0) {
1124                 fprintf(stderr, "can't allocate memory for floor display list\n");
1125                 exit(EXIT_FAILURE);
1126         }
1127         glNewList(glhanoi->floorList, GL_COMPILE);
1128         x0 = -glhanoi->boardSize / 2.0;
1129         tx0 = 0.0f;
1130         setMaterial(col, cWhite, 128);
1131         setTexture(0);
1132         glNormal3fv(up);
1133         for(i = 0; i < BOARD_SQUARES; i++, x0 += tileSize, tx0 += texIncr) {
1134                 x1 = x0 + tileSize;
1135                 tx1 = tx0 + texIncr;
1136                 z0 = -glhanoi->boardSize / 2.0;
1137                 tz0 = 0.0f;
1138                 for(j = 0; j < BOARD_SQUARES; j++, z0 += tileSize, tz0 += texIncr) {
1139                         int colIndex = (i + j) & 0x1;
1140
1141                         z1 = z0 + tileSize;
1142                         tz1 = tz0 + texIncr;
1143
1144                         if(colIndex)
1145                                 col = cWhite;
1146                         else
1147                                 col = cBlack;
1148
1149                         setMaterial(col, cWhite, 100);
1150
1151                         glBegin(GL_QUADS);
1152
1153                         glTexCoord2d(tx0, tz0);
1154                         glVertex3f(x0, 0.0, z0);
1155
1156                         glTexCoord2d(tx0, tz1);
1157                         glVertex3f(x0, 0.0, z1);
1158
1159                         glTexCoord2d(tx1, tz1);
1160                         glVertex3f(x1, 0.0, z1);
1161
1162                         glTexCoord2d(tx1, tz0);
1163                         glVertex3f(x1, 0.0, z0);
1164                         glEnd();
1165                 }
1166         }
1167         glEndList();
1168 }
1169
1170 void initTowers(void)
1171 {
1172         if((glhanoi->baseList = glGenLists(1)) == 0) {
1173                 fprintf(stderr, "can't allocate memory for towers display list\n");
1174                 exit(EXIT_FAILURE);
1175         }
1176         glNewList(glhanoi->baseList, GL_COMPILE);
1177         setMaterial(baseColor, cWhite, 50);
1178         drawCuboid(glhanoi->baseLength, glhanoi->baseWidth,
1179                            glhanoi->baseHeight);
1180         glEndList();
1181
1182
1183         if((glhanoi->poleList = glGenLists(1)) == 0) {
1184                 fprintf(stderr, "can't allocate memory for towers display list\n");
1185                 exit(EXIT_FAILURE);
1186         }
1187         glNewList(glhanoi->poleList, GL_COMPILE);
1188         glPushMatrix();
1189         glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
1190         setMaterial(poleColor, cWhite, 50);
1191         drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1192         glPushMatrix();
1193         glTranslatef(-glhanoi->poleOffset, 0.0, 0.0);
1194         drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1195         glPopMatrix();
1196         glTranslatef(glhanoi->poleOffset, 0.0, 0.0);
1197         drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1198         glPopMatrix();
1199         glEndList();
1200 }
1201
1202 double cfunc(double x)
1203 {
1204 #define COMP <
1205         if(x < 2.0 / 7.0) {
1206                 return (1.0 / 12.0) / (1.0 / 7.0) * x;
1207         }
1208         if(x < 3.0 / 7.0) {
1209                 return (1.0 + 1.0 / 6.0) * x - 1.0 / 6.0;
1210         }
1211         if(x < 4.0 / 7.0) {
1212                 return (2.0 + 1.0 / 3.0) * x - 2.0 / 3.0;
1213         }
1214         if(x < 5.0 / 7.0) {
1215                 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1216         }
1217         return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1218 }
1219
1220 void initDisks(void)
1221 {
1222         int i;
1223         if((glhanoi->disk =
1224                 (Disk *) calloc(glhanoi->numberOfDisks, sizeof(Disk))) == NULL) {
1225                 perror("initDisks");
1226                 exit(EXIT_FAILURE);
1227         }
1228
1229         for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
1230                 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1231                 double f = cfunc((GLfloat) i / (GLfloat) glhanoi->numberOfDisks);
1232                 GLfloat diskColor = f * 360.0;
1233                 GLfloat color[3];
1234                 Disk *disk = &glhanoi->disk[i];
1235
1236                 disk->id = i;
1237                 disk->position[0] = -glhanoi->poleOffset;
1238                 disk->position[1] = glhanoi->diskHeight * height;
1239                 disk->position[2] = 0.0;
1240                 disk->rotation[0] = 0.0;
1241                 disk->rotation[1] = 0.0;
1242                 disk->rotation[2] = 0.0;
1243
1244                 color[0] = diskColor;
1245                 color[1] = 1.0f;
1246                 color[2] = 1.0f;
1247                 HSVtoRGBv(color, color);
1248
1249                 if((disk->displayList = glGenLists(1)) == 0) {
1250                         fprintf(stderr,
1251                                         "can't allocate memory for disk %d display list\n", i);
1252                         exit(EXIT_FAILURE);
1253                 }
1254                 glNewList(disk->displayList, GL_COMPILE);
1255                 setMaterial(color, cWhite, 100.0);
1256                 drawDisk3D(glhanoi->poleRadius, getDiskRadius(i),
1257                                    glhanoi->diskHeight);
1258                 glEndList();
1259         }
1260         for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
1261                 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1262                 int h = glhanoi->maxDiskIdx - i;
1263                 glhanoi->diskPos[h] = glhanoi->diskHeight * height;
1264                 push(glhanoi->src, &glhanoi->disk[i]);
1265         }
1266 }
1267
1268 void initLights(Bool state)
1269 {
1270         if(state) {
1271                 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1272                 glLightfv(GL_LIGHT0, GL_AMBIENT, amb0);
1273                 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
1274                 glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1275
1276                 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1277                 glLightfv(GL_LIGHT1, GL_AMBIENT, amb1);
1278                 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
1279                 glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1280
1281                 glEnable(GL_LIGHTING);
1282                 glEnable(GL_LIGHT0);
1283                 glEnable(GL_LIGHT1);
1284         } else {
1285                 glDisable(GL_LIGHTING);
1286         }
1287 }
1288
1289 void drawFloor(void)
1290 {
1291         glCallList(glhanoi->floorList);
1292 }
1293
1294 void drawTowers(void)
1295 {
1296         glCallList(glhanoi->baseList);
1297         glCallList(glhanoi->poleList);
1298 }
1299
1300 /* Window management, etc
1301  */
1302 void reshape_glhanoi(ModeInfo * mi, int width, int height)
1303 {
1304         glViewport(0, 0, (GLint) width, (GLint) height);
1305
1306         glMatrixMode(GL_PROJECTION);
1307         glLoadIdentity();
1308         gluPerspective(30.0, (GLdouble) width / (GLdouble) height, 1.0,
1309                                    2 * MAX_CAMERA_RADIUS);
1310
1311         glMatrixMode(GL_MODELVIEW);
1312         glLoadIdentity();
1313
1314         glClear(GL_COLOR_BUFFER_BIT);
1315 }
1316
1317 void init_glhanoi(ModeInfo * mi)
1318 {
1319         if(!glhanoi_cfg) {
1320                 glhanoi_cfg =
1321                         (glhcfg *) calloc(MI_NUM_SCREENS(mi), sizeof(glhcfg));
1322                 if(!glhanoi_cfg) {
1323                         fprintf(stderr, "%s: out of memory creating configs\n",
1324                                         progname);
1325                         exit(1);
1326                 }
1327                 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1328         }
1329
1330         glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1331         glhanoi->glx_context = init_GL(mi);
1332         glhanoi->numberOfDisks = MI_BATCHCOUNT(mi);
1333         glhanoi->maxDiskIdx = glhanoi->numberOfDisks - 1;
1334         glhanoi->wire = MI_IS_WIREFRAME(mi);
1335         glhanoi->light = light;
1336         glhanoi->fog = fog;
1337         glhanoi->texture = texture;
1338
1339         reshape_glhanoi(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1340
1341         if(glhanoi->wire) {
1342                 glhanoi->light = FALSE;
1343                 glhanoi->fog = FALSE;
1344                 glhanoi->texture = FALSE;
1345         }
1346
1347         initLights(!glhanoi->wire && glhanoi->light);
1348         if(makeTextures() != 0) {
1349                 fprintf(stderr, "can't allocate memory for marble texture\n");
1350                 exit(EXIT_FAILURE);
1351         }
1352
1353         initData();
1354         initView();
1355         initFloor();
1356         initTowers();
1357         initDisks();
1358
1359         glEnable(GL_DEPTH_TEST);
1360         glEnable(GL_NORMALIZE);
1361         glEnable(GL_CULL_FACE);
1362         glShadeModel(GL_SMOOTH);
1363         if(glhanoi->fog) {
1364                 glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], 1.0);
1365                 glFogi(GL_FOG_MODE, GL_LINEAR);
1366                 glFogfv(GL_FOG_COLOR, fogcolor);
1367                 glFogf(GL_FOG_DENSITY, 0.35f);
1368                 glHint(GL_FOG_HINT, GL_NICEST);
1369                 glFogf(GL_FOG_START, MIN_CAMERA_RADIUS);
1370                 glFogf(GL_FOG_END, MAX_CAMERA_RADIUS / 1.9);
1371                 glEnable(GL_FOG);
1372         }
1373
1374
1375         glhanoi->duration = START_DURATION;
1376         changeState(START);
1377 }
1378
1379 void draw_glhanoi(ModeInfo * mi)
1380 {
1381         Display *dpy = MI_DISPLAY(mi);
1382         Window window = MI_WINDOW(mi);
1383
1384         glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1385
1386         if(!glhanoi->glx_context)
1387                 return;
1388         glPolygonMode(GL_FRONT, glhanoi->wire ? GL_LINE : GL_FILL);
1389
1390         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1391
1392         glLoadIdentity();
1393
1394         update_glhanoi();
1395         updateView();
1396
1397         if(!glhanoi->wire && glhanoi->texture) {
1398                 glEnable(GL_TEXTURE_2D);
1399         }
1400         drawFloor();
1401         glDisable(GL_TEXTURE_2D);
1402
1403         drawTowers();
1404         drawDisks();
1405
1406         if(mi->fps_p) {
1407                 do_fps(mi);
1408         }
1409         glFinish();
1410
1411         glXSwapBuffers(dpy, window);
1412 }
1413
1414 Bool glhanoi_handle_event(ModeInfo * mi, XEvent * event)
1415 {
1416         glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1417
1418         if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
1419                 glhanoi->button_down_p = True;
1420                 glhanoi->drag_x = event->xbutton.x;
1421                 glhanoi->drag_y = event->xbutton.y;
1422                 return True;
1423         } else if(event->xany.type == ButtonRelease
1424                           && event->xbutton.button == Button1) {
1425                 glhanoi->button_down_p = False;
1426                 return True;
1427         } else if(event->xany.type == ButtonPress &&
1428                           (event->xbutton.button == Button4
1429                            || event->xbutton.button == Button5)) {
1430                 switch (event->xbutton.button) {
1431                 case Button4:
1432                         glhanoi->camera[2] += 0.01;
1433                         break;
1434                 case Button5:
1435                         glhanoi->camera[2] -= 0.01;
1436                         break;
1437                 default:
1438                         fprintf(stderr,
1439                                         "glhanoi: unknown button in mousewheel handler\n");
1440                 }
1441                 return True;
1442         } else if(event->xany.type == MotionNotify
1443                           && glhanoi_cfg->button_down_p) {
1444                 int x_diff, y_diff;
1445
1446                 x_diff = event->xbutton.x - glhanoi->drag_x;
1447                 y_diff = event->xbutton.y - glhanoi->drag_y;
1448
1449                 glhanoi->camera[0] = (float)x_diff / (float)MI_WIDTH(mi);
1450                 glhanoi->camera[1] = (float)y_diff / (float)MI_HEIGHT(mi);
1451
1452                 return True;
1453         }
1454         return False;
1455 }
1456
1457 void release_glhanoi(ModeInfo * mi)
1458 {
1459         if(glhanoi_cfg != NULL) {
1460                 int screen;
1461                 for(screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1462                         int i;
1463                         int j;
1464                         glhcfg *glh = &glhanoi_cfg[screen];
1465                         glDeleteLists(glh->floorList, 1);
1466                         glDeleteLists(glh->baseList, 1);
1467                         glDeleteLists(glh->poleList, 1);
1468                         for(j = 0; j < glh->numberOfDisks; ++j) {
1469                                 glDeleteLists(glh->disk[j].displayList, 1);
1470                         }
1471                         free(glh->disk);
1472                         for(i = 0; i < 3; i++) {
1473                                 if(glh->pole[i].data != NULL) {
1474                                         free(glh->pole[i].data);
1475                                 }
1476                         }
1477                 }
1478         }
1479         free(glhanoi_cfg);
1480         glhanoi_cfg = NULL;
1481         glDeleteLists(glhanoi->textureNames[0], 2);
1482 }
1483
1484 #endif                                                  /* USE_GL */