1 /* glhanoi, Copyright (c) 2005 Dave Atkinson <dave.atkinson@uwe.ac.uk>
2 * except noise function code Copyright (c) 2002 Ken Perlin
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
14 #include <X11/Intrinsic.h>
18 #define PROGCLASS "glhanoi"
19 #define HACK_INIT init_glhanoi
20 #define HACK_DRAW draw_glhanoi
21 #define HACK_RESHAPE reshape_glhanoi
22 #define HACK_HANDLE_EVENT glhanoi_handle_event
23 #define EVENT_MASK PointerMotionMask
24 #define glh_opts xlockmore_opts
26 #define DEF_DELAY "15000"
28 #define DEF_WIRE "False"
29 #define DEF_LIGHT "True"
30 #define DEF_FPS "False"
31 #define DEF_FOG "False"
32 #define DEF_TEXTURE "True"
34 #define DEFAULTS "*delay: " DEF_DELAY "\n" \
35 "*count: " DEF_DISKS "\n" \
36 "*showFPS: " DEF_FPS "\n" \
37 "*wireframe: " DEF_WIRE "\n"
41 #define START_DURATION 1.0
42 #define FINISH_DURATION 1.0
43 #define BASE_LENGTH 30.0
44 #define BOARD_SQUARES 8
46 #define MAX_CAMERA_RADIUS 250.0
47 #define MIN_CAMERA_RADIUS 75.0
49 #define MARBLE_SCALE 1.01
56 #define MARBLE_TEXTURE_SIZE 256
59 #define countof(x) (sizeof((x))/sizeof((*x)))
62 #include "xlockmore.h"
64 #ifdef USE_GL /* whole file */
68 #ifdef HAVE_GETTIMEOFDAY
69 #ifdef GETTIMEOFDAY_TWO_ARGS
70 # include <sys/time.h>
72 typedef struct timeval glhtime;
73 #else /* GETTIMEOFDAY_TWO_ARGS */
74 # include <sys/time.h>
76 typedef struct timeval glhtime;
78 #else /* HAVE_GETTIMEOFDAY */
80 # include <sys/timeb.h>
81 typedef struct timeb glhtime;
82 #endif /* HAVE_FTIME */
83 #endif /* HAVE_GETTIMEOFDAY */
87 #ifdef HAVE_GETTIMEOFDAY
89 #ifdef GETTIMEOFDAY_TWO_ARGS
90 gettimeofday(&t, NULL);
91 #else /* !GETTIMEOFDAY_TWO_ARGS */
93 #endif /* !GETTIMEOFDAY_TWO_ARGS */
94 #else /* !HAVE_GETTIMEOFDAY */
97 #endif /* HAVE_FTIME */
98 #endif /* !HAVE_GETTIMEOFDAY */
99 return t.tv_sec + t.tv_usec / 1000000.0;
120 GLfloat xmin, xmax, ymin;
123 GLfloat ucostheta, usintheta;
134 GLXContext *glx_context;
163 float *diskPos; /* pre-computed disk positions on rods */
171 rotator *the_rotator;
174 GLuint textureNames[N_TEXTURES];
179 static glhcfg *glhanoi_cfg = NULL;
180 static glhcfg *glhanoi = NULL;
185 static XrmOptionDescRec opts[] = {
186 {"-light", ".glhanoi.light", XrmoptionNoArg, (caddr_t) "true"},
187 {"+light", ".glhanoi.light", XrmoptionNoArg, (caddr_t) "false"},
188 {"-fog", ".glhanoi.fog", XrmoptionNoArg, (caddr_t) "true"},
189 {"+fog", ".glhanoi.fog", XrmoptionNoArg, (caddr_t) "false"},
190 {"-texture", ".glhanoi.texture", XrmoptionNoArg, (caddr_t) "true"},
191 {"+texture", ".glhanoi.texture", XrmoptionNoArg, (caddr_t) "false"}
194 static argtype vars[] = {
195 {(caddr_t *) (void *)&light, "light", "Light", DEF_LIGHT, t_Bool},
196 {(caddr_t *) (void *)&fog, "fog", "Fog", DEF_FOG, t_Bool},
197 {(caddr_t *) (void *)&texture, "texture", "Texture", DEF_TEXTURE,
201 static OptionStruct desc[] = {
202 {"+/-light", "whether to light the scene"},
203 {"+/-fog", "whether to apply fog to the scene"},
204 {"+/-texture", "whether to apply texture to the scene"}
207 ModeSpecOpt glh_opts = { countof(opts), opts, countof(vars), vars, desc };
211 ModStruct glhanoi_description = {
212 "glhanoi", "init_glhanoi", "draw_glhanoi", "release_glhanoi",
213 "draw_glhanoi", "init_glhanoi", NULL, &glhanoi_opts,
214 1000, 1, 2, 1, 4, 1.0, "",
215 "Towers of Hanoi", 0, NULL
220 static GLfloat cBlack[] = { 0.0, 0.0, 0.0, 1.0 };
221 static GLfloat cWhite[] = { 1.0, 1.0, 1.0, 1.0 };
222 static GLfloat poleColor[] = { 0.545, 0.137, 0.137 };
223 static GLfloat baseColor[] = { 0.34, 0.34, 0.48 };
224 static GLfloat fogcolor[] = { 0.5, 0.5, 0.5 };
226 static float left[] = { 1.0, 0.0, 0.0 };
227 static float up[] = { 0.0, 1.0, 0.0 };
228 static float front[] = { 0.0, 0.0, 1.0 };
229 static float right[] = { -1.0, 0.0, 0.0 };
230 static float down[] = { 0.0, -1.0, 0.0 };
231 static float back[] = { 0.0, 0.0, -1.0 };
233 GLfloat pos0[4] = { 50.0, 50.0, 50.0, 0.0 };
234 GLfloat amb0[4] = { 0.0, 0.0, 0.0, 1.0 };
235 GLfloat dif0[4] = { 1.0, 1.0, 1.0, 1.0 };
236 GLfloat spc0[4] = { 0.0, 1.0, 1.0, 1.0 };
238 GLfloat pos1[4] = { -50.0, 50.0, -50.0, 0.0 };
239 GLfloat amb1[4] = { 0.0, 0.0, 0.0, 1.0 };
240 GLfloat dif1[4] = { 1.0, 1.0, 1.0, 1.0 };
241 GLfloat spc1[4] = { 1.0, 1.0, 1.0, 1.0 };
243 float g = 3.0 * 9.80665; /* hmm, looks like we need more gravity, Scotty... */
245 #define DOPUSH(X, Y) (((X)->count) >= ((X)->size)) ? NULL : ((X)->data[(X)->count++] = (Y))
246 #define DOPOP(X) (X)->count <= 0 ? NULL : ((X)->data[--((X)->count)])
248 Disk *push(int idx, Disk * d)
250 return DOPUSH(&glhanoi->pole[idx], d);
255 return DOPOP(&glhanoi->pole[idx]);
258 /* inline */ static void swap(int *x, int *y)
266 * magic - it's magic...
273 while((i & 0x01) == 0) {
277 return count % 2 == 0;
280 float distance(float *p0, float *p1)
286 return (float)sqrt(x * x + y * y + z * z);
289 GLfloat A(GLfloat a, GLfloat b, GLfloat c)
292 return c / (a * b - 0.25 * sum * sum);
295 void moveSetup(Disk * disk)
299 int src = glhanoi->src;
300 int dst = glhanoi->dst;
302 GLfloat sintheta, costheta;
304 if(glhanoi->state != FINISHED && random() % 6 == 0) {
306 -180.0 * (2 - 2 * random() % 2) * (random() % 3 + 1);
308 disk->rotAngle = -180.0;
311 disk->base0 = glhanoi->diskPos[glhanoi->pole[src].count];
314 FINISHED ? disk->base0 : glhanoi->diskPos[glhanoi->pole[dst].
317 disk->xmin = glhanoi->poleOffset * (src - 1);
318 disk->xmax = glhanoi->poleOffset * (dst - 1);
319 disk->ymin = glhanoi->poleHeight;
321 glhanoi->poleHeight + fabs(disk->xmax -
322 disk->xmin) * (glhanoi->state ==
328 numberOfDisks : 1.0);
330 h = ymax - disk->ymin;
331 theta = atan((disk->xmin - disk->xmax) * A(disk->xmin, disk->xmax, h));
334 costheta = cos(theta);
335 sintheta = sin(theta);
339 (2.0 * A(disk->xmin, disk->xmax, h) * costheta * costheta)));
340 disk->usintheta = u * sintheta;
341 disk->ucostheta = u * costheta;
343 (-u + sqrt(u * u + 2.0 * g * fabs(disk->ymin - disk->base0))) / g;
344 disk->u1 = u + g * disk->t1;
345 disk->t2 = 2.0 * disk->usintheta / g;
346 disk->u2 = disk->usintheta - g * disk->t2;
351 int fudge = glhanoi->move + 2;
352 int magicNumber = magic(fudge);
354 glhanoi->currentDisk = pop(glhanoi->src);
355 moveSetup(glhanoi->currentDisk);
356 push(glhanoi->dst, glhanoi->currentDisk);
359 if(fudge == 1 || magicNumber) {
360 swap(&glhanoi->src, &glhanoi->tmp);
362 if(fudge == 0 || glhanoi->magicNumber) {
363 swap(&glhanoi->dst, &glhanoi->tmp);
365 glhanoi->magicNumber = magicNumber;
368 double lerp(double alpha, double start, double end)
370 return start + alpha * (end - start);
373 void upfunc(GLdouble t, Disk * d)
375 d->position[0] = d->xmin;
376 d->position[1] = d->base0 + (d->u1 - 0.5 * g * t) * t;
378 d->rotation[1] = 0.0;
381 void parafunc(GLdouble t, Disk * d)
383 d->position[0] = d->xmin + d->ucostheta * t;
384 d->position[1] = d->ymin + (d->usintheta - 0.5 * g * t) * t;
387 d->rotAngle * (d->position[0] - d->xmin) / (d->xmax - d->xmin);
390 void downfunc(GLdouble t, Disk * d)
392 d->position[0] = d->xmax;
393 d->position[1] = d->ymin + (d->u2 - 0.5 * g * t) * t;
395 d->rotation[1] = 0.0;
398 Bool computePosition(GLfloat t, Disk * d)
400 Bool finished = FALSE;
404 } else if(t < d->t1 + d->t2) {
405 parafunc(t - d->t1, d);
407 downfunc(t - d->t1 - d->t2, d);
408 if(d->position[1] <= d->base1) {
409 d->position[1] = d->base1;
416 void updateView(void)
418 double longitude, latitude, radius;
419 double a, b, c, A, B;
421 get_position(glhanoi->the_rotator, NULL, NULL, &radius,
422 !glhanoi->button_down_p);
423 get_rotation(glhanoi->the_rotator, &longitude, &latitude, NULL,
424 !glhanoi->button_down_p);
425 longitude += glhanoi->camera[0];
426 latitude += glhanoi->camera[1];
427 radius += glhanoi->camera[2];
428 longitude = longitude - floor(longitude);
429 latitude = latitude - floor(latitude);
430 radius = radius - floor(radius);
432 latitude = 1.0 - latitude;
435 radius = 1.0 - radius;
438 b = glhanoi->centre[1];
439 c = (MIN_CAMERA_RADIUS +
440 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS));
441 A = M_PI / 4.0 * (1.0 - latitude);
442 a = sqrt(b * b + c * c - 2.0 * b * c * cos(A));
443 B = asin(sin(A) * b / a);
444 glRotatef(-B * 180 / M_PI, 1.0, 0.0, 0.0);
446 glTranslatef(0.0f, 0.0f,
447 -(MIN_CAMERA_RADIUS +
448 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS)));
449 glRotatef(longitude * 360.0, 0.0f, 1.0f, 0.0f);
450 glRotatef(latitude * 180.0, cos(longitude * 2.0 * M_PI), 0.0,
451 sin(longitude * 2.0 * M_PI));
454 void changeState(State state)
456 glhanoi->state = state;
457 glhanoi->startTime = getTime();
460 void update_glhanoi(void)
462 double t = getTime() - glhanoi->startTime;
466 switch (glhanoi->state) {
468 if(t < glhanoi->duration) {
472 if(glhanoi->numberOfDisks % 2 == 0) {
473 swap(&glhanoi->tmp, &glhanoi->dst);
475 glhanoi->magicNumber = 1;
477 changeState(MOVE_DISK);
481 if(computePosition(t, glhanoi->currentDisk)) {
482 changeState(MOVE_FINISHED);
487 if(++glhanoi->move < glhanoi->numberOfMoves) {
489 changeState(MOVE_DISK);
491 glhanoi->duration = FINISH_DURATION;
492 changeState(FINISHED);
497 while(t < glhanoi->duration) {
500 glhanoi->src = glhanoi->olddst;
501 glhanoi->dst = glhanoi->oldsrc;
502 for(i = 0; i < glhanoi->numberOfDisks; ++i) {
503 Disk *disk = pop(glhanoi->src);
504 assert(disk != NULL);
507 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
508 push(glhanoi->dst, &glhanoi->disk[i]);
510 changeState(MONEY_SHOT);
515 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
516 double delay = 0.25 * i;
524 finished = computePosition(t - delay, &glhanoi->disk[i]);
525 glhanoi->disk[i].rotation[1] = 0.0;
532 glhanoi->src = glhanoi->oldsrc;
533 glhanoi->tmp = glhanoi->oldtmp;
534 glhanoi->dst = glhanoi->olddst;
541 fprintf(stderr, "Invalid state\n");
546 void HSVtoRGBf(GLfloat h, GLfloat s, GLfloat v,
547 GLfloat * r, GLfloat * g, GLfloat * b)
554 GLfloat i, f, p, q, t;
558 h /= 60.0; /* h now in [0,6). */
559 i = floor((double)h); /* i now largest integer <= h */
560 f = h - i; /* f is no fractional part of h */
562 q = v * (1.0 - (s * f));
563 t = v * (1.0 - (s * (1.0 - f)));
599 void HSVtoRGBv(GLfloat * hsv, GLfloat * rgb)
601 HSVtoRGBf(hsv[0], hsv[1], hsv[2], &rgb[0], &rgb[1], &rgb[2]);
604 void setMaterial(GLfloat color[3], GLfloat hlite[3], int shininess)
607 glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
608 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
609 glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
613 * drawTube: I know all this stuff is available in gluQuadrics
614 * but I'd originally intended to texture the poles with a 3D wood
615 * texture, but I was having difficulty getting wood... what? Why
616 * are all you Amercians laughing..? Anyway, I don't know if enough
617 * people's hardware supports 3D textures, so I didn't bother (xorg
618 * ATI server doesn't :-( )
620 void drawTube(GLdouble bottomRadius, GLdouble topRadius,
621 GLdouble bottomThickness, GLdouble topThickness,
622 GLdouble height, GLuint nSlice, GLuint nLoop)
625 GLfloat *cosCache = malloc(sizeof(GLfloat) * nSlice);
626 GLfloat *sinCache = malloc(sizeof(GLfloat) * nSlice);
629 GLint lastSlice = nSlice - 1;
634 if(bottomThickness > bottomRadius) {
635 bottomThickness = bottomRadius;
637 if(topThickness > topRadius) {
638 topThickness = topRadius;
640 if(bottomThickness < 0.0) {
641 bottomThickness = 0.0;
643 if(topThickness < 0.0) {
646 if(topRadius >= bottomRadius) {
647 maxRadius = topRadius;
649 maxRadius = bottomRadius;
654 radius = bottomRadius;
655 innerRadius = bottomRadius - bottomThickness;
656 /* innerTexCoordSize = texCoordSize * innerRadius / maxRadius; */
657 /* outerTexCoordSize = texCoordSize * radius / maxRadius; */
658 /* yTexCoord = minTexCoord; */
660 glBegin(GL_QUAD_STRIP);
662 glNormal3f(0.0, -1.0, 0.0);
664 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + innerTexCoordSize); */
665 glVertex3f(0.0, y, innerRadius);
667 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + outerTexCoordSize); */
668 glVertex3f(0.0, y, radius);
670 for(slice = lastSlice; slice >= 0; --slice) {
671 GLfloat theta = 2.0 * M_PI * (double)slice / nSlice;
673 cosCache[slice] = cos(theta);
674 sinCache[slice] = sin(theta);
676 /* glTexCoord3f(midTexCoord + sinCache[slice] * innerTexCoordSize, */
678 /* midTexCoord + cosCache[slice] * innerTexCoordSize); */
679 glVertex3f(innerRadius * sinCache[slice], y,
680 innerRadius * cosCache[slice]);
681 /* glTexCoord3f(midTexCoord + sinCache[slice] * outerTexCoordSize, */
683 /* midTexCoord + cosCache[slice] * outerTexCoordSize); */
684 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
689 for(loop = 0; loop < nLoop; ++loop) {
690 GLfloat lowerRadius =
691 bottomRadius + (topRadius -
692 bottomRadius) * (float)loop / (nLoop);
693 GLfloat upperRadius =
694 bottomRadius + (topRadius - bottomRadius) * (float)(loop +
697 GLfloat lowerY = height * (float)loop / (nLoop);
698 GLfloat upperY = height * (float)(loop + 1) / (nLoop);
699 GLfloat factor = (topRadius - topThickness) -
700 (bottomRadius - bottomThickness);
703 glBegin(GL_QUAD_STRIP);
704 for(slice = 0; slice < nSlice; ++slice) {
705 glNormal3f(sinCache[slice], 0.0, cosCache[slice]);
706 glVertex3f(upperRadius * sinCache[slice], upperY,
707 upperRadius * cosCache[slice]);
708 glVertex3f(lowerRadius * sinCache[slice], lowerY,
709 lowerRadius * cosCache[slice]);
711 glNormal3f(0.0, 0.0, 1.0);
712 glVertex3f(0.0, upperY, upperRadius);
713 glVertex3f(0.0, lowerY, lowerRadius);
717 lowerRadius = bottomRadius - bottomThickness +
718 factor * (float)loop / (nLoop);
719 upperRadius = bottomRadius - bottomThickness +
720 factor * (float)(loop + 1) / (nLoop);
722 glBegin(GL_QUAD_STRIP);
723 glNormal3f(0.0, 0.0, -1.0);
724 glVertex3f(0.0, upperY, upperRadius);
725 glVertex3f(0.0, lowerY, lowerRadius);
726 for(slice = lastSlice; slice >= 0; --slice) {
727 glNormal3f(-sinCache[slice], 0.0, -cosCache[slice]);
728 glVertex3f(upperRadius * sinCache[slice], upperY,
729 upperRadius * cosCache[slice]);
730 glVertex3f(lowerRadius * sinCache[slice], lowerY,
731 lowerRadius * cosCache[slice]);
739 innerRadius = topRadius - topThickness;
741 glBegin(GL_QUAD_STRIP);
742 glNormal3f(0.0, 1.0, 0.0);
743 for(slice = 0; slice < nSlice; ++slice) {
744 glVertex3f(innerRadius * sinCache[slice], y,
745 innerRadius * cosCache[slice]);
747 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
749 glVertex3f(0.0, y, innerRadius);
750 glVertex3f(0.0, y, radius);
754 void drawPole(GLfloat radius, GLfloat length)
756 drawTube(radius, radius, radius, radius, length, NSLICE, NLOOPS);
759 void drawDisk3D(GLdouble inner_radius, GLdouble outer_radius,
762 drawTube(outer_radius, outer_radius, outer_radius - inner_radius,
763 outer_radius - inner_radius, height, NSLICE, NLOOPS);
766 void drawCuboid(GLfloat length, GLfloat width, GLfloat height)
768 GLfloat xmin = -length / 2.0f;
769 GLfloat xmax = length / 2.0f;
770 GLfloat zmin = -width / 2.0f;
771 GLfloat zmax = width / 2.0f;
773 GLfloat ymax = height;
778 glVertex3f(xmin, ymin, zmax); /* 0 */
779 glVertex3f(xmax, ymin, zmax); /* 1 */
780 glVertex3f(xmax, ymax, zmax); /* 2 */
781 glVertex3f(xmin, ymax, zmax); /* 3 */
784 glVertex3f(xmax, ymin, zmax); /* 1 */
785 glVertex3f(xmax, ymin, zmin); /* 5 */
786 glVertex3f(xmax, ymax, zmin); /* 6 */
787 glVertex3f(xmax, ymax, zmax); /* 2 */
790 glVertex3f(xmax, ymin, zmin); /* 5 */
791 glVertex3f(xmin, ymin, zmin); /* 4 */
792 glVertex3f(xmin, ymax, zmin); /* 7 */
793 glVertex3f(xmax, ymax, zmin); /* 6 */
796 glVertex3f(xmin, ymin, zmin); /* 4 */
797 glVertex3f(xmin, ymin, zmax); /* 0 */
798 glVertex3f(xmin, ymax, zmax); /* 3 */
799 glVertex3f(xmin, ymax, zmin); /* 7 */
802 glVertex3f(xmin, ymax, zmax); /* 3 */
803 glVertex3f(xmax, ymax, zmax); /* 2 */
804 glVertex3f(xmax, ymax, zmin); /* 6 */
805 glVertex3f(xmin, ymax, zmin); /* 7 */
808 glVertex3f(xmin, ymin, zmin); /* 4 */
809 glVertex3f(xmax, ymin, zmin); /* 5 */
810 glVertex3f(xmax, ymin, zmax); /* 1 */
811 glVertex3f(xmin, ymin, zmax); /* 0 */
820 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
821 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
822 Disk *disk = &glhanoi->disk[i];
823 GLfloat *pos = disk->position;
824 GLfloat *rot = disk->rotation;
827 glTranslatef(pos[0], pos[1], pos[2]);
829 glTranslatef(0.0, glhanoi->diskHeight / 2.0, 0.0);
830 glRotatef(rot[1], 0.0, 0.0, 1.0);
831 glTranslatef(0.0, -glhanoi->diskHeight / 2.0, 0.0);
833 glCallList(disk->displayList);
839 GLfloat getDiskRadius(int i)
841 return ((GLfloat) i + 3.0) * glhanoi->poleRadius;
846 GLfloat maxDiskRadius;
849 glhanoi->baseLength = BASE_LENGTH;
850 glhanoi->poleRadius = glhanoi->baseLength /
851 (2.0 * (3 * glhanoi->numberOfDisks + 7.0));
852 maxDiskRadius = getDiskRadius(glhanoi->numberOfDisks);
853 glhanoi->baseWidth = 2.0 * maxDiskRadius;
854 glhanoi->poleOffset = 2.0 * getDiskRadius(glhanoi->maxDiskIdx);
855 glhanoi->diskHeight = 2.0 * glhanoi->poleRadius;
856 glhanoi->baseHeight = 2.0 * glhanoi->poleRadius;
857 glhanoi->poleHeight = glhanoi->numberOfDisks *
858 glhanoi->diskHeight + glhanoi->poleRadius;
859 glhanoi->numberOfMoves = (1 << glhanoi->numberOfDisks) - 1;
860 glhanoi->boardSize = glhanoi->baseLength * 0.5 * (1.0 + sqrt(5.0));
862 for(i = 0; i < 3; i++) {
863 if((glhanoi->pole[i].data =
864 calloc(glhanoi->numberOfDisks, sizeof(Disk *))) == NULL) {
865 fprintf(stderr, "%s: out of memory creating stack %d\n",
869 glhanoi->pole[i].size = glhanoi->numberOfDisks;
871 if((glhanoi->diskPos =
872 calloc(glhanoi->numberOfDisks, sizeof(double))) == NULL) {
873 fprintf(stderr, "%s: out of memory creating diskPos\n", progname);
877 glhanoi->the_rotator = make_rotator(0.1, 0.025, 0, 1, 0.005, False);
878 glhanoi->button_down_p = False;
880 glhanoi->src = glhanoi->oldsrc = 0;
881 glhanoi->tmp = glhanoi->oldtmp = 1;
882 glhanoi->dst = glhanoi->olddst = 2;
887 glhanoi->camera[0] = 0.0;
888 glhanoi->camera[1] = 0.0;
889 glhanoi->camera[2] = 0.0;
890 glhanoi->centre[0] = 0.0;
891 glhanoi->centre[1] = glhanoi->poleHeight * 3.0;
892 glhanoi->centre[2] = 0.0;
896 * noise_improved.c - based on ImprovedNoise.java
897 * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
899 static double fade(double t)
901 return t * t * t * (t * (t * 6 - 15) + 10);
904 static double grad(int hash, double x, double y, double z)
906 int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
907 double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
908 v = h < 4 ? y : h == 12 || h == 14 ? x : z;
909 return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
912 static int p[512], permutation[] = { 151, 160, 137, 91, 90, 15,
913 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
914 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26,
915 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
916 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
917 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60,
918 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143,
919 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
920 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198,
921 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118,
922 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
923 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
924 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108,
925 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251,
926 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145,
927 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
928 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
929 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
933 static void initNoise(void)
936 for(i = 0; i < 256; i++)
937 p[256 + i] = p[i] = permutation[i];
940 double improved_noise(double x, double y, double z)
943 int A, AA, AB, B, BA, BB;
944 int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
945 Y = (int)floor(y) & 255, /* CONTAINS POINT. */
946 Z = (int)floor(z) & 255;
947 static int start = 0;
952 x -= floor(x); /* FIND RELATIVE X,Y,Z */
953 y -= floor(y); /* OF POINT IN CUBE. */
955 u = fade(x), /* COMPUTE FADE CURVES */
956 v = fade(y), /* FOR EACH OF X,Y,Z. */
958 A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, /* HASH COORDINATES OF */
959 B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; /* THE 8 CUBE CORNERS, */
960 return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), /* AND ADD */
961 grad(p[BA], x - 1, y, z)), /* BLENDED */
962 lerp(u, grad(p[AB], x, y - 1, z), /* RESULTS */
963 grad(p[BB], x - 1, y - 1, z))), /* FROM 8 CORNERS */
964 lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)), /* OF CUBE */
965 lerp(u, grad(p[AB + 1], x, y - 1, z - 1),
966 grad(p[BB + 1], x - 1, y - 1, z - 1))));
970 * end noise_improved.c - based on ImprovedNoise.java
975 /* GLfloat *points; */
978 typedef struct tex_col_t tex_col_t;
980 GLubyte *makeTexture(int x_size, int y_size, int z_size,
981 GLuint(*texFunc) (double, double, double,
982 tex_col_t *), tex_col_t * colours)
985 GLubyte *textureData;
991 calloc(x_size * y_size * z_size, sizeof(GLuint))) == NULL) {
1000 texturePtr = (void *)textureData;
1001 for(k = 0; k < z_size; k++, z += zi) {
1003 for(j = 0; j < y_size; j++, y += yi) {
1005 for(i = 0; i < x_size; i++, x += xi) {
1006 *texturePtr = texFunc(x, y, z, colours);
1014 tex_col_t makeMarbleColours(void)
1016 tex_col_t marbleColours;
1019 marbleColours.colours = calloc(sizeof(GLuint), ncols);
1020 marbleColours.ncols = ncols;
1022 marbleColours.colours[0] = 0x3f3f3f3f;
1023 marbleColours.colours[1] = 0xffffffff;
1025 return marbleColours;
1028 double turb(double x, double y, double z, int octaves)
1033 for(oct = 0; oct < octaves; ++oct) {
1034 r += fabs(improved_noise(freq * x, freq * y, freq * z)) / freq;
1040 void perturb(double *x, double *y, double *z, double scale)
1042 double t = scale * turb(*x, *y, *z, 4);
1048 double f_m(double x, double y, double z)
1050 return sin(3.0 * M_PI * x);
1053 GLuint C_m(double x, const tex_col_t * tex_cols)
1055 int r = tex_cols->colours[0] & 0xff;
1056 int g = tex_cols->colours[0] >> 8 & 0xff;
1057 int b = tex_cols->colours[0] >> 16 & 0xff;
1062 factor = (1.0 + sin(2.0 * M_PI * x)) / 2.0;
1064 r1 = (tex_cols->colours[1] & 0xff);
1065 g1 = (tex_cols->colours[1] >> 8 & 0xff);
1066 b1 = (tex_cols->colours[1] >> 16 & 0xff);
1068 r += (int)(factor * (r1 - r));
1069 g += (int)(factor * (g1 - g));
1070 b += (int)(factor * (b1 - b));
1072 return 0xff000000 | (b << 16) | (g << 8) | r;
1076 GLuint makeMarbleTexture(double x, double y, double z, tex_col_t * colours)
1078 perturb(&x, &y, &z, MARBLE_SCALE);
1079 return C_m(f_m(x, y, z), colours);
1082 void setTexture(int n)
1084 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[n]);
1087 int makeTextures(void)
1089 GLubyte *marbleTexture;
1090 tex_col_t marbleColours;
1092 glGenTextures(N_TEXTURES, glhanoi->textureNames);
1094 marbleColours = makeMarbleColours();
1096 makeTexture(MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 1,
1097 makeMarbleTexture, &marbleColours)) == NULL) {
1101 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[0]);
1102 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1103 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1104 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1106 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1107 MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 0,
1108 GL_RGBA, GL_UNSIGNED_BYTE, marbleTexture);
1109 free(marbleTexture);
1114 void initFloor(void)
1117 float tileSize = glhanoi->boardSize / BOARD_SQUARES;
1118 float x0, x1, z0, z1;
1119 float tx0, tx1, tz0, tz1;
1120 float *col = cWhite;
1121 float texIncr = 1.0 / BOARD_SQUARES;
1123 if((glhanoi->floorList = glGenLists(1)) == 0) {
1124 fprintf(stderr, "can't allocate memory for floor display list\n");
1127 glNewList(glhanoi->floorList, GL_COMPILE);
1128 x0 = -glhanoi->boardSize / 2.0;
1130 setMaterial(col, cWhite, 128);
1133 for(i = 0; i < BOARD_SQUARES; i++, x0 += tileSize, tx0 += texIncr) {
1135 tx1 = tx0 + texIncr;
1136 z0 = -glhanoi->boardSize / 2.0;
1138 for(j = 0; j < BOARD_SQUARES; j++, z0 += tileSize, tz0 += texIncr) {
1139 int colIndex = (i + j) & 0x1;
1142 tz1 = tz0 + texIncr;
1149 setMaterial(col, cWhite, 100);
1153 glTexCoord2d(tx0, tz0);
1154 glVertex3f(x0, 0.0, z0);
1156 glTexCoord2d(tx0, tz1);
1157 glVertex3f(x0, 0.0, z1);
1159 glTexCoord2d(tx1, tz1);
1160 glVertex3f(x1, 0.0, z1);
1162 glTexCoord2d(tx1, tz0);
1163 glVertex3f(x1, 0.0, z0);
1170 void initTowers(void)
1172 if((glhanoi->baseList = glGenLists(1)) == 0) {
1173 fprintf(stderr, "can't allocate memory for towers display list\n");
1176 glNewList(glhanoi->baseList, GL_COMPILE);
1177 setMaterial(baseColor, cWhite, 50);
1178 drawCuboid(glhanoi->baseLength, glhanoi->baseWidth,
1179 glhanoi->baseHeight);
1183 if((glhanoi->poleList = glGenLists(1)) == 0) {
1184 fprintf(stderr, "can't allocate memory for towers display list\n");
1187 glNewList(glhanoi->poleList, GL_COMPILE);
1189 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
1190 setMaterial(poleColor, cWhite, 50);
1191 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1193 glTranslatef(-glhanoi->poleOffset, 0.0, 0.0);
1194 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1196 glTranslatef(glhanoi->poleOffset, 0.0, 0.0);
1197 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1202 double cfunc(double x)
1206 return (1.0 / 12.0) / (1.0 / 7.0) * x;
1209 return (1.0 + 1.0 / 6.0) * x - 1.0 / 6.0;
1212 return (2.0 + 1.0 / 3.0) * x - 2.0 / 3.0;
1215 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1217 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1220 void initDisks(void)
1224 (Disk *) calloc(glhanoi->numberOfDisks, sizeof(Disk))) == NULL) {
1225 perror("initDisks");
1229 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
1230 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1231 double f = cfunc((GLfloat) i / (GLfloat) glhanoi->numberOfDisks);
1232 GLfloat diskColor = f * 360.0;
1234 Disk *disk = &glhanoi->disk[i];
1237 disk->position[0] = -glhanoi->poleOffset;
1238 disk->position[1] = glhanoi->diskHeight * height;
1239 disk->position[2] = 0.0;
1240 disk->rotation[0] = 0.0;
1241 disk->rotation[1] = 0.0;
1242 disk->rotation[2] = 0.0;
1244 color[0] = diskColor;
1247 HSVtoRGBv(color, color);
1249 if((disk->displayList = glGenLists(1)) == 0) {
1251 "can't allocate memory for disk %d display list\n", i);
1254 glNewList(disk->displayList, GL_COMPILE);
1255 setMaterial(color, cWhite, 100.0);
1256 drawDisk3D(glhanoi->poleRadius, getDiskRadius(i),
1257 glhanoi->diskHeight);
1260 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
1261 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1262 int h = glhanoi->maxDiskIdx - i;
1263 glhanoi->diskPos[h] = glhanoi->diskHeight * height;
1264 push(glhanoi->src, &glhanoi->disk[i]);
1268 void initLights(Bool state)
1271 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1272 glLightfv(GL_LIGHT0, GL_AMBIENT, amb0);
1273 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
1274 glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1276 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1277 glLightfv(GL_LIGHT1, GL_AMBIENT, amb1);
1278 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
1279 glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1281 glEnable(GL_LIGHTING);
1282 glEnable(GL_LIGHT0);
1283 glEnable(GL_LIGHT1);
1285 glDisable(GL_LIGHTING);
1289 void drawFloor(void)
1291 glCallList(glhanoi->floorList);
1294 void drawTowers(void)
1296 glCallList(glhanoi->baseList);
1297 glCallList(glhanoi->poleList);
1300 /* Window management, etc
1302 void reshape_glhanoi(ModeInfo * mi, int width, int height)
1304 glViewport(0, 0, (GLint) width, (GLint) height);
1306 glMatrixMode(GL_PROJECTION);
1308 gluPerspective(30.0, (GLdouble) width / (GLdouble) height, 1.0,
1309 2 * MAX_CAMERA_RADIUS);
1311 glMatrixMode(GL_MODELVIEW);
1314 glClear(GL_COLOR_BUFFER_BIT);
1317 void init_glhanoi(ModeInfo * mi)
1321 (glhcfg *) calloc(MI_NUM_SCREENS(mi), sizeof(glhcfg));
1323 fprintf(stderr, "%s: out of memory creating configs\n",
1327 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1330 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1331 glhanoi->glx_context = init_GL(mi);
1332 glhanoi->numberOfDisks = MI_BATCHCOUNT(mi);
1333 glhanoi->maxDiskIdx = glhanoi->numberOfDisks - 1;
1334 glhanoi->wire = MI_IS_WIREFRAME(mi);
1335 glhanoi->light = light;
1337 glhanoi->texture = texture;
1339 reshape_glhanoi(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1342 glhanoi->light = FALSE;
1343 glhanoi->fog = FALSE;
1344 glhanoi->texture = FALSE;
1347 initLights(!glhanoi->wire && glhanoi->light);
1348 if(makeTextures() != 0) {
1349 fprintf(stderr, "can't allocate memory for marble texture\n");
1359 glEnable(GL_DEPTH_TEST);
1360 glEnable(GL_NORMALIZE);
1361 glEnable(GL_CULL_FACE);
1362 glShadeModel(GL_SMOOTH);
1364 glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], 1.0);
1365 glFogi(GL_FOG_MODE, GL_LINEAR);
1366 glFogfv(GL_FOG_COLOR, fogcolor);
1367 glFogf(GL_FOG_DENSITY, 0.35f);
1368 glHint(GL_FOG_HINT, GL_NICEST);
1369 glFogf(GL_FOG_START, MIN_CAMERA_RADIUS);
1370 glFogf(GL_FOG_END, MAX_CAMERA_RADIUS / 1.9);
1375 glhanoi->duration = START_DURATION;
1379 void draw_glhanoi(ModeInfo * mi)
1381 Display *dpy = MI_DISPLAY(mi);
1382 Window window = MI_WINDOW(mi);
1384 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1386 if(!glhanoi->glx_context)
1388 glPolygonMode(GL_FRONT, glhanoi->wire ? GL_LINE : GL_FILL);
1390 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1397 if(!glhanoi->wire && glhanoi->texture) {
1398 glEnable(GL_TEXTURE_2D);
1401 glDisable(GL_TEXTURE_2D);
1411 glXSwapBuffers(dpy, window);
1414 Bool glhanoi_handle_event(ModeInfo * mi, XEvent * event)
1416 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1418 if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
1419 glhanoi->button_down_p = True;
1420 glhanoi->drag_x = event->xbutton.x;
1421 glhanoi->drag_y = event->xbutton.y;
1423 } else if(event->xany.type == ButtonRelease
1424 && event->xbutton.button == Button1) {
1425 glhanoi->button_down_p = False;
1427 } else if(event->xany.type == ButtonPress &&
1428 (event->xbutton.button == Button4
1429 || event->xbutton.button == Button5)) {
1430 switch (event->xbutton.button) {
1432 glhanoi->camera[2] += 0.01;
1435 glhanoi->camera[2] -= 0.01;
1439 "glhanoi: unknown button in mousewheel handler\n");
1442 } else if(event->xany.type == MotionNotify
1443 && glhanoi_cfg->button_down_p) {
1446 x_diff = event->xbutton.x - glhanoi->drag_x;
1447 y_diff = event->xbutton.y - glhanoi->drag_y;
1449 glhanoi->camera[0] = (float)x_diff / (float)MI_WIDTH(mi);
1450 glhanoi->camera[1] = (float)y_diff / (float)MI_HEIGHT(mi);
1457 void release_glhanoi(ModeInfo * mi)
1459 if(glhanoi_cfg != NULL) {
1461 for(screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1464 glhcfg *glh = &glhanoi_cfg[screen];
1465 glDeleteLists(glh->floorList, 1);
1466 glDeleteLists(glh->baseList, 1);
1467 glDeleteLists(glh->poleList, 1);
1468 for(j = 0; j < glh->numberOfDisks; ++j) {
1469 glDeleteLists(glh->disk[j].displayList, 1);
1472 for(i = 0; i < 3; i++) {
1473 if(glh->pole[i].data != NULL) {
1474 free(glh->pole[i].data);
1481 glDeleteLists(glhanoi->textureNames[0], 2);