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