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_LIGHT "True"
19 #define DEF_FOG "False"
20 #define DEF_TEXTURE "True"
22 #define DEFAULTS "*delay: 15000\n" \
27 # define refresh_glhanoi 0
31 #define START_DURATION 1.0
32 #define FINISH_DURATION 1.0
33 #define BASE_LENGTH 30.0
34 #define BOARD_SQUARES 8
36 #define MAX_CAMERA_RADIUS 250.0
37 #define MIN_CAMERA_RADIUS 75.0
39 #define MARBLE_SCALE 1.01
42 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
49 #define MARBLE_TEXTURE_SIZE 256
52 #define countof(x) (sizeof((x))/sizeof((*x)))
55 #include "xlockmore.h"
57 #ifdef USE_GL /* whole file */
59 typedef struct timeval glhtime;
61 static double getTime(void)
64 #ifdef GETTIMEOFDAY_TWO_ARGS
65 gettimeofday(&t, NULL);
66 #else /* !GETTIMEOFDAY_TWO_ARGS */
68 #endif /* !GETTIMEOFDAY_TWO_ARGS */
69 return t.tv_sec + t.tv_usec / 1000000.0;
90 GLfloat xmin, xmax, ymin;
93 GLfloat ucostheta, usintheta;
105 GLXContext *glx_context;
134 float *diskPos; /* pre-computed disk positions on rods */
139 int floorpolys, basepolys, polepolys;
142 rotator *the_rotator;
145 GLuint textureNames[N_TEXTURES];
154 static glhcfg *glhanoi_cfg = NULL;
159 static XrmOptionDescRec opts[] = {
160 {"-light", ".glhanoi.light", XrmoptionNoArg, "true"},
161 {"+light", ".glhanoi.light", XrmoptionNoArg, "false"},
162 {"-fog", ".glhanoi.fog", XrmoptionNoArg, "true"},
163 {"+fog", ".glhanoi.fog", XrmoptionNoArg, "false"},
164 {"-texture", ".glhanoi.texture", XrmoptionNoArg, "true"},
165 {"+texture", ".glhanoi.texture", XrmoptionNoArg, "false"}
168 static argtype vars[] = {
169 {&light, "light", "Light", DEF_LIGHT, t_Bool},
170 {&fog, "fog", "Fog", DEF_FOG, t_Bool},
171 {&texture, "texture", "Texture", DEF_TEXTURE, t_Bool}
174 static OptionStruct desc[] = {
175 {"+/-light", "whether to light the scene"},
176 {"+/-fog", "whether to apply fog to the scene"},
177 {"+/-texture", "whether to apply texture to the scene"}
180 ENTRYPOINT ModeSpecOpt glhanoi_opts = { countof(opts), opts, countof(vars), vars, desc };
184 ModStruct glhanoi_description = {
185 "glhanoi", "init_glhanoi", "draw_glhanoi", "release_glhanoi",
186 "draw_glhanoi", "init_glhanoi", NULL, &glhanoi_opts,
187 1000, 1, 2, 1, 4, 1.0, "",
188 "Towers of Hanoi", 0, NULL
193 static const GLfloat cBlack[] = { 0.0, 0.0, 0.0, 1.0 };
194 static const GLfloat cWhite[] = { 1.0, 1.0, 1.0, 1.0 };
195 static const GLfloat poleColor[] = { 0.545, 0.137, 0.137 };
196 static const GLfloat baseColor[] = { 0.34, 0.34, 0.48 };
197 static const GLfloat fogcolor[] = { 0.5, 0.5, 0.5 };
199 static const float left[] = { 1.0, 0.0, 0.0 };
200 static const float up[] = { 0.0, 1.0, 0.0 };
201 static const float front[] = { 0.0, 0.0, 1.0 };
202 static const float right[] = { -1.0, 0.0, 0.0 };
203 static const float down[] = { 0.0, -1.0, 0.0 };
204 static const float back[] = { 0.0, 0.0, -1.0 };
206 static const GLfloat pos0[4] = { 50.0, 50.0, 50.0, 0.0 };
207 static const GLfloat amb0[4] = { 0.0, 0.0, 0.0, 1.0 };
208 static const GLfloat dif0[4] = { 1.0, 1.0, 1.0, 1.0 };
209 static const GLfloat spc0[4] = { 0.0, 1.0, 1.0, 1.0 };
211 static const GLfloat pos1[4] = { -50.0, 50.0, -50.0, 0.0 };
212 static const GLfloat amb1[4] = { 0.0, 0.0, 0.0, 1.0 };
213 static const GLfloat dif1[4] = { 1.0, 1.0, 1.0, 1.0 };
214 static const GLfloat spc1[4] = { 1.0, 1.0, 1.0, 1.0 };
216 static float g = 3.0 * 9.80665; /* hmm, looks like we need more gravity, Scotty... */
218 #define DOPUSH(X, Y) (((X)->count) >= ((X)->size)) ? NULL : ((X)->data[(X)->count++] = (Y))
219 #define DOPOP(X) (X)->count <= 0 ? NULL : ((X)->data[--((X)->count)])
221 static Disk *push(glhcfg *glhanoi, int idx, Disk * d)
223 return DOPUSH(&glhanoi->pole[idx], d);
226 static Disk *pop(glhcfg *glhanoi, int idx)
228 return DOPOP(&glhanoi->pole[idx]);
231 static inline void swap(int *x, int *y)
239 * magic - it's magic...
241 static int magic(int i)
246 while((i & 0x01) == 0) {
250 return count % 2 == 0;
254 static float distance(float *p0, float *p1)
260 return (float)sqrt(x * x + y * y + z * z);
264 static GLfloat A(GLfloat a, GLfloat b, GLfloat c)
267 return c / (a * b - 0.25 * sum * sum);
270 static void moveSetup(glhcfg *glhanoi, Disk * disk)
274 int src = glhanoi->src;
275 int dst = glhanoi->dst;
277 GLfloat sintheta, costheta;
279 if(glhanoi->state != FINISHED && random() % 6 == 0) {
281 -180.0 * (2 - 2 * random() % 2) * (random() % 3 + 1);
283 disk->rotAngle = -180.0;
286 disk->base0 = glhanoi->diskPos[glhanoi->pole[src].count];
289 FINISHED ? disk->base0 : glhanoi->diskPos[glhanoi->pole[dst].
292 disk->xmin = glhanoi->poleOffset * (src - 1);
293 disk->xmax = glhanoi->poleOffset * (dst - 1);
294 disk->ymin = glhanoi->poleHeight;
296 glhanoi->poleHeight + fabs(disk->xmax -
297 disk->xmin) * (glhanoi->state ==
303 numberOfDisks : 1.0);
305 h = ymax - disk->ymin;
306 theta = atan((disk->xmin - disk->xmax) * A(disk->xmin, disk->xmax, h));
309 costheta = cos(theta);
310 sintheta = sin(theta);
314 (2.0 * A(disk->xmin, disk->xmax, h) * costheta * costheta)));
315 disk->usintheta = u * sintheta;
316 disk->ucostheta = u * costheta;
318 (-u + sqrt(u * u + 2.0 * g * fabs(disk->ymin - disk->base0))) / g;
319 disk->u1 = u + g * disk->t1;
320 disk->t2 = 2.0 * disk->usintheta / g;
321 disk->u2 = disk->usintheta - g * disk->t2;
324 static void makeMove(glhcfg *glhanoi)
326 int fudge = glhanoi->move + 2;
327 int magicNumber = magic(fudge);
329 glhanoi->currentDisk = pop(glhanoi, glhanoi->src);
330 moveSetup(glhanoi, glhanoi->currentDisk);
331 push(glhanoi, glhanoi->dst, glhanoi->currentDisk);
334 if(fudge == 1 || magicNumber) {
335 swap(&glhanoi->src, &glhanoi->tmp);
337 if(fudge == 0 || glhanoi->magicNumber) {
338 swap(&glhanoi->dst, &glhanoi->tmp);
340 glhanoi->magicNumber = magicNumber;
343 static double lerp(double alpha, double start, double end)
345 return start + alpha * (end - start);
348 static void upfunc(GLdouble t, Disk * d)
350 d->position[0] = d->xmin;
351 d->position[1] = d->base0 + (d->u1 - 0.5 * g * t) * t;
353 d->rotation[1] = 0.0;
356 static void parafunc(GLdouble t, Disk * d)
358 d->position[0] = d->xmin + d->ucostheta * t;
359 d->position[1] = d->ymin + (d->usintheta - 0.5 * g * t) * t;
362 d->rotAngle * (d->position[0] - d->xmin) / (d->xmax - d->xmin);
365 static void downfunc(GLdouble t, Disk * d)
367 d->position[0] = d->xmax;
368 d->position[1] = d->ymin + (d->u2 - 0.5 * g * t) * t;
370 d->rotation[1] = 0.0;
373 static Bool computePosition(GLfloat t, Disk * d)
375 Bool finished = False;
379 } else if(t < d->t1 + d->t2) {
380 parafunc(t - d->t1, d);
382 downfunc(t - d->t1 - d->t2, d);
383 if(d->position[1] <= d->base1) {
384 d->position[1] = d->base1;
391 static void updateView(glhcfg *glhanoi)
393 double longitude, latitude, radius;
394 double a, b, c, A, B;
396 get_position(glhanoi->the_rotator, NULL, NULL, &radius,
397 !glhanoi->button_down_p);
398 get_rotation(glhanoi->the_rotator, &longitude, &latitude, NULL,
399 !glhanoi->button_down_p);
400 longitude += glhanoi->camera[0];
401 latitude += glhanoi->camera[1];
402 radius += glhanoi->camera[2];
403 longitude = longitude - floor(longitude);
404 latitude = latitude - floor(latitude);
405 radius = radius - floor(radius);
407 latitude = 1.0 - latitude;
410 radius = 1.0 - radius;
413 b = glhanoi->centre[1];
414 c = (MIN_CAMERA_RADIUS +
415 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS));
416 A = M_PI / 4.0 * (1.0 - latitude);
417 a = sqrt(b * b + c * c - 2.0 * b * c * cos(A));
418 B = asin(sin(A) * b / a);
419 glRotatef(-B * 180 / M_PI, 1.0, 0.0, 0.0);
421 glTranslatef(0.0f, 0.0f,
422 -(MIN_CAMERA_RADIUS +
423 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS)));
424 glRotatef(longitude * 360.0, 0.0f, 1.0f, 0.0f);
425 glRotatef(latitude * 180.0, cos(longitude * 2.0 * M_PI), 0.0,
426 sin(longitude * 2.0 * M_PI));
429 static void changeState(glhcfg *glhanoi, State state)
431 glhanoi->state = state;
432 glhanoi->startTime = getTime();
435 static void update_glhanoi(glhcfg *glhanoi)
437 double t = getTime() - glhanoi->startTime;
441 switch (glhanoi->state) {
443 if(t < glhanoi->duration) {
447 if(glhanoi->numberOfDisks % 2 == 0) {
448 swap(&glhanoi->tmp, &glhanoi->dst);
450 glhanoi->magicNumber = 1;
452 changeState(glhanoi, MOVE_DISK);
456 if(computePosition(t, glhanoi->currentDisk)) {
457 changeState(glhanoi, MOVE_FINISHED);
462 if(++glhanoi->move < glhanoi->numberOfMoves) {
464 changeState(glhanoi, MOVE_DISK);
466 glhanoi->duration = FINISH_DURATION;
467 changeState(glhanoi, FINISHED);
472 while(t < glhanoi->duration) {
475 glhanoi->src = glhanoi->olddst;
476 glhanoi->dst = glhanoi->oldsrc;
477 for(i = 0; i < glhanoi->numberOfDisks; ++i) {
478 Disk *disk = pop(glhanoi, glhanoi->src);
479 assert(disk != NULL);
480 moveSetup(glhanoi, disk);
482 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
483 push(glhanoi, glhanoi->dst, &glhanoi->disk[i]);
485 changeState(glhanoi, MONEY_SHOT);
490 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
491 double delay = 0.25 * i;
499 finished = computePosition(t - delay, &glhanoi->disk[i]);
500 glhanoi->disk[i].rotation[1] = 0.0;
507 glhanoi->src = glhanoi->oldsrc;
508 glhanoi->tmp = glhanoi->oldtmp;
509 glhanoi->dst = glhanoi->olddst;
510 changeState(glhanoi, START);
516 fprintf(stderr, "Invalid state\n");
521 static void HSVtoRGBf(GLfloat h, GLfloat s, GLfloat v,
522 GLfloat * r, GLfloat * g, GLfloat * b)
529 GLfloat i, f, p, q, t;
533 h /= 60.0; /* h now in [0,6). */
534 i = floor((double)h); /* i now largest integer <= h */
535 f = h - i; /* f is no fractional part of h */
537 q = v * (1.0 - (s * f));
538 t = v * (1.0 - (s * (1.0 - f)));
574 static void HSVtoRGBv(GLfloat * hsv, GLfloat * rgb)
576 HSVtoRGBf(hsv[0], hsv[1], hsv[2], &rgb[0], &rgb[1], &rgb[2]);
579 static void setMaterial(const GLfloat color[3], const GLfloat hlite[3], int shininess)
582 glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
583 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
584 glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
588 * drawTube: I know all this stuff is available in gluQuadrics
589 * but I'd originally intended to texture the poles with a 3D wood
590 * texture, but I was having difficulty getting wood... what? Why
591 * are all you Amercians laughing..? Anyway, I don't know if enough
592 * people's hardware supports 3D textures, so I didn't bother (xorg
593 * ATI server doesn't :-( )
595 static int drawTube(GLdouble bottomRadius, GLdouble topRadius,
596 GLdouble bottomThickness, GLdouble topThickness,
597 GLdouble height, GLuint nSlice, GLuint nLoop)
601 GLfloat *cosCache = malloc(sizeof(GLfloat) * nSlice);
602 GLfloat *sinCache = malloc(sizeof(GLfloat) * nSlice);
605 GLint lastSlice = nSlice - 1;
610 if(bottomThickness > bottomRadius) {
611 bottomThickness = bottomRadius;
613 if(topThickness > topRadius) {
614 topThickness = topRadius;
616 if(bottomThickness < 0.0) {
617 bottomThickness = 0.0;
619 if(topThickness < 0.0) {
622 if(topRadius >= bottomRadius) {
623 maxRadius = topRadius;
625 maxRadius = bottomRadius;
630 radius = bottomRadius;
631 innerRadius = bottomRadius - bottomThickness;
632 /* innerTexCoordSize = texCoordSize * innerRadius / maxRadius; */
633 /* outerTexCoordSize = texCoordSize * radius / maxRadius; */
634 /* yTexCoord = minTexCoord; */
636 glBegin(GL_QUAD_STRIP);
638 glNormal3f(0.0, -1.0, 0.0);
640 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + innerTexCoordSize); */
641 glVertex3f(0.0, y, innerRadius);
643 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + outerTexCoordSize); */
644 glVertex3f(0.0, y, radius);
646 for(slice = lastSlice; slice >= 0; --slice) {
647 GLfloat theta = 2.0 * M_PI * (double)slice / nSlice;
649 cosCache[slice] = cos(theta);
650 sinCache[slice] = sin(theta);
652 /* glTexCoord3f(midTexCoord + sinCache[slice] * innerTexCoordSize, */
654 /* midTexCoord + cosCache[slice] * innerTexCoordSize); */
655 glVertex3f(innerRadius * sinCache[slice], y,
656 innerRadius * cosCache[slice]);
657 /* glTexCoord3f(midTexCoord + sinCache[slice] * outerTexCoordSize, */
659 /* midTexCoord + cosCache[slice] * outerTexCoordSize); */
660 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
666 for(loop = 0; loop < nLoop; ++loop) {
667 GLfloat lowerRadius =
668 bottomRadius + (topRadius -
669 bottomRadius) * (float)loop / (nLoop);
670 GLfloat upperRadius =
671 bottomRadius + (topRadius - bottomRadius) * (float)(loop +
674 GLfloat lowerY = height * (float)loop / (nLoop);
675 GLfloat upperY = height * (float)(loop + 1) / (nLoop);
676 GLfloat factor = (topRadius - topThickness) -
677 (bottomRadius - bottomThickness);
680 glBegin(GL_QUAD_STRIP);
681 for(slice = 0; slice < nSlice; ++slice) {
682 glNormal3f(sinCache[slice], 0.0, cosCache[slice]);
683 glVertex3f(upperRadius * sinCache[slice], upperY,
684 upperRadius * cosCache[slice]);
685 glVertex3f(lowerRadius * sinCache[slice], lowerY,
686 lowerRadius * cosCache[slice]);
689 glNormal3f(0.0, 0.0, 1.0);
690 glVertex3f(0.0, upperY, upperRadius);
691 glVertex3f(0.0, lowerY, lowerRadius);
696 lowerRadius = bottomRadius - bottomThickness +
697 factor * (float)loop / (nLoop);
698 upperRadius = bottomRadius - bottomThickness +
699 factor * (float)(loop + 1) / (nLoop);
701 glBegin(GL_QUAD_STRIP);
702 glNormal3f(0.0, 0.0, -1.0);
703 glVertex3f(0.0, upperY, upperRadius);
704 glVertex3f(0.0, lowerY, lowerRadius);
705 for(slice = lastSlice; slice >= 0; --slice) {
706 glNormal3f(-sinCache[slice], 0.0, -cosCache[slice]);
707 glVertex3f(upperRadius * sinCache[slice], upperY,
708 upperRadius * cosCache[slice]);
709 glVertex3f(lowerRadius * sinCache[slice], lowerY,
710 lowerRadius * cosCache[slice]);
719 innerRadius = topRadius - topThickness;
721 glBegin(GL_QUAD_STRIP);
722 glNormal3f(0.0, 1.0, 0.0);
723 for(slice = 0; slice < nSlice; ++slice) {
724 glVertex3f(innerRadius * sinCache[slice], y,
725 innerRadius * cosCache[slice]);
727 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
730 glVertex3f(0.0, y, innerRadius);
731 glVertex3f(0.0, y, radius);
736 static int drawPole(GLfloat radius, GLfloat length)
738 return drawTube(radius, radius, radius, radius, length, NSLICE, NLOOPS);
741 static int drawDisk3D(GLdouble inner_radius, GLdouble outer_radius,
744 return drawTube(outer_radius, outer_radius, outer_radius - inner_radius,
745 outer_radius - inner_radius, height, NSLICE, NLOOPS);
748 static int drawCuboid(GLfloat length, GLfloat width, GLfloat height)
750 GLfloat xmin = -length / 2.0f;
751 GLfloat xmax = length / 2.0f;
752 GLfloat zmin = -width / 2.0f;
753 GLfloat zmax = width / 2.0f;
755 GLfloat ymax = height;
761 glVertex3f(xmin, ymin, zmax); /* 0 */
762 glVertex3f(xmax, ymin, zmax); /* 1 */
763 glVertex3f(xmax, ymax, zmax); /* 2 */
764 glVertex3f(xmin, ymax, zmax); /* 3 */
768 glVertex3f(xmax, ymin, zmax); /* 1 */
769 glVertex3f(xmax, ymin, zmin); /* 5 */
770 glVertex3f(xmax, ymax, zmin); /* 6 */
771 glVertex3f(xmax, ymax, zmax); /* 2 */
775 glVertex3f(xmax, ymin, zmin); /* 5 */
776 glVertex3f(xmin, ymin, zmin); /* 4 */
777 glVertex3f(xmin, ymax, zmin); /* 7 */
778 glVertex3f(xmax, ymax, zmin); /* 6 */
782 glVertex3f(xmin, ymin, zmin); /* 4 */
783 glVertex3f(xmin, ymin, zmax); /* 0 */
784 glVertex3f(xmin, ymax, zmax); /* 3 */
785 glVertex3f(xmin, ymax, zmin); /* 7 */
789 glVertex3f(xmin, ymax, zmax); /* 3 */
790 glVertex3f(xmax, ymax, zmax); /* 2 */
791 glVertex3f(xmax, ymax, zmin); /* 6 */
792 glVertex3f(xmin, ymax, zmin); /* 7 */
796 glVertex3f(xmin, ymin, zmin); /* 4 */
797 glVertex3f(xmax, ymin, zmin); /* 5 */
798 glVertex3f(xmax, ymin, zmax); /* 1 */
799 glVertex3f(xmin, ymin, zmax); /* 0 */
805 static int drawDisks(glhcfg *glhanoi)
811 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
812 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
813 Disk *disk = &glhanoi->disk[i];
814 GLfloat *pos = disk->position;
815 GLfloat *rot = disk->rotation;
818 glTranslatef(pos[0], pos[1], pos[2]);
820 glTranslatef(0.0, glhanoi->diskHeight / 2.0, 0.0);
821 glRotatef(rot[1], 0.0, 0.0, 1.0);
822 glTranslatef(0.0, -glhanoi->diskHeight / 2.0, 0.0);
824 glCallList(disk->displayList);
825 polys += disk->polys;
832 static GLfloat getDiskRadius(glhcfg *glhanoi, int i)
834 return ((GLfloat) i + 3.0) * glhanoi->poleRadius;
837 static void initData(glhcfg *glhanoi)
839 GLfloat maxDiskRadius;
842 glhanoi->baseLength = BASE_LENGTH;
843 glhanoi->poleRadius = glhanoi->baseLength /
844 (2.0 * (3 * glhanoi->numberOfDisks + 7.0));
845 maxDiskRadius = getDiskRadius(glhanoi, glhanoi->numberOfDisks);
846 glhanoi->baseWidth = 2.0 * maxDiskRadius;
847 glhanoi->poleOffset = 2.0 * getDiskRadius(glhanoi, glhanoi->maxDiskIdx);
848 glhanoi->diskHeight = 2.0 * glhanoi->poleRadius;
849 glhanoi->baseHeight = 2.0 * glhanoi->poleRadius;
850 glhanoi->poleHeight = glhanoi->numberOfDisks *
851 glhanoi->diskHeight + glhanoi->poleRadius;
852 glhanoi->numberOfMoves = (1 << glhanoi->numberOfDisks) - 1;
853 glhanoi->boardSize = glhanoi->baseLength * 0.5 * (1.0 + sqrt(5.0));
855 for(i = 0; i < 3; i++) {
856 if((glhanoi->pole[i].data =
857 calloc(glhanoi->numberOfDisks, sizeof(Disk *))) == NULL) {
858 fprintf(stderr, "%s: out of memory creating stack %d\n",
862 glhanoi->pole[i].size = glhanoi->numberOfDisks;
864 if((glhanoi->diskPos =
865 calloc(glhanoi->numberOfDisks, sizeof(double))) == NULL) {
866 fprintf(stderr, "%s: out of memory creating diskPos\n", progname);
870 glhanoi->the_rotator = make_rotator(0.1, 0.025, 0, 1, 0.005, False);
871 glhanoi->button_down_p = False;
873 glhanoi->src = glhanoi->oldsrc = 0;
874 glhanoi->tmp = glhanoi->oldtmp = 1;
875 glhanoi->dst = glhanoi->olddst = 2;
878 static void initView(glhcfg *glhanoi)
880 glhanoi->camera[0] = 0.0;
881 glhanoi->camera[1] = 0.0;
882 glhanoi->camera[2] = 0.0;
883 glhanoi->centre[0] = 0.0;
884 glhanoi->centre[1] = glhanoi->poleHeight * 3.0;
885 glhanoi->centre[2] = 0.0;
889 * noise_improved.c - based on ImprovedNoise.java
890 * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
892 static double fade(double t)
894 return t * t * t * (t * (t * 6 - 15) + 10);
897 static double grad(int hash, double x, double y, double z)
899 int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
900 double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
901 v = h < 4 ? y : h == 12 || h == 14 ? x : z;
902 return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
905 static const int permutation[] = { 151, 160, 137, 91, 90, 15,
906 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
907 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26,
908 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
909 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
910 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60,
911 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143,
912 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
913 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198,
914 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118,
915 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
916 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
917 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108,
918 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251,
919 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145,
920 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
921 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
922 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
926 static void initNoise(glhcfg *glhanoi)
929 for(i = 0; i < 256; i++)
930 glhanoi->p[256 + i] = glhanoi->p[i] = permutation[i];
933 static double improved_noise(glhcfg *glhanoi, double x, double y, double z)
936 int A, AA, AB, B, BA, BB;
937 int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
938 Y = (int)floor(y) & 255, /* CONTAINS POINT. */
939 Z = (int)floor(z) & 255;
940 if(!glhanoi->noise_initted) {
942 glhanoi->noise_initted = 1;
944 x -= floor(x); /* FIND RELATIVE X,Y,Z */
945 y -= floor(y); /* OF POINT IN CUBE. */
947 u = fade(x), /* COMPUTE FADE CURVES */
948 v = fade(y), /* FOR EACH OF X,Y,Z. */
950 A = glhanoi->p[X] + Y, AA = glhanoi->p[A] + Z, AB = glhanoi->p[A + 1] + Z, /* HASH COORDINATES OF */
951 B = glhanoi->p[X + 1] + Y, BA = glhanoi->p[B] + Z, BB = glhanoi->p[B + 1] + Z; /* THE 8 CUBE CORNERS, */
952 return lerp(w, lerp(v, lerp(u, grad(glhanoi->p[AA], x, y, z), /* AND ADD */
953 grad(glhanoi->p[BA], x - 1, y, z)), /* BLENDED */
954 lerp(u, grad(glhanoi->p[AB], x, y - 1, z), /* RESULTS */
955 grad(glhanoi->p[BB], x - 1, y - 1, z))), /* FROM 8 CORNERS */
956 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 */
957 lerp(u, grad(glhanoi->p[AB + 1], x, y - 1, z - 1),
958 grad(glhanoi->p[BB + 1], x - 1, y - 1, z - 1))));
962 * end noise_improved.c - based on ImprovedNoise.java
967 /* GLfloat *points; */
970 typedef struct tex_col_t tex_col_t;
972 static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
973 GLuint(*texFunc) (glhcfg *, double, double, double,
974 tex_col_t *), tex_col_t * colours)
977 GLubyte *textureData;
983 calloc(x_size * y_size * z_size, sizeof(GLuint))) == NULL) {
992 texturePtr = (void *)textureData;
993 for(k = 0; k < z_size; k++, z += zi) {
995 for(j = 0; j < y_size; j++, y += yi) {
997 for(i = 0; i < x_size; i++, x += xi) {
998 *texturePtr = texFunc(glhanoi, x, y, z, colours);
1006 static tex_col_t makeMarbleColours(void)
1008 tex_col_t marbleColours;
1011 marbleColours.colours = calloc(sizeof(GLuint), ncols);
1012 marbleColours.ncols = ncols;
1014 marbleColours.colours[0] = 0x3f3f3f3f;
1015 marbleColours.colours[1] = 0xffffffff;
1017 return marbleColours;
1020 static double turb(glhcfg *glhanoi, double x, double y, double z, int octaves)
1025 for(oct = 0; oct < octaves; ++oct) {
1026 r += fabs(improved_noise(glhanoi, freq * x, freq * y, freq * z)) / freq;
1032 static void perturb(glhcfg *glhanoi, double *x, double *y, double *z, double scale)
1034 double t = scale * turb(glhanoi, *x, *y, *z, 4);
1040 static double f_m(double x, double y, double z)
1042 return sin(3.0 * M_PI * x);
1045 static GLuint C_m(double x, const tex_col_t * tex_cols)
1047 int r = tex_cols->colours[0] & 0xff;
1048 int g = tex_cols->colours[0] >> 8 & 0xff;
1049 int b = tex_cols->colours[0] >> 16 & 0xff;
1054 factor = (1.0 + sin(2.0 * M_PI * x)) / 2.0;
1056 r1 = (tex_cols->colours[1] & 0xff);
1057 g1 = (tex_cols->colours[1] >> 8 & 0xff);
1058 b1 = (tex_cols->colours[1] >> 16 & 0xff);
1060 r += (int)(factor * (r1 - r));
1061 g += (int)(factor * (g1 - g));
1062 b += (int)(factor * (b1 - b));
1064 return 0xff000000 | (b << 16) | (g << 8) | r;
1068 static GLuint makeMarbleTexture(glhcfg *glhanoi, double x, double y, double z, tex_col_t * colours)
1070 perturb(glhanoi, &x, &y, &z, MARBLE_SCALE);
1071 return C_m(f_m(x, y, z), colours);
1074 static void setTexture(glhcfg *glhanoi, int n)
1076 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[n]);
1079 static int makeTextures(glhcfg *glhanoi)
1081 GLubyte *marbleTexture;
1082 tex_col_t marbleColours;
1084 glGenTextures(N_TEXTURES, glhanoi->textureNames);
1086 marbleColours = makeMarbleColours();
1088 makeTexture(glhanoi, MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 1,
1089 makeMarbleTexture, &marbleColours)) == NULL) {
1093 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[0]);
1094 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1095 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1096 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1097 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1098 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1099 MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 0,
1100 GL_RGBA, GL_UNSIGNED_BYTE, marbleTexture);
1101 free(marbleTexture);
1106 static void initFloor(glhcfg *glhanoi)
1109 float tileSize = glhanoi->boardSize / BOARD_SQUARES;
1110 float x0, x1, z0, z1;
1111 float tx0, tx1, tz0, tz1;
1112 const float *col = cWhite;
1113 float texIncr = 1.0 / BOARD_SQUARES;
1115 glhanoi->floorpolys = 0;
1116 if((glhanoi->floorList = glGenLists(1)) == 0) {
1117 fprintf(stderr, "can't allocate memory for floor display list\n");
1120 glNewList(glhanoi->floorList, GL_COMPILE);
1121 x0 = -glhanoi->boardSize / 2.0;
1123 setMaterial(col, cWhite, 128);
1124 setTexture(glhanoi, 0);
1126 for(i = 0; i < BOARD_SQUARES; i++, x0 += tileSize, tx0 += texIncr) {
1128 tx1 = tx0 + texIncr;
1129 z0 = -glhanoi->boardSize / 2.0;
1131 for(j = 0; j < BOARD_SQUARES; j++, z0 += tileSize, tz0 += texIncr) {
1132 int colIndex = (i + j) & 0x1;
1135 tz1 = tz0 + texIncr;
1142 setMaterial(col, cWhite, 100);
1146 glTexCoord2d(tx0, tz0);
1147 glVertex3f(x0, 0.0, z0);
1149 glTexCoord2d(tx0, tz1);
1150 glVertex3f(x0, 0.0, z1);
1152 glTexCoord2d(tx1, tz1);
1153 glVertex3f(x1, 0.0, z1);
1155 glTexCoord2d(tx1, tz0);
1156 glVertex3f(x1, 0.0, z0);
1157 glhanoi->floorpolys++;
1164 static void initTowers(glhcfg *glhanoi)
1166 if((glhanoi->baseList = glGenLists(1)) == 0) {
1167 fprintf(stderr, "can't allocate memory for towers display list\n");
1170 glNewList(glhanoi->baseList, GL_COMPILE);
1171 setMaterial(baseColor, cWhite, 50);
1172 glhanoi->basepolys = drawCuboid(glhanoi->baseLength, glhanoi->baseWidth,
1173 glhanoi->baseHeight);
1177 if((glhanoi->poleList = glGenLists(1)) == 0) {
1178 fprintf(stderr, "can't allocate memory for towers display list\n");
1181 glNewList(glhanoi->poleList, GL_COMPILE);
1183 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
1184 setMaterial(poleColor, cWhite, 50);
1185 glhanoi->polepolys = drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1187 glTranslatef(-glhanoi->poleOffset, 0.0, 0.0);
1188 glhanoi->polepolys += drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1190 glTranslatef(glhanoi->poleOffset, 0.0, 0.0);
1191 glhanoi->polepolys += drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1196 static double cfunc(double x)
1200 return (1.0 / 12.0) / (1.0 / 7.0) * x;
1203 return (1.0 + 1.0 / 6.0) * x - 1.0 / 6.0;
1206 return (2.0 + 1.0 / 3.0) * x - 2.0 / 3.0;
1209 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1211 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1214 static void initDisks(glhcfg *glhanoi)
1218 (Disk *) calloc(glhanoi->numberOfDisks, sizeof(Disk))) == NULL) {
1219 perror("initDisks");
1223 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
1224 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1225 double f = cfunc((GLfloat) i / (GLfloat) glhanoi->numberOfDisks);
1226 GLfloat diskColor = f * 360.0;
1228 Disk *disk = &glhanoi->disk[i];
1231 disk->position[0] = -glhanoi->poleOffset;
1232 disk->position[1] = glhanoi->diskHeight * height;
1233 disk->position[2] = 0.0;
1234 disk->rotation[0] = 0.0;
1235 disk->rotation[1] = 0.0;
1236 disk->rotation[2] = 0.0;
1239 color[0] = diskColor;
1242 HSVtoRGBv(color, color);
1244 if((disk->displayList = glGenLists(1)) == 0) {
1246 "can't allocate memory for disk %d display list\n", i);
1249 glNewList(disk->displayList, GL_COMPILE);
1250 setMaterial(color, cWhite, 100.0);
1251 disk->polys += drawDisk3D(glhanoi->poleRadius,
1252 getDiskRadius(glhanoi, i),
1253 glhanoi->diskHeight);
1256 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
1257 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1258 int h = glhanoi->maxDiskIdx - i;
1259 glhanoi->diskPos[h] = glhanoi->diskHeight * height;
1260 push(glhanoi, glhanoi->src, &glhanoi->disk[i]);
1264 static void initLights(Bool state)
1267 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1268 glLightfv(GL_LIGHT0, GL_AMBIENT, amb0);
1269 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
1270 glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1272 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1273 glLightfv(GL_LIGHT1, GL_AMBIENT, amb1);
1274 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
1275 glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1277 glEnable(GL_LIGHTING);
1278 glEnable(GL_LIGHT0);
1279 glEnable(GL_LIGHT1);
1281 glDisable(GL_LIGHTING);
1285 static int drawFloor(glhcfg *glhanoi)
1287 glCallList(glhanoi->floorList);
1288 return glhanoi->floorpolys;
1291 static int drawTowers(glhcfg *glhanoi)
1293 glCallList(glhanoi->baseList);
1294 glCallList(glhanoi->poleList);
1295 return glhanoi->basepolys + glhanoi->polepolys;
1298 /* Window management, etc
1300 ENTRYPOINT void reshape_glhanoi(ModeInfo * mi, int width, int height)
1302 glViewport(0, 0, (GLint) width, (GLint) height);
1304 glMatrixMode(GL_PROJECTION);
1306 gluPerspective(30.0, (GLdouble) width / (GLdouble) height, 1.0,
1307 2 * MAX_CAMERA_RADIUS);
1309 glMatrixMode(GL_MODELVIEW);
1312 glClear(GL_COLOR_BUFFER_BIT);
1315 ENTRYPOINT void init_glhanoi(ModeInfo * mi)
1320 (glhcfg *) calloc(MI_NUM_SCREENS(mi), sizeof(glhcfg));
1322 fprintf(stderr, "%s: out of memory creating configs\n",
1328 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1329 glhanoi->glx_context = init_GL(mi);
1330 glhanoi->numberOfDisks = MI_BATCHCOUNT(mi);
1332 if (glhanoi->numberOfDisks <= 1)
1333 glhanoi->numberOfDisks = 3 + (int) BELLRAND(9);
1335 /* magicnumber is a bitfield, so we can't have more than 31 discs
1336 on a system with 4-byte ints. */
1337 if (glhanoi->numberOfDisks >= 8 * sizeof(int))
1338 glhanoi->numberOfDisks = (8 * sizeof(int)) - 1;
1340 glhanoi->maxDiskIdx = glhanoi->numberOfDisks - 1;
1341 glhanoi->wire = MI_IS_WIREFRAME(mi);
1342 glhanoi->light = light;
1344 glhanoi->texture = texture;
1346 reshape_glhanoi(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1349 glhanoi->light = False;
1350 glhanoi->fog = False;
1351 glhanoi->texture = False;
1354 initLights(!glhanoi->wire && glhanoi->light);
1355 if(makeTextures(glhanoi) != 0) {
1356 fprintf(stderr, "can't allocate memory for marble texture\n");
1363 initTowers(glhanoi);
1366 glEnable(GL_DEPTH_TEST);
1367 glEnable(GL_NORMALIZE);
1368 glEnable(GL_CULL_FACE);
1369 glShadeModel(GL_SMOOTH);
1371 glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], 1.0);
1372 glFogi(GL_FOG_MODE, GL_LINEAR);
1373 glFogfv(GL_FOG_COLOR, fogcolor);
1374 glFogf(GL_FOG_DENSITY, 0.35f);
1375 glHint(GL_FOG_HINT, GL_NICEST);
1376 glFogf(GL_FOG_START, MIN_CAMERA_RADIUS);
1377 glFogf(GL_FOG_END, MAX_CAMERA_RADIUS / 1.9);
1382 glhanoi->duration = START_DURATION;
1383 changeState(glhanoi, START);
1386 ENTRYPOINT void draw_glhanoi(ModeInfo * mi)
1388 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1389 Display *dpy = MI_DISPLAY(mi);
1390 Window window = MI_WINDOW(mi);
1392 if(!glhanoi->glx_context)
1395 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(glhanoi->glx_context));
1397 glPolygonMode(GL_FRONT, glhanoi->wire ? GL_LINE : GL_FILL);
1399 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1400 mi->polygon_count = 0;
1404 update_glhanoi(glhanoi);
1405 updateView(glhanoi);
1407 if(!glhanoi->wire && glhanoi->texture) {
1408 glEnable(GL_TEXTURE_2D);
1410 mi->polygon_count += drawFloor(glhanoi);
1411 glDisable(GL_TEXTURE_2D);
1413 mi->polygon_count += drawTowers(glhanoi);
1414 mi->polygon_count += drawDisks(glhanoi);
1421 glXSwapBuffers(dpy, window);
1424 ENTRYPOINT Bool glhanoi_handle_event(ModeInfo * mi, XEvent * event)
1426 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1428 if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
1429 glhanoi->button_down_p = True;
1430 glhanoi->drag_x = event->xbutton.x;
1431 glhanoi->drag_y = event->xbutton.y;
1433 } else if(event->xany.type == ButtonRelease
1434 && event->xbutton.button == Button1) {
1435 glhanoi->button_down_p = False;
1437 } else if(event->xany.type == ButtonPress &&
1438 (event->xbutton.button == Button4
1439 || event->xbutton.button == Button5)) {
1440 switch (event->xbutton.button) {
1442 glhanoi->camera[2] += 0.01;
1445 glhanoi->camera[2] -= 0.01;
1449 "glhanoi: unknown button in mousewheel handler\n");
1452 } else if(event->xany.type == MotionNotify
1453 && glhanoi_cfg->button_down_p) {
1456 x_diff = event->xbutton.x - glhanoi->drag_x;
1457 y_diff = event->xbutton.y - glhanoi->drag_y;
1459 glhanoi->camera[0] = (float)x_diff / (float)MI_WIDTH(mi);
1460 glhanoi->camera[1] = (float)y_diff / (float)MI_HEIGHT(mi);
1467 ENTRYPOINT void release_glhanoi(ModeInfo * mi)
1469 if(glhanoi_cfg != NULL) {
1471 for(screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1474 glhcfg *glh = &glhanoi_cfg[screen];
1475 glDeleteLists(glh->floorList, 1);
1476 glDeleteLists(glh->baseList, 1);
1477 glDeleteLists(glh->poleList, 1);
1478 glDeleteLists(glh->textureNames[0], 2);
1479 for(j = 0; j < glh->numberOfDisks; ++j) {
1480 glDeleteLists(glh->disk[j].displayList, 1);
1483 for(i = 0; i < 3; i++) {
1484 if(glh->pole[i].data != NULL) {
1485 free(glh->pole[i].data);
1494 XSCREENSAVER_MODULE ("GLHanoi", glhanoi)