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
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
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"
26 #define DEFAULTS "*delay: " DEF_DELAY "\n" \
27 "*count: " DEF_DISKS "\n" \
28 "*showFPS: " DEF_FPS "\n" \
29 "*wireframe: " DEF_WIRE "\n"
31 # define refresh_glhanoi 0
35 #define START_DURATION 1.0
36 #define FINISH_DURATION 1.0
37 #define BASE_LENGTH 30.0
38 #define BOARD_SQUARES 8
40 #define MAX_CAMERA_RADIUS 250.0
41 #define MIN_CAMERA_RADIUS 75.0
43 #define MARBLE_SCALE 1.01
46 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
53 #define MARBLE_TEXTURE_SIZE 256
56 #define countof(x) (sizeof((x))/sizeof((*x)))
59 #include "xlockmore.h"
61 #ifdef USE_GL /* whole file */
63 typedef struct timeval glhtime;
65 static double getTime(void)
68 #ifdef GETTIMEOFDAY_TWO_ARGS
69 gettimeofday(&t, NULL);
70 #else /* !GETTIMEOFDAY_TWO_ARGS */
72 #endif /* !GETTIMEOFDAY_TWO_ARGS */
73 return t.tv_sec + t.tv_usec / 1000000.0;
94 GLfloat xmin, xmax, ymin;
97 GLfloat ucostheta, usintheta;
108 GLXContext *glx_context;
137 float *diskPos; /* pre-computed disk positions on rods */
145 rotator *the_rotator;
148 GLuint textureNames[N_TEXTURES];
157 static glhcfg *glhanoi_cfg = NULL;
162 static XrmOptionDescRec opts[] = {
163 {"-light", ".glhanoi.light", XrmoptionNoArg, "true"},
164 {"+light", ".glhanoi.light", XrmoptionNoArg, "false"},
165 {"-fog", ".glhanoi.fog", XrmoptionNoArg, "true"},
166 {"+fog", ".glhanoi.fog", XrmoptionNoArg, "false"},
167 {"-texture", ".glhanoi.texture", XrmoptionNoArg, "true"},
168 {"+texture", ".glhanoi.texture", XrmoptionNoArg, "false"}
171 static argtype vars[] = {
172 {&light, "light", "Light", DEF_LIGHT, t_Bool},
173 {&fog, "fog", "Fog", DEF_FOG, t_Bool},
174 {&texture, "texture", "Texture", DEF_TEXTURE, t_Bool}
177 static OptionStruct desc[] = {
178 {"+/-light", "whether to light the scene"},
179 {"+/-fog", "whether to apply fog to the scene"},
180 {"+/-texture", "whether to apply texture to the scene"}
183 ENTRYPOINT ModeSpecOpt glhanoi_opts = { countof(opts), opts, countof(vars), vars, desc };
187 ModStruct glhanoi_description = {
188 "glhanoi", "init_glhanoi", "draw_glhanoi", "release_glhanoi",
189 "draw_glhanoi", "init_glhanoi", NULL, &glhanoi_opts,
190 1000, 1, 2, 1, 4, 1.0, "",
191 "Towers of Hanoi", 0, NULL
196 static const GLfloat cBlack[] = { 0.0, 0.0, 0.0, 1.0 };
197 static const GLfloat cWhite[] = { 1.0, 1.0, 1.0, 1.0 };
198 static const GLfloat poleColor[] = { 0.545, 0.137, 0.137 };
199 static const GLfloat baseColor[] = { 0.34, 0.34, 0.48 };
200 static const GLfloat fogcolor[] = { 0.5, 0.5, 0.5 };
202 static const float left[] = { 1.0, 0.0, 0.0 };
203 static const float up[] = { 0.0, 1.0, 0.0 };
204 static const float front[] = { 0.0, 0.0, 1.0 };
205 static const float right[] = { -1.0, 0.0, 0.0 };
206 static const float down[] = { 0.0, -1.0, 0.0 };
207 static const float back[] = { 0.0, 0.0, -1.0 };
209 static const GLfloat pos0[4] = { 50.0, 50.0, 50.0, 0.0 };
210 static const GLfloat amb0[4] = { 0.0, 0.0, 0.0, 1.0 };
211 static const GLfloat dif0[4] = { 1.0, 1.0, 1.0, 1.0 };
212 static const GLfloat spc0[4] = { 0.0, 1.0, 1.0, 1.0 };
214 static const GLfloat pos1[4] = { -50.0, 50.0, -50.0, 0.0 };
215 static const GLfloat amb1[4] = { 0.0, 0.0, 0.0, 1.0 };
216 static const GLfloat dif1[4] = { 1.0, 1.0, 1.0, 1.0 };
217 static const GLfloat spc1[4] = { 1.0, 1.0, 1.0, 1.0 };
219 static float g = 3.0 * 9.80665; /* hmm, looks like we need more gravity, Scotty... */
221 #define DOPUSH(X, Y) (((X)->count) >= ((X)->size)) ? NULL : ((X)->data[(X)->count++] = (Y))
222 #define DOPOP(X) (X)->count <= 0 ? NULL : ((X)->data[--((X)->count)])
224 static Disk *push(glhcfg *glhanoi, int idx, Disk * d)
226 return DOPUSH(&glhanoi->pole[idx], d);
229 static Disk *pop(glhcfg *glhanoi, int idx)
231 return DOPOP(&glhanoi->pole[idx]);
234 static inline void swap(int *x, int *y)
242 * magic - it's magic...
244 static int magic(int i)
249 while((i & 0x01) == 0) {
253 return count % 2 == 0;
257 static float distance(float *p0, float *p1)
263 return (float)sqrt(x * x + y * y + z * z);
267 static GLfloat A(GLfloat a, GLfloat b, GLfloat c)
270 return c / (a * b - 0.25 * sum * sum);
273 static void moveSetup(glhcfg *glhanoi, Disk * disk)
277 int src = glhanoi->src;
278 int dst = glhanoi->dst;
280 GLfloat sintheta, costheta;
282 if(glhanoi->state != FINISHED && random() % 6 == 0) {
284 -180.0 * (2 - 2 * random() % 2) * (random() % 3 + 1);
286 disk->rotAngle = -180.0;
289 disk->base0 = glhanoi->diskPos[glhanoi->pole[src].count];
292 FINISHED ? disk->base0 : glhanoi->diskPos[glhanoi->pole[dst].
295 disk->xmin = glhanoi->poleOffset * (src - 1);
296 disk->xmax = glhanoi->poleOffset * (dst - 1);
297 disk->ymin = glhanoi->poleHeight;
299 glhanoi->poleHeight + fabs(disk->xmax -
300 disk->xmin) * (glhanoi->state ==
306 numberOfDisks : 1.0);
308 h = ymax - disk->ymin;
309 theta = atan((disk->xmin - disk->xmax) * A(disk->xmin, disk->xmax, h));
312 costheta = cos(theta);
313 sintheta = sin(theta);
317 (2.0 * A(disk->xmin, disk->xmax, h) * costheta * costheta)));
318 disk->usintheta = u * sintheta;
319 disk->ucostheta = u * costheta;
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;
327 static void makeMove(glhcfg *glhanoi)
329 int fudge = glhanoi->move + 2;
330 int magicNumber = magic(fudge);
332 glhanoi->currentDisk = pop(glhanoi, glhanoi->src);
333 moveSetup(glhanoi, glhanoi->currentDisk);
334 push(glhanoi, glhanoi->dst, glhanoi->currentDisk);
337 if(fudge == 1 || magicNumber) {
338 swap(&glhanoi->src, &glhanoi->tmp);
340 if(fudge == 0 || glhanoi->magicNumber) {
341 swap(&glhanoi->dst, &glhanoi->tmp);
343 glhanoi->magicNumber = magicNumber;
346 static double lerp(double alpha, double start, double end)
348 return start + alpha * (end - start);
351 static void upfunc(GLdouble t, Disk * d)
353 d->position[0] = d->xmin;
354 d->position[1] = d->base0 + (d->u1 - 0.5 * g * t) * t;
356 d->rotation[1] = 0.0;
359 static void parafunc(GLdouble t, Disk * d)
361 d->position[0] = d->xmin + d->ucostheta * t;
362 d->position[1] = d->ymin + (d->usintheta - 0.5 * g * t) * t;
365 d->rotAngle * (d->position[0] - d->xmin) / (d->xmax - d->xmin);
368 static void downfunc(GLdouble t, Disk * d)
370 d->position[0] = d->xmax;
371 d->position[1] = d->ymin + (d->u2 - 0.5 * g * t) * t;
373 d->rotation[1] = 0.0;
376 static Bool computePosition(GLfloat t, Disk * d)
378 Bool finished = False;
382 } else if(t < d->t1 + d->t2) {
383 parafunc(t - d->t1, d);
385 downfunc(t - d->t1 - d->t2, d);
386 if(d->position[1] <= d->base1) {
387 d->position[1] = d->base1;
394 static void updateView(glhcfg *glhanoi)
396 double longitude, latitude, radius;
397 double a, b, c, A, B;
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);
410 latitude = 1.0 - latitude;
413 radius = 1.0 - radius;
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);
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));
432 static void changeState(glhcfg *glhanoi, State state)
434 glhanoi->state = state;
435 glhanoi->startTime = getTime();
438 static void update_glhanoi(glhcfg *glhanoi)
440 double t = getTime() - glhanoi->startTime;
444 switch (glhanoi->state) {
446 if(t < glhanoi->duration) {
450 if(glhanoi->numberOfDisks % 2 == 0) {
451 swap(&glhanoi->tmp, &glhanoi->dst);
453 glhanoi->magicNumber = 1;
455 changeState(glhanoi, MOVE_DISK);
459 if(computePosition(t, glhanoi->currentDisk)) {
460 changeState(glhanoi, MOVE_FINISHED);
465 if(++glhanoi->move < glhanoi->numberOfMoves) {
467 changeState(glhanoi, MOVE_DISK);
469 glhanoi->duration = FINISH_DURATION;
470 changeState(glhanoi, FINISHED);
475 while(t < glhanoi->duration) {
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);
485 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
486 push(glhanoi, glhanoi->dst, &glhanoi->disk[i]);
488 changeState(glhanoi, MONEY_SHOT);
493 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
494 double delay = 0.25 * i;
502 finished = computePosition(t - delay, &glhanoi->disk[i]);
503 glhanoi->disk[i].rotation[1] = 0.0;
510 glhanoi->src = glhanoi->oldsrc;
511 glhanoi->tmp = glhanoi->oldtmp;
512 glhanoi->dst = glhanoi->olddst;
513 changeState(glhanoi, START);
519 fprintf(stderr, "Invalid state\n");
524 static void HSVtoRGBf(GLfloat h, GLfloat s, GLfloat v,
525 GLfloat * r, GLfloat * g, GLfloat * b)
532 GLfloat i, f, p, q, t;
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 */
540 q = v * (1.0 - (s * f));
541 t = v * (1.0 - (s * (1.0 - f)));
577 static void HSVtoRGBv(GLfloat * hsv, GLfloat * rgb)
579 HSVtoRGBf(hsv[0], hsv[1], hsv[2], &rgb[0], &rgb[1], &rgb[2]);
582 static void setMaterial(const GLfloat color[3], const GLfloat hlite[3], int shininess)
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] */
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 :-( )
598 static void drawTube(GLdouble bottomRadius, GLdouble topRadius,
599 GLdouble bottomThickness, GLdouble topThickness,
600 GLdouble height, GLuint nSlice, GLuint nLoop)
603 GLfloat *cosCache = malloc(sizeof(GLfloat) * nSlice);
604 GLfloat *sinCache = malloc(sizeof(GLfloat) * nSlice);
607 GLint lastSlice = nSlice - 1;
612 if(bottomThickness > bottomRadius) {
613 bottomThickness = bottomRadius;
615 if(topThickness > topRadius) {
616 topThickness = topRadius;
618 if(bottomThickness < 0.0) {
619 bottomThickness = 0.0;
621 if(topThickness < 0.0) {
624 if(topRadius >= bottomRadius) {
625 maxRadius = topRadius;
627 maxRadius = bottomRadius;
632 radius = bottomRadius;
633 innerRadius = bottomRadius - bottomThickness;
634 /* innerTexCoordSize = texCoordSize * innerRadius / maxRadius; */
635 /* outerTexCoordSize = texCoordSize * radius / maxRadius; */
636 /* yTexCoord = minTexCoord; */
638 glBegin(GL_QUAD_STRIP);
640 glNormal3f(0.0, -1.0, 0.0);
642 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + innerTexCoordSize); */
643 glVertex3f(0.0, y, innerRadius);
645 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + outerTexCoordSize); */
646 glVertex3f(0.0, y, radius);
648 for(slice = lastSlice; slice >= 0; --slice) {
649 GLfloat theta = 2.0 * M_PI * (double)slice / nSlice;
651 cosCache[slice] = cos(theta);
652 sinCache[slice] = sin(theta);
654 /* glTexCoord3f(midTexCoord + sinCache[slice] * innerTexCoordSize, */
656 /* midTexCoord + cosCache[slice] * innerTexCoordSize); */
657 glVertex3f(innerRadius * sinCache[slice], y,
658 innerRadius * cosCache[slice]);
659 /* glTexCoord3f(midTexCoord + sinCache[slice] * outerTexCoordSize, */
661 /* midTexCoord + cosCache[slice] * outerTexCoordSize); */
662 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
667 for(loop = 0; loop < nLoop; ++loop) {
668 GLfloat lowerRadius =
669 bottomRadius + (topRadius -
670 bottomRadius) * (float)loop / (nLoop);
671 GLfloat upperRadius =
672 bottomRadius + (topRadius - bottomRadius) * (float)(loop +
675 GLfloat lowerY = height * (float)loop / (nLoop);
676 GLfloat upperY = height * (float)(loop + 1) / (nLoop);
677 GLfloat factor = (topRadius - topThickness) -
678 (bottomRadius - bottomThickness);
681 glBegin(GL_QUAD_STRIP);
682 for(slice = 0; slice < nSlice; ++slice) {
683 glNormal3f(sinCache[slice], 0.0, cosCache[slice]);
684 glVertex3f(upperRadius * sinCache[slice], upperY,
685 upperRadius * cosCache[slice]);
686 glVertex3f(lowerRadius * sinCache[slice], lowerY,
687 lowerRadius * cosCache[slice]);
689 glNormal3f(0.0, 0.0, 1.0);
690 glVertex3f(0.0, upperY, upperRadius);
691 glVertex3f(0.0, lowerY, lowerRadius);
695 lowerRadius = bottomRadius - bottomThickness +
696 factor * (float)loop / (nLoop);
697 upperRadius = bottomRadius - bottomThickness +
698 factor * (float)(loop + 1) / (nLoop);
700 glBegin(GL_QUAD_STRIP);
701 glNormal3f(0.0, 0.0, -1.0);
702 glVertex3f(0.0, upperY, upperRadius);
703 glVertex3f(0.0, lowerY, lowerRadius);
704 for(slice = lastSlice; slice >= 0; --slice) {
705 glNormal3f(-sinCache[slice], 0.0, -cosCache[slice]);
706 glVertex3f(upperRadius * sinCache[slice], upperY,
707 upperRadius * cosCache[slice]);
708 glVertex3f(lowerRadius * sinCache[slice], lowerY,
709 lowerRadius * cosCache[slice]);
717 innerRadius = topRadius - topThickness;
719 glBegin(GL_QUAD_STRIP);
720 glNormal3f(0.0, 1.0, 0.0);
721 for(slice = 0; slice < nSlice; ++slice) {
722 glVertex3f(innerRadius * sinCache[slice], y,
723 innerRadius * cosCache[slice]);
725 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
727 glVertex3f(0.0, y, innerRadius);
728 glVertex3f(0.0, y, radius);
732 static void drawPole(GLfloat radius, GLfloat length)
734 drawTube(radius, radius, radius, radius, length, NSLICE, NLOOPS);
737 static void drawDisk3D(GLdouble inner_radius, GLdouble outer_radius,
740 drawTube(outer_radius, outer_radius, outer_radius - inner_radius,
741 outer_radius - inner_radius, height, NSLICE, NLOOPS);
744 static void drawCuboid(GLfloat length, GLfloat width, GLfloat height)
746 GLfloat xmin = -length / 2.0f;
747 GLfloat xmax = length / 2.0f;
748 GLfloat zmin = -width / 2.0f;
749 GLfloat zmax = width / 2.0f;
751 GLfloat ymax = height;
756 glVertex3f(xmin, ymin, zmax); /* 0 */
757 glVertex3f(xmax, ymin, zmax); /* 1 */
758 glVertex3f(xmax, ymax, zmax); /* 2 */
759 glVertex3f(xmin, ymax, zmax); /* 3 */
762 glVertex3f(xmax, ymin, zmax); /* 1 */
763 glVertex3f(xmax, ymin, zmin); /* 5 */
764 glVertex3f(xmax, ymax, zmin); /* 6 */
765 glVertex3f(xmax, ymax, zmax); /* 2 */
768 glVertex3f(xmax, ymin, zmin); /* 5 */
769 glVertex3f(xmin, ymin, zmin); /* 4 */
770 glVertex3f(xmin, ymax, zmin); /* 7 */
771 glVertex3f(xmax, ymax, zmin); /* 6 */
774 glVertex3f(xmin, ymin, zmin); /* 4 */
775 glVertex3f(xmin, ymin, zmax); /* 0 */
776 glVertex3f(xmin, ymax, zmax); /* 3 */
777 glVertex3f(xmin, ymax, zmin); /* 7 */
780 glVertex3f(xmin, ymax, zmax); /* 3 */
781 glVertex3f(xmax, ymax, zmax); /* 2 */
782 glVertex3f(xmax, ymax, zmin); /* 6 */
783 glVertex3f(xmin, ymax, zmin); /* 7 */
786 glVertex3f(xmin, ymin, zmin); /* 4 */
787 glVertex3f(xmax, ymin, zmin); /* 5 */
788 glVertex3f(xmax, ymin, zmax); /* 1 */
789 glVertex3f(xmin, ymin, zmax); /* 0 */
793 static void drawDisks(glhcfg *glhanoi)
798 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
799 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
800 Disk *disk = &glhanoi->disk[i];
801 GLfloat *pos = disk->position;
802 GLfloat *rot = disk->rotation;
805 glTranslatef(pos[0], pos[1], pos[2]);
807 glTranslatef(0.0, glhanoi->diskHeight / 2.0, 0.0);
808 glRotatef(rot[1], 0.0, 0.0, 1.0);
809 glTranslatef(0.0, -glhanoi->diskHeight / 2.0, 0.0);
811 glCallList(disk->displayList);
817 static GLfloat getDiskRadius(glhcfg *glhanoi, int i)
819 return ((GLfloat) i + 3.0) * glhanoi->poleRadius;
822 static void initData(glhcfg *glhanoi)
824 GLfloat maxDiskRadius;
827 glhanoi->baseLength = BASE_LENGTH;
828 glhanoi->poleRadius = glhanoi->baseLength /
829 (2.0 * (3 * glhanoi->numberOfDisks + 7.0));
830 maxDiskRadius = getDiskRadius(glhanoi, glhanoi->numberOfDisks);
831 glhanoi->baseWidth = 2.0 * maxDiskRadius;
832 glhanoi->poleOffset = 2.0 * getDiskRadius(glhanoi, glhanoi->maxDiskIdx);
833 glhanoi->diskHeight = 2.0 * glhanoi->poleRadius;
834 glhanoi->baseHeight = 2.0 * glhanoi->poleRadius;
835 glhanoi->poleHeight = glhanoi->numberOfDisks *
836 glhanoi->diskHeight + glhanoi->poleRadius;
837 glhanoi->numberOfMoves = (1 << glhanoi->numberOfDisks) - 1;
838 glhanoi->boardSize = glhanoi->baseLength * 0.5 * (1.0 + sqrt(5.0));
840 for(i = 0; i < 3; i++) {
841 if((glhanoi->pole[i].data =
842 calloc(glhanoi->numberOfDisks, sizeof(Disk *))) == NULL) {
843 fprintf(stderr, "%s: out of memory creating stack %d\n",
847 glhanoi->pole[i].size = glhanoi->numberOfDisks;
849 if((glhanoi->diskPos =
850 calloc(glhanoi->numberOfDisks, sizeof(double))) == NULL) {
851 fprintf(stderr, "%s: out of memory creating diskPos\n", progname);
855 glhanoi->the_rotator = make_rotator(0.1, 0.025, 0, 1, 0.005, False);
856 glhanoi->button_down_p = False;
858 glhanoi->src = glhanoi->oldsrc = 0;
859 glhanoi->tmp = glhanoi->oldtmp = 1;
860 glhanoi->dst = glhanoi->olddst = 2;
863 static void initView(glhcfg *glhanoi)
865 glhanoi->camera[0] = 0.0;
866 glhanoi->camera[1] = 0.0;
867 glhanoi->camera[2] = 0.0;
868 glhanoi->centre[0] = 0.0;
869 glhanoi->centre[1] = glhanoi->poleHeight * 3.0;
870 glhanoi->centre[2] = 0.0;
874 * noise_improved.c - based on ImprovedNoise.java
875 * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
877 static double fade(double t)
879 return t * t * t * (t * (t * 6 - 15) + 10);
882 static double grad(int hash, double x, double y, double z)
884 int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
885 double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
886 v = h < 4 ? y : h == 12 || h == 14 ? x : z;
887 return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
890 static const int permutation[] = { 151, 160, 137, 91, 90, 15,
891 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
892 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26,
893 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
894 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
895 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60,
896 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143,
897 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
898 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198,
899 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118,
900 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
901 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
902 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108,
903 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251,
904 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145,
905 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
906 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
907 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
911 static void initNoise(glhcfg *glhanoi)
914 for(i = 0; i < 256; i++)
915 glhanoi->p[256 + i] = glhanoi->p[i] = permutation[i];
918 static double improved_noise(glhcfg *glhanoi, double x, double y, double z)
921 int A, AA, AB, B, BA, BB;
922 int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
923 Y = (int)floor(y) & 255, /* CONTAINS POINT. */
924 Z = (int)floor(z) & 255;
925 if(!glhanoi->noise_initted) {
927 glhanoi->noise_initted = 1;
929 x -= floor(x); /* FIND RELATIVE X,Y,Z */
930 y -= floor(y); /* OF POINT IN CUBE. */
932 u = fade(x), /* COMPUTE FADE CURVES */
933 v = fade(y), /* FOR EACH OF X,Y,Z. */
935 A = glhanoi->p[X] + Y, AA = glhanoi->p[A] + Z, AB = glhanoi->p[A + 1] + Z, /* HASH COORDINATES OF */
936 B = glhanoi->p[X + 1] + Y, BA = glhanoi->p[B] + Z, BB = glhanoi->p[B + 1] + Z; /* THE 8 CUBE CORNERS, */
937 return lerp(w, lerp(v, lerp(u, grad(glhanoi->p[AA], x, y, z), /* AND ADD */
938 grad(glhanoi->p[BA], x - 1, y, z)), /* BLENDED */
939 lerp(u, grad(glhanoi->p[AB], x, y - 1, z), /* RESULTS */
940 grad(glhanoi->p[BB], x - 1, y - 1, z))), /* FROM 8 CORNERS */
941 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 */
942 lerp(u, grad(glhanoi->p[AB + 1], x, y - 1, z - 1),
943 grad(glhanoi->p[BB + 1], x - 1, y - 1, z - 1))));
947 * end noise_improved.c - based on ImprovedNoise.java
952 /* GLfloat *points; */
955 typedef struct tex_col_t tex_col_t;
957 static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
958 GLuint(*texFunc) (glhcfg *, double, double, double,
959 tex_col_t *), tex_col_t * colours)
962 GLubyte *textureData;
968 calloc(x_size * y_size * z_size, sizeof(GLuint))) == NULL) {
977 texturePtr = (void *)textureData;
978 for(k = 0; k < z_size; k++, z += zi) {
980 for(j = 0; j < y_size; j++, y += yi) {
982 for(i = 0; i < x_size; i++, x += xi) {
983 *texturePtr = texFunc(glhanoi, x, y, z, colours);
991 static tex_col_t makeMarbleColours(void)
993 tex_col_t marbleColours;
996 marbleColours.colours = calloc(sizeof(GLuint), ncols);
997 marbleColours.ncols = ncols;
999 marbleColours.colours[0] = 0x3f3f3f3f;
1000 marbleColours.colours[1] = 0xffffffff;
1002 return marbleColours;
1005 static double turb(glhcfg *glhanoi, double x, double y, double z, int octaves)
1010 for(oct = 0; oct < octaves; ++oct) {
1011 r += fabs(improved_noise(glhanoi, freq * x, freq * y, freq * z)) / freq;
1017 static void perturb(glhcfg *glhanoi, double *x, double *y, double *z, double scale)
1019 double t = scale * turb(glhanoi, *x, *y, *z, 4);
1025 static double f_m(double x, double y, double z)
1027 return sin(3.0 * M_PI * x);
1030 static GLuint C_m(double x, const tex_col_t * tex_cols)
1032 int r = tex_cols->colours[0] & 0xff;
1033 int g = tex_cols->colours[0] >> 8 & 0xff;
1034 int b = tex_cols->colours[0] >> 16 & 0xff;
1039 factor = (1.0 + sin(2.0 * M_PI * x)) / 2.0;
1041 r1 = (tex_cols->colours[1] & 0xff);
1042 g1 = (tex_cols->colours[1] >> 8 & 0xff);
1043 b1 = (tex_cols->colours[1] >> 16 & 0xff);
1045 r += (int)(factor * (r1 - r));
1046 g += (int)(factor * (g1 - g));
1047 b += (int)(factor * (b1 - b));
1049 return 0xff000000 | (b << 16) | (g << 8) | r;
1053 static GLuint makeMarbleTexture(glhcfg *glhanoi, double x, double y, double z, tex_col_t * colours)
1055 perturb(glhanoi, &x, &y, &z, MARBLE_SCALE);
1056 return C_m(f_m(x, y, z), colours);
1059 static void setTexture(glhcfg *glhanoi, int n)
1061 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[n]);
1064 static int makeTextures(glhcfg *glhanoi)
1066 GLubyte *marbleTexture;
1067 tex_col_t marbleColours;
1069 glGenTextures(N_TEXTURES, glhanoi->textureNames);
1071 marbleColours = makeMarbleColours();
1073 makeTexture(glhanoi, MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 1,
1074 makeMarbleTexture, &marbleColours)) == NULL) {
1078 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[0]);
1079 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1080 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1081 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1082 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1083 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1084 MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 0,
1085 GL_RGBA, GL_UNSIGNED_BYTE, marbleTexture);
1086 free(marbleTexture);
1091 static void initFloor(glhcfg *glhanoi)
1094 float tileSize = glhanoi->boardSize / BOARD_SQUARES;
1095 float x0, x1, z0, z1;
1096 float tx0, tx1, tz0, tz1;
1097 const float *col = cWhite;
1098 float texIncr = 1.0 / BOARD_SQUARES;
1100 if((glhanoi->floorList = glGenLists(1)) == 0) {
1101 fprintf(stderr, "can't allocate memory for floor display list\n");
1104 glNewList(glhanoi->floorList, GL_COMPILE);
1105 x0 = -glhanoi->boardSize / 2.0;
1107 setMaterial(col, cWhite, 128);
1108 setTexture(glhanoi, 0);
1110 for(i = 0; i < BOARD_SQUARES; i++, x0 += tileSize, tx0 += texIncr) {
1112 tx1 = tx0 + texIncr;
1113 z0 = -glhanoi->boardSize / 2.0;
1115 for(j = 0; j < BOARD_SQUARES; j++, z0 += tileSize, tz0 += texIncr) {
1116 int colIndex = (i + j) & 0x1;
1119 tz1 = tz0 + texIncr;
1126 setMaterial(col, cWhite, 100);
1130 glTexCoord2d(tx0, tz0);
1131 glVertex3f(x0, 0.0, z0);
1133 glTexCoord2d(tx0, tz1);
1134 glVertex3f(x0, 0.0, z1);
1136 glTexCoord2d(tx1, tz1);
1137 glVertex3f(x1, 0.0, z1);
1139 glTexCoord2d(tx1, tz0);
1140 glVertex3f(x1, 0.0, z0);
1147 static void initTowers(glhcfg *glhanoi)
1149 if((glhanoi->baseList = glGenLists(1)) == 0) {
1150 fprintf(stderr, "can't allocate memory for towers display list\n");
1153 glNewList(glhanoi->baseList, GL_COMPILE);
1154 setMaterial(baseColor, cWhite, 50);
1155 drawCuboid(glhanoi->baseLength, glhanoi->baseWidth,
1156 glhanoi->baseHeight);
1160 if((glhanoi->poleList = glGenLists(1)) == 0) {
1161 fprintf(stderr, "can't allocate memory for towers display list\n");
1164 glNewList(glhanoi->poleList, GL_COMPILE);
1166 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
1167 setMaterial(poleColor, cWhite, 50);
1168 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1170 glTranslatef(-glhanoi->poleOffset, 0.0, 0.0);
1171 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1173 glTranslatef(glhanoi->poleOffset, 0.0, 0.0);
1174 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1179 static double cfunc(double x)
1183 return (1.0 / 12.0) / (1.0 / 7.0) * x;
1186 return (1.0 + 1.0 / 6.0) * x - 1.0 / 6.0;
1189 return (2.0 + 1.0 / 3.0) * x - 2.0 / 3.0;
1192 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1194 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1197 static void initDisks(glhcfg *glhanoi)
1201 (Disk *) calloc(glhanoi->numberOfDisks, sizeof(Disk))) == NULL) {
1202 perror("initDisks");
1206 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
1207 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1208 double f = cfunc((GLfloat) i / (GLfloat) glhanoi->numberOfDisks);
1209 GLfloat diskColor = f * 360.0;
1211 Disk *disk = &glhanoi->disk[i];
1214 disk->position[0] = -glhanoi->poleOffset;
1215 disk->position[1] = glhanoi->diskHeight * height;
1216 disk->position[2] = 0.0;
1217 disk->rotation[0] = 0.0;
1218 disk->rotation[1] = 0.0;
1219 disk->rotation[2] = 0.0;
1221 color[0] = diskColor;
1224 HSVtoRGBv(color, color);
1226 if((disk->displayList = glGenLists(1)) == 0) {
1228 "can't allocate memory for disk %d display list\n", i);
1231 glNewList(disk->displayList, GL_COMPILE);
1232 setMaterial(color, cWhite, 100.0);
1233 drawDisk3D(glhanoi->poleRadius,
1234 getDiskRadius(glhanoi, i),
1235 glhanoi->diskHeight);
1238 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
1239 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1240 int h = glhanoi->maxDiskIdx - i;
1241 glhanoi->diskPos[h] = glhanoi->diskHeight * height;
1242 push(glhanoi, glhanoi->src, &glhanoi->disk[i]);
1246 static void initLights(Bool state)
1249 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1250 glLightfv(GL_LIGHT0, GL_AMBIENT, amb0);
1251 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
1252 glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1254 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1255 glLightfv(GL_LIGHT1, GL_AMBIENT, amb1);
1256 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
1257 glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1259 glEnable(GL_LIGHTING);
1260 glEnable(GL_LIGHT0);
1261 glEnable(GL_LIGHT1);
1263 glDisable(GL_LIGHTING);
1267 static void drawFloor(glhcfg *glhanoi)
1269 glCallList(glhanoi->floorList);
1272 static void drawTowers(glhcfg *glhanoi)
1274 glCallList(glhanoi->baseList);
1275 glCallList(glhanoi->poleList);
1278 /* Window management, etc
1280 ENTRYPOINT void reshape_glhanoi(ModeInfo * mi, int width, int height)
1282 glViewport(0, 0, (GLint) width, (GLint) height);
1284 glMatrixMode(GL_PROJECTION);
1286 gluPerspective(30.0, (GLdouble) width / (GLdouble) height, 1.0,
1287 2 * MAX_CAMERA_RADIUS);
1289 glMatrixMode(GL_MODELVIEW);
1292 glClear(GL_COLOR_BUFFER_BIT);
1295 ENTRYPOINT void init_glhanoi(ModeInfo * mi)
1300 (glhcfg *) calloc(MI_NUM_SCREENS(mi), sizeof(glhcfg));
1302 fprintf(stderr, "%s: out of memory creating configs\n",
1308 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1309 glhanoi->glx_context = init_GL(mi);
1310 glhanoi->numberOfDisks = MI_BATCHCOUNT(mi);
1312 if (glhanoi->numberOfDisks <= 1)
1313 glhanoi->numberOfDisks = 3 + (int) BELLRAND(9);
1315 /* magicnumber is a bitfield, so we can't have more than 31 discs
1316 on a system with 4-byte ints. */
1317 if (glhanoi->numberOfDisks >= 8 * sizeof(int))
1318 glhanoi->numberOfDisks = (8 * sizeof(int)) - 1;
1320 glhanoi->maxDiskIdx = glhanoi->numberOfDisks - 1;
1321 glhanoi->wire = MI_IS_WIREFRAME(mi);
1322 glhanoi->light = light;
1324 glhanoi->texture = texture;
1326 reshape_glhanoi(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1329 glhanoi->light = False;
1330 glhanoi->fog = False;
1331 glhanoi->texture = False;
1334 initLights(!glhanoi->wire && glhanoi->light);
1335 if(makeTextures(glhanoi) != 0) {
1336 fprintf(stderr, "can't allocate memory for marble texture\n");
1343 initTowers(glhanoi);
1346 glEnable(GL_DEPTH_TEST);
1347 glEnable(GL_NORMALIZE);
1348 glEnable(GL_CULL_FACE);
1349 glShadeModel(GL_SMOOTH);
1351 glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], 1.0);
1352 glFogi(GL_FOG_MODE, GL_LINEAR);
1353 glFogfv(GL_FOG_COLOR, fogcolor);
1354 glFogf(GL_FOG_DENSITY, 0.35f);
1355 glHint(GL_FOG_HINT, GL_NICEST);
1356 glFogf(GL_FOG_START, MIN_CAMERA_RADIUS);
1357 glFogf(GL_FOG_END, MAX_CAMERA_RADIUS / 1.9);
1362 glhanoi->duration = START_DURATION;
1363 changeState(glhanoi, START);
1366 ENTRYPOINT void draw_glhanoi(ModeInfo * mi)
1368 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1369 Display *dpy = MI_DISPLAY(mi);
1370 Window window = MI_WINDOW(mi);
1372 if(!glhanoi->glx_context)
1375 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(glhanoi->glx_context));
1377 glPolygonMode(GL_FRONT, glhanoi->wire ? GL_LINE : GL_FILL);
1379 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1383 update_glhanoi(glhanoi);
1384 updateView(glhanoi);
1386 if(!glhanoi->wire && glhanoi->texture) {
1387 glEnable(GL_TEXTURE_2D);
1390 glDisable(GL_TEXTURE_2D);
1392 drawTowers(glhanoi);
1400 glXSwapBuffers(dpy, window);
1403 ENTRYPOINT Bool glhanoi_handle_event(ModeInfo * mi, XEvent * event)
1405 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1407 if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
1408 glhanoi->button_down_p = True;
1409 glhanoi->drag_x = event->xbutton.x;
1410 glhanoi->drag_y = event->xbutton.y;
1412 } else if(event->xany.type == ButtonRelease
1413 && event->xbutton.button == Button1) {
1414 glhanoi->button_down_p = False;
1416 } else if(event->xany.type == ButtonPress &&
1417 (event->xbutton.button == Button4
1418 || event->xbutton.button == Button5)) {
1419 switch (event->xbutton.button) {
1421 glhanoi->camera[2] += 0.01;
1424 glhanoi->camera[2] -= 0.01;
1428 "glhanoi: unknown button in mousewheel handler\n");
1431 } else if(event->xany.type == MotionNotify
1432 && glhanoi_cfg->button_down_p) {
1435 x_diff = event->xbutton.x - glhanoi->drag_x;
1436 y_diff = event->xbutton.y - glhanoi->drag_y;
1438 glhanoi->camera[0] = (float)x_diff / (float)MI_WIDTH(mi);
1439 glhanoi->camera[1] = (float)y_diff / (float)MI_HEIGHT(mi);
1446 ENTRYPOINT void release_glhanoi(ModeInfo * mi)
1448 if(glhanoi_cfg != NULL) {
1450 for(screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1453 glhcfg *glh = &glhanoi_cfg[screen];
1454 glDeleteLists(glh->floorList, 1);
1455 glDeleteLists(glh->baseList, 1);
1456 glDeleteLists(glh->poleList, 1);
1457 glDeleteLists(glh->textureNames[0], 2);
1458 for(j = 0; j < glh->numberOfDisks; ++j) {
1459 glDeleteLists(glh->disk[j].displayList, 1);
1462 for(i = 0; i < 3; i++) {
1463 if(glh->pole[i].data != NULL) {
1464 free(glh->pole[i].data);
1473 XSCREENSAVER_MODULE ("GLHanoi", glhanoi)