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