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 */
144 rotator *the_rotator;
147 GLuint textureNames[N_TEXTURES];
156 static glhcfg *glhanoi_cfg = NULL;
161 static XrmOptionDescRec opts[] = {
162 {"-light", ".glhanoi.light", XrmoptionNoArg, "true"},
163 {"+light", ".glhanoi.light", XrmoptionNoArg, "false"},
164 {"-fog", ".glhanoi.fog", XrmoptionNoArg, "true"},
165 {"+fog", ".glhanoi.fog", XrmoptionNoArg, "false"},
166 {"-texture", ".glhanoi.texture", XrmoptionNoArg, "true"},
167 {"+texture", ".glhanoi.texture", XrmoptionNoArg, "false"}
170 static argtype vars[] = {
171 {&light, "light", "Light", DEF_LIGHT, t_Bool},
172 {&fog, "fog", "Fog", DEF_FOG, t_Bool},
173 {&texture, "texture", "Texture", DEF_TEXTURE, t_Bool}
176 static OptionStruct desc[] = {
177 {"+/-light", "whether to light the scene"},
178 {"+/-fog", "whether to apply fog to the scene"},
179 {"+/-texture", "whether to apply texture to the scene"}
182 ENTRYPOINT ModeSpecOpt glhanoi_opts = { countof(opts), opts, countof(vars), vars, desc };
186 ModStruct glhanoi_description = {
187 "glhanoi", "init_glhanoi", "draw_glhanoi", "release_glhanoi",
188 "draw_glhanoi", "init_glhanoi", NULL, &glhanoi_opts,
189 1000, 1, 2, 1, 4, 1.0, "",
190 "Towers of Hanoi", 0, NULL
195 static const GLfloat cBlack[] = { 0.0, 0.0, 0.0, 1.0 };
196 static const GLfloat cWhite[] = { 1.0, 1.0, 1.0, 1.0 };
197 static const GLfloat poleColor[] = { 0.545, 0.137, 0.137 };
198 static const GLfloat baseColor[] = { 0.34, 0.34, 0.48 };
199 static const GLfloat fogcolor[] = { 0.5, 0.5, 0.5 };
201 static const float left[] = { 1.0, 0.0, 0.0 };
202 static const float up[] = { 0.0, 1.0, 0.0 };
203 static const float front[] = { 0.0, 0.0, 1.0 };
204 static const float right[] = { -1.0, 0.0, 0.0 };
205 static const float down[] = { 0.0, -1.0, 0.0 };
206 static const float back[] = { 0.0, 0.0, -1.0 };
208 static const GLfloat pos0[4] = { 50.0, 50.0, 50.0, 0.0 };
209 static const GLfloat amb0[4] = { 0.0, 0.0, 0.0, 1.0 };
210 static const GLfloat dif0[4] = { 1.0, 1.0, 1.0, 1.0 };
211 static const GLfloat spc0[4] = { 0.0, 1.0, 1.0, 1.0 };
213 static const GLfloat pos1[4] = { -50.0, 50.0, -50.0, 0.0 };
214 static const GLfloat amb1[4] = { 0.0, 0.0, 0.0, 1.0 };
215 static const GLfloat dif1[4] = { 1.0, 1.0, 1.0, 1.0 };
216 static const GLfloat spc1[4] = { 1.0, 1.0, 1.0, 1.0 };
218 static float g = 3.0 * 9.80665; /* hmm, looks like we need more gravity, Scotty... */
220 #define DOPUSH(X, Y) (((X)->count) >= ((X)->size)) ? NULL : ((X)->data[(X)->count++] = (Y))
221 #define DOPOP(X) (X)->count <= 0 ? NULL : ((X)->data[--((X)->count)])
223 static Disk *push(glhcfg *glhanoi, int idx, Disk * d)
225 return DOPUSH(&glhanoi->pole[idx], d);
228 static Disk *pop(glhcfg *glhanoi, int idx)
230 return DOPOP(&glhanoi->pole[idx]);
233 static inline void swap(int *x, int *y)
241 * magic - it's magic...
243 static int magic(int i)
248 while((i & 0x01) == 0) {
252 return count % 2 == 0;
256 static float distance(float *p0, float *p1)
262 return (float)sqrt(x * x + y * y + z * z);
266 static GLfloat A(GLfloat a, GLfloat b, GLfloat c)
269 return c / (a * b - 0.25 * sum * sum);
272 static void moveSetup(glhcfg *glhanoi, Disk * disk)
276 int src = glhanoi->src;
277 int dst = glhanoi->dst;
279 GLfloat sintheta, costheta;
281 if(glhanoi->state != FINISHED && random() % 6 == 0) {
283 -180.0 * (2 - 2 * random() % 2) * (random() % 3 + 1);
285 disk->rotAngle = -180.0;
288 disk->base0 = glhanoi->diskPos[glhanoi->pole[src].count];
291 FINISHED ? disk->base0 : glhanoi->diskPos[glhanoi->pole[dst].
294 disk->xmin = glhanoi->poleOffset * (src - 1);
295 disk->xmax = glhanoi->poleOffset * (dst - 1);
296 disk->ymin = glhanoi->poleHeight;
298 glhanoi->poleHeight + fabs(disk->xmax -
299 disk->xmin) * (glhanoi->state ==
305 numberOfDisks : 1.0);
307 h = ymax - disk->ymin;
308 theta = atan((disk->xmin - disk->xmax) * A(disk->xmin, disk->xmax, h));
311 costheta = cos(theta);
312 sintheta = sin(theta);
316 (2.0 * A(disk->xmin, disk->xmax, h) * costheta * costheta)));
317 disk->usintheta = u * sintheta;
318 disk->ucostheta = u * costheta;
320 (-u + sqrt(u * u + 2.0 * g * fabs(disk->ymin - disk->base0))) / g;
321 disk->u1 = u + g * disk->t1;
322 disk->t2 = 2.0 * disk->usintheta / g;
323 disk->u2 = disk->usintheta - g * disk->t2;
326 static void makeMove(glhcfg *glhanoi)
328 int fudge = glhanoi->move + 2;
329 int magicNumber = magic(fudge);
331 glhanoi->currentDisk = pop(glhanoi, glhanoi->src);
332 moveSetup(glhanoi, glhanoi->currentDisk);
333 push(glhanoi, glhanoi->dst, glhanoi->currentDisk);
336 if(fudge == 1 || magicNumber) {
337 swap(&glhanoi->src, &glhanoi->tmp);
339 if(fudge == 0 || glhanoi->magicNumber) {
340 swap(&glhanoi->dst, &glhanoi->tmp);
342 glhanoi->magicNumber = magicNumber;
345 static double lerp(double alpha, double start, double end)
347 return start + alpha * (end - start);
350 static void upfunc(GLdouble t, Disk * d)
352 d->position[0] = d->xmin;
353 d->position[1] = d->base0 + (d->u1 - 0.5 * g * t) * t;
355 d->rotation[1] = 0.0;
358 static void parafunc(GLdouble t, Disk * d)
360 d->position[0] = d->xmin + d->ucostheta * t;
361 d->position[1] = d->ymin + (d->usintheta - 0.5 * g * t) * t;
364 d->rotAngle * (d->position[0] - d->xmin) / (d->xmax - d->xmin);
367 static void downfunc(GLdouble t, Disk * d)
369 d->position[0] = d->xmax;
370 d->position[1] = d->ymin + (d->u2 - 0.5 * g * t) * t;
372 d->rotation[1] = 0.0;
375 static Bool computePosition(GLfloat t, Disk * d)
377 Bool finished = False;
381 } else if(t < d->t1 + d->t2) {
382 parafunc(t - d->t1, d);
384 downfunc(t - d->t1 - d->t2, d);
385 if(d->position[1] <= d->base1) {
386 d->position[1] = d->base1;
393 static void updateView(glhcfg *glhanoi)
395 double longitude, latitude, radius;
396 double a, b, c, A, B;
398 get_position(glhanoi->the_rotator, NULL, NULL, &radius,
399 !glhanoi->button_down_p);
400 get_rotation(glhanoi->the_rotator, &longitude, &latitude, NULL,
401 !glhanoi->button_down_p);
402 longitude += glhanoi->camera[0];
403 latitude += glhanoi->camera[1];
404 radius += glhanoi->camera[2];
405 longitude = longitude - floor(longitude);
406 latitude = latitude - floor(latitude);
407 radius = radius - floor(radius);
409 latitude = 1.0 - latitude;
412 radius = 1.0 - radius;
415 b = glhanoi->centre[1];
416 c = (MIN_CAMERA_RADIUS +
417 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS));
418 A = M_PI / 4.0 * (1.0 - latitude);
419 a = sqrt(b * b + c * c - 2.0 * b * c * cos(A));
420 B = asin(sin(A) * b / a);
421 glRotatef(-B * 180 / M_PI, 1.0, 0.0, 0.0);
423 glTranslatef(0.0f, 0.0f,
424 -(MIN_CAMERA_RADIUS +
425 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS)));
426 glRotatef(longitude * 360.0, 0.0f, 1.0f, 0.0f);
427 glRotatef(latitude * 180.0, cos(longitude * 2.0 * M_PI), 0.0,
428 sin(longitude * 2.0 * M_PI));
431 static void changeState(glhcfg *glhanoi, State state)
433 glhanoi->state = state;
434 glhanoi->startTime = getTime();
437 static void update_glhanoi(glhcfg *glhanoi)
439 double t = getTime() - glhanoi->startTime;
443 switch (glhanoi->state) {
445 if(t < glhanoi->duration) {
449 if(glhanoi->numberOfDisks % 2 == 0) {
450 swap(&glhanoi->tmp, &glhanoi->dst);
452 glhanoi->magicNumber = 1;
454 changeState(glhanoi, MOVE_DISK);
458 if(computePosition(t, glhanoi->currentDisk)) {
459 changeState(glhanoi, MOVE_FINISHED);
464 if(++glhanoi->move < glhanoi->numberOfMoves) {
466 changeState(glhanoi, MOVE_DISK);
468 glhanoi->duration = FINISH_DURATION;
469 changeState(glhanoi, FINISHED);
474 while(t < glhanoi->duration) {
477 glhanoi->src = glhanoi->olddst;
478 glhanoi->dst = glhanoi->oldsrc;
479 for(i = 0; i < glhanoi->numberOfDisks; ++i) {
480 Disk *disk = pop(glhanoi, glhanoi->src);
481 assert(disk != NULL);
482 moveSetup(glhanoi, disk);
484 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
485 push(glhanoi, glhanoi->dst, &glhanoi->disk[i]);
487 changeState(glhanoi, MONEY_SHOT);
492 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
493 double delay = 0.25 * i;
501 finished = computePosition(t - delay, &glhanoi->disk[i]);
502 glhanoi->disk[i].rotation[1] = 0.0;
509 glhanoi->src = glhanoi->oldsrc;
510 glhanoi->tmp = glhanoi->oldtmp;
511 glhanoi->dst = glhanoi->olddst;
512 changeState(glhanoi, START);
518 fprintf(stderr, "Invalid state\n");
523 static void HSVtoRGBf(GLfloat h, GLfloat s, GLfloat v,
524 GLfloat * r, GLfloat * g, GLfloat * b)
531 GLfloat i, f, p, q, t;
535 h /= 60.0; /* h now in [0,6). */
536 i = floor((double)h); /* i now largest integer <= h */
537 f = h - i; /* f is no fractional part of h */
539 q = v * (1.0 - (s * f));
540 t = v * (1.0 - (s * (1.0 - f)));
576 static void HSVtoRGBv(GLfloat * hsv, GLfloat * rgb)
578 HSVtoRGBf(hsv[0], hsv[1], hsv[2], &rgb[0], &rgb[1], &rgb[2]);
581 static void setMaterial(const GLfloat color[3], const GLfloat hlite[3], int shininess)
584 glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
585 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
586 glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
590 * drawTube: I know all this stuff is available in gluQuadrics
591 * but I'd originally intended to texture the poles with a 3D wood
592 * texture, but I was having difficulty getting wood... what? Why
593 * are all you Amercians laughing..? Anyway, I don't know if enough
594 * people's hardware supports 3D textures, so I didn't bother (xorg
595 * ATI server doesn't :-( )
597 static void drawTube(GLdouble bottomRadius, GLdouble topRadius,
598 GLdouble bottomThickness, GLdouble topThickness,
599 GLdouble height, GLuint nSlice, GLuint nLoop)
602 GLfloat *cosCache = malloc(sizeof(GLfloat) * nSlice);
603 GLfloat *sinCache = malloc(sizeof(GLfloat) * nSlice);
606 GLint lastSlice = nSlice - 1;
611 if(bottomThickness > bottomRadius) {
612 bottomThickness = bottomRadius;
614 if(topThickness > topRadius) {
615 topThickness = topRadius;
617 if(bottomThickness < 0.0) {
618 bottomThickness = 0.0;
620 if(topThickness < 0.0) {
623 if(topRadius >= bottomRadius) {
624 maxRadius = topRadius;
626 maxRadius = bottomRadius;
631 radius = bottomRadius;
632 innerRadius = bottomRadius - bottomThickness;
633 /* innerTexCoordSize = texCoordSize * innerRadius / maxRadius; */
634 /* outerTexCoordSize = texCoordSize * radius / maxRadius; */
635 /* yTexCoord = minTexCoord; */
637 glBegin(GL_QUAD_STRIP);
639 glNormal3f(0.0, -1.0, 0.0);
641 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + innerTexCoordSize); */
642 glVertex3f(0.0, y, innerRadius);
644 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + outerTexCoordSize); */
645 glVertex3f(0.0, y, radius);
647 for(slice = lastSlice; slice >= 0; --slice) {
648 GLfloat theta = 2.0 * M_PI * (double)slice / nSlice;
650 cosCache[slice] = cos(theta);
651 sinCache[slice] = sin(theta);
653 /* glTexCoord3f(midTexCoord + sinCache[slice] * innerTexCoordSize, */
655 /* midTexCoord + cosCache[slice] * innerTexCoordSize); */
656 glVertex3f(innerRadius * sinCache[slice], y,
657 innerRadius * cosCache[slice]);
658 /* glTexCoord3f(midTexCoord + sinCache[slice] * outerTexCoordSize, */
660 /* midTexCoord + cosCache[slice] * outerTexCoordSize); */
661 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]);
688 glNormal3f(0.0, 0.0, 1.0);
689 glVertex3f(0.0, upperY, upperRadius);
690 glVertex3f(0.0, lowerY, lowerRadius);
694 lowerRadius = bottomRadius - bottomThickness +
695 factor * (float)loop / (nLoop);
696 upperRadius = bottomRadius - bottomThickness +
697 factor * (float)(loop + 1) / (nLoop);
699 glBegin(GL_QUAD_STRIP);
700 glNormal3f(0.0, 0.0, -1.0);
701 glVertex3f(0.0, upperY, upperRadius);
702 glVertex3f(0.0, lowerY, lowerRadius);
703 for(slice = lastSlice; slice >= 0; --slice) {
704 glNormal3f(-sinCache[slice], 0.0, -cosCache[slice]);
705 glVertex3f(upperRadius * sinCache[slice], upperY,
706 upperRadius * cosCache[slice]);
707 glVertex3f(lowerRadius * sinCache[slice], lowerY,
708 lowerRadius * cosCache[slice]);
716 innerRadius = topRadius - topThickness;
718 glBegin(GL_QUAD_STRIP);
719 glNormal3f(0.0, 1.0, 0.0);
720 for(slice = 0; slice < nSlice; ++slice) {
721 glVertex3f(innerRadius * sinCache[slice], y,
722 innerRadius * cosCache[slice]);
724 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
726 glVertex3f(0.0, y, innerRadius);
727 glVertex3f(0.0, y, radius);
731 static void drawPole(GLfloat radius, GLfloat length)
733 drawTube(radius, radius, radius, radius, length, NSLICE, NLOOPS);
736 static void drawDisk3D(GLdouble inner_radius, GLdouble outer_radius,
739 drawTube(outer_radius, outer_radius, outer_radius - inner_radius,
740 outer_radius - inner_radius, height, NSLICE, NLOOPS);
743 static void drawCuboid(GLfloat length, GLfloat width, GLfloat height)
745 GLfloat xmin = -length / 2.0f;
746 GLfloat xmax = length / 2.0f;
747 GLfloat zmin = -width / 2.0f;
748 GLfloat zmax = width / 2.0f;
750 GLfloat ymax = height;
755 glVertex3f(xmin, ymin, zmax); /* 0 */
756 glVertex3f(xmax, ymin, zmax); /* 1 */
757 glVertex3f(xmax, ymax, zmax); /* 2 */
758 glVertex3f(xmin, ymax, zmax); /* 3 */
761 glVertex3f(xmax, ymin, zmax); /* 1 */
762 glVertex3f(xmax, ymin, zmin); /* 5 */
763 glVertex3f(xmax, ymax, zmin); /* 6 */
764 glVertex3f(xmax, ymax, zmax); /* 2 */
767 glVertex3f(xmax, ymin, zmin); /* 5 */
768 glVertex3f(xmin, ymin, zmin); /* 4 */
769 glVertex3f(xmin, ymax, zmin); /* 7 */
770 glVertex3f(xmax, ymax, zmin); /* 6 */
773 glVertex3f(xmin, ymin, zmin); /* 4 */
774 glVertex3f(xmin, ymin, zmax); /* 0 */
775 glVertex3f(xmin, ymax, zmax); /* 3 */
776 glVertex3f(xmin, ymax, zmin); /* 7 */
779 glVertex3f(xmin, ymax, zmax); /* 3 */
780 glVertex3f(xmax, ymax, zmax); /* 2 */
781 glVertex3f(xmax, ymax, zmin); /* 6 */
782 glVertex3f(xmin, ymax, zmin); /* 7 */
785 glVertex3f(xmin, ymin, zmin); /* 4 */
786 glVertex3f(xmax, ymin, zmin); /* 5 */
787 glVertex3f(xmax, ymin, zmax); /* 1 */
788 glVertex3f(xmin, ymin, zmax); /* 0 */
792 static void drawDisks(glhcfg *glhanoi)
797 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
798 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
799 Disk *disk = &glhanoi->disk[i];
800 GLfloat *pos = disk->position;
801 GLfloat *rot = disk->rotation;
804 glTranslatef(pos[0], pos[1], pos[2]);
806 glTranslatef(0.0, glhanoi->diskHeight / 2.0, 0.0);
807 glRotatef(rot[1], 0.0, 0.0, 1.0);
808 glTranslatef(0.0, -glhanoi->diskHeight / 2.0, 0.0);
810 glCallList(disk->displayList);
816 static GLfloat getDiskRadius(glhcfg *glhanoi, int i)
818 return ((GLfloat) i + 3.0) * glhanoi->poleRadius;
821 static void initData(glhcfg *glhanoi)
823 GLfloat maxDiskRadius;
826 glhanoi->baseLength = BASE_LENGTH;
827 glhanoi->poleRadius = glhanoi->baseLength /
828 (2.0 * (3 * glhanoi->numberOfDisks + 7.0));
829 maxDiskRadius = getDiskRadius(glhanoi, glhanoi->numberOfDisks);
830 glhanoi->baseWidth = 2.0 * maxDiskRadius;
831 glhanoi->poleOffset = 2.0 * getDiskRadius(glhanoi, glhanoi->maxDiskIdx);
832 glhanoi->diskHeight = 2.0 * glhanoi->poleRadius;
833 glhanoi->baseHeight = 2.0 * glhanoi->poleRadius;
834 glhanoi->poleHeight = glhanoi->numberOfDisks *
835 glhanoi->diskHeight + glhanoi->poleRadius;
836 glhanoi->numberOfMoves = (1 << glhanoi->numberOfDisks) - 1;
837 glhanoi->boardSize = glhanoi->baseLength * 0.5 * (1.0 + sqrt(5.0));
839 for(i = 0; i < 3; i++) {
840 if((glhanoi->pole[i].data =
841 calloc(glhanoi->numberOfDisks, sizeof(Disk *))) == NULL) {
842 fprintf(stderr, "%s: out of memory creating stack %d\n",
846 glhanoi->pole[i].size = glhanoi->numberOfDisks;
848 if((glhanoi->diskPos =
849 calloc(glhanoi->numberOfDisks, sizeof(double))) == NULL) {
850 fprintf(stderr, "%s: out of memory creating diskPos\n", progname);
854 glhanoi->the_rotator = make_rotator(0.1, 0.025, 0, 1, 0.005, False);
855 glhanoi->button_down_p = False;
857 glhanoi->src = glhanoi->oldsrc = 0;
858 glhanoi->tmp = glhanoi->oldtmp = 1;
859 glhanoi->dst = glhanoi->olddst = 2;
862 static void initView(glhcfg *glhanoi)
864 glhanoi->camera[0] = 0.0;
865 glhanoi->camera[1] = 0.0;
866 glhanoi->camera[2] = 0.0;
867 glhanoi->centre[0] = 0.0;
868 glhanoi->centre[1] = glhanoi->poleHeight * 3.0;
869 glhanoi->centre[2] = 0.0;
873 * noise_improved.c - based on ImprovedNoise.java
874 * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
876 static double fade(double t)
878 return t * t * t * (t * (t * 6 - 15) + 10);
881 static double grad(int hash, double x, double y, double z)
883 int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
884 double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
885 v = h < 4 ? y : h == 12 || h == 14 ? x : z;
886 return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
889 static const int permutation[] = { 151, 160, 137, 91, 90, 15,
890 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
891 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26,
892 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
893 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
894 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60,
895 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143,
896 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
897 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198,
898 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118,
899 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
900 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
901 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108,
902 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251,
903 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145,
904 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
905 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
906 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
910 static void initNoise(glhcfg *glhanoi)
913 for(i = 0; i < 256; i++)
914 glhanoi->p[256 + i] = glhanoi->p[i] = permutation[i];
917 static double improved_noise(glhcfg *glhanoi, double x, double y, double z)
920 int A, AA, AB, B, BA, BB;
921 int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
922 Y = (int)floor(y) & 255, /* CONTAINS POINT. */
923 Z = (int)floor(z) & 255;
924 if(!glhanoi->noise_initted) {
926 glhanoi->noise_initted = 1;
928 x -= floor(x); /* FIND RELATIVE X,Y,Z */
929 y -= floor(y); /* OF POINT IN CUBE. */
931 u = fade(x), /* COMPUTE FADE CURVES */
932 v = fade(y), /* FOR EACH OF X,Y,Z. */
934 A = glhanoi->p[X] + Y, AA = glhanoi->p[A] + Z, AB = glhanoi->p[A + 1] + Z, /* HASH COORDINATES OF */
935 B = glhanoi->p[X + 1] + Y, BA = glhanoi->p[B] + Z, BB = glhanoi->p[B + 1] + Z; /* THE 8 CUBE CORNERS, */
936 return lerp(w, lerp(v, lerp(u, grad(glhanoi->p[AA], x, y, z), /* AND ADD */
937 grad(glhanoi->p[BA], x - 1, y, z)), /* BLENDED */
938 lerp(u, grad(glhanoi->p[AB], x, y - 1, z), /* RESULTS */
939 grad(glhanoi->p[BB], x - 1, y - 1, z))), /* FROM 8 CORNERS */
940 lerp(v, lerp(u, grad(glhanoi->p[AA + 1], x, y, z - 1), grad(glhanoi->p[BA + 1], x - 1, y, z - 1)), /* OF CUBE */
941 lerp(u, grad(glhanoi->p[AB + 1], x, y - 1, z - 1),
942 grad(glhanoi->p[BB + 1], x - 1, y - 1, z - 1))));
946 * end noise_improved.c - based on ImprovedNoise.java
951 /* GLfloat *points; */
954 typedef struct tex_col_t tex_col_t;
956 static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
957 GLuint(*texFunc) (glhcfg *, double, double, double,
958 tex_col_t *), tex_col_t * colours)
961 GLubyte *textureData;
967 calloc(x_size * y_size * z_size, sizeof(GLuint))) == NULL) {
976 texturePtr = (void *)textureData;
977 for(k = 0; k < z_size; k++, z += zi) {
979 for(j = 0; j < y_size; j++, y += yi) {
981 for(i = 0; i < x_size; i++, x += xi) {
982 *texturePtr = texFunc(glhanoi, x, y, z, colours);
990 static tex_col_t makeMarbleColours(void)
992 tex_col_t marbleColours;
995 marbleColours.colours = calloc(sizeof(GLuint), ncols);
996 marbleColours.ncols = ncols;
998 marbleColours.colours[0] = 0x3f3f3f3f;
999 marbleColours.colours[1] = 0xffffffff;
1001 return marbleColours;
1004 static double turb(glhcfg *glhanoi, double x, double y, double z, int octaves)
1009 for(oct = 0; oct < octaves; ++oct) {
1010 r += fabs(improved_noise(glhanoi, freq * x, freq * y, freq * z)) / freq;
1016 static void perturb(glhcfg *glhanoi, double *x, double *y, double *z, double scale)
1018 double t = scale * turb(glhanoi, *x, *y, *z, 4);
1024 static double f_m(double x, double y, double z)
1026 return sin(3.0 * M_PI * x);
1029 static GLuint C_m(double x, const tex_col_t * tex_cols)
1031 int r = tex_cols->colours[0] & 0xff;
1032 int g = tex_cols->colours[0] >> 8 & 0xff;
1033 int b = tex_cols->colours[0] >> 16 & 0xff;
1038 factor = (1.0 + sin(2.0 * M_PI * x)) / 2.0;
1040 r1 = (tex_cols->colours[1] & 0xff);
1041 g1 = (tex_cols->colours[1] >> 8 & 0xff);
1042 b1 = (tex_cols->colours[1] >> 16 & 0xff);
1044 r += (int)(factor * (r1 - r));
1045 g += (int)(factor * (g1 - g));
1046 b += (int)(factor * (b1 - b));
1048 return 0xff000000 | (b << 16) | (g << 8) | r;
1052 static GLuint makeMarbleTexture(glhcfg *glhanoi, double x, double y, double z, tex_col_t * colours)
1054 perturb(glhanoi, &x, &y, &z, MARBLE_SCALE);
1055 return C_m(f_m(x, y, z), colours);
1058 static void setTexture(glhcfg *glhanoi, int n)
1060 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[n]);
1063 static int makeTextures(glhcfg *glhanoi)
1065 GLubyte *marbleTexture;
1066 tex_col_t marbleColours;
1068 glGenTextures(N_TEXTURES, glhanoi->textureNames);
1070 marbleColours = makeMarbleColours();
1072 makeTexture(glhanoi, MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 1,
1073 makeMarbleTexture, &marbleColours)) == NULL) {
1077 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[0]);
1078 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1079 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1080 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1081 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1082 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1083 MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 0,
1084 GL_RGBA, GL_UNSIGNED_BYTE, marbleTexture);
1085 free(marbleTexture);
1090 static void initFloor(glhcfg *glhanoi)
1093 float tileSize = glhanoi->boardSize / BOARD_SQUARES;
1094 float x0, x1, z0, z1;
1095 float tx0, tx1, tz0, tz1;
1096 const float *col = cWhite;
1097 float texIncr = 1.0 / BOARD_SQUARES;
1099 if((glhanoi->floorList = glGenLists(1)) == 0) {
1100 fprintf(stderr, "can't allocate memory for floor display list\n");
1103 glNewList(glhanoi->floorList, GL_COMPILE);
1104 x0 = -glhanoi->boardSize / 2.0;
1106 setMaterial(col, cWhite, 128);
1107 setTexture(glhanoi, 0);
1109 for(i = 0; i < BOARD_SQUARES; i++, x0 += tileSize, tx0 += texIncr) {
1111 tx1 = tx0 + texIncr;
1112 z0 = -glhanoi->boardSize / 2.0;
1114 for(j = 0; j < BOARD_SQUARES; j++, z0 += tileSize, tz0 += texIncr) {
1115 int colIndex = (i + j) & 0x1;
1118 tz1 = tz0 + texIncr;
1125 setMaterial(col, cWhite, 100);
1129 glTexCoord2d(tx0, tz0);
1130 glVertex3f(x0, 0.0, z0);
1132 glTexCoord2d(tx0, tz1);
1133 glVertex3f(x0, 0.0, z1);
1135 glTexCoord2d(tx1, tz1);
1136 glVertex3f(x1, 0.0, z1);
1138 glTexCoord2d(tx1, tz0);
1139 glVertex3f(x1, 0.0, z0);
1146 static void initTowers(glhcfg *glhanoi)
1148 if((glhanoi->baseList = glGenLists(1)) == 0) {
1149 fprintf(stderr, "can't allocate memory for towers display list\n");
1152 glNewList(glhanoi->baseList, GL_COMPILE);
1153 setMaterial(baseColor, cWhite, 50);
1154 drawCuboid(glhanoi->baseLength, glhanoi->baseWidth,
1155 glhanoi->baseHeight);
1159 if((glhanoi->poleList = glGenLists(1)) == 0) {
1160 fprintf(stderr, "can't allocate memory for towers display list\n");
1163 glNewList(glhanoi->poleList, GL_COMPILE);
1165 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
1166 setMaterial(poleColor, cWhite, 50);
1167 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1169 glTranslatef(-glhanoi->poleOffset, 0.0, 0.0);
1170 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1172 glTranslatef(glhanoi->poleOffset, 0.0, 0.0);
1173 drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1178 static double cfunc(double x)
1182 return (1.0 / 12.0) / (1.0 / 7.0) * x;
1185 return (1.0 + 1.0 / 6.0) * x - 1.0 / 6.0;
1188 return (2.0 + 1.0 / 3.0) * x - 2.0 / 3.0;
1191 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1193 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1196 static void initDisks(glhcfg *glhanoi)
1200 (Disk *) calloc(glhanoi->numberOfDisks, sizeof(Disk))) == NULL) {
1201 perror("initDisks");
1205 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
1206 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1207 double f = cfunc((GLfloat) i / (GLfloat) glhanoi->numberOfDisks);
1208 GLfloat diskColor = f * 360.0;
1210 Disk *disk = &glhanoi->disk[i];
1213 disk->position[0] = -glhanoi->poleOffset;
1214 disk->position[1] = glhanoi->diskHeight * height;
1215 disk->position[2] = 0.0;
1216 disk->rotation[0] = 0.0;
1217 disk->rotation[1] = 0.0;
1218 disk->rotation[2] = 0.0;
1220 color[0] = diskColor;
1223 HSVtoRGBv(color, color);
1225 if((disk->displayList = glGenLists(1)) == 0) {
1227 "can't allocate memory for disk %d display list\n", i);
1230 glNewList(disk->displayList, GL_COMPILE);
1231 setMaterial(color, cWhite, 100.0);
1232 drawDisk3D(glhanoi->poleRadius,
1233 getDiskRadius(glhanoi, i),
1234 glhanoi->diskHeight);
1237 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
1238 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1239 int h = glhanoi->maxDiskIdx - i;
1240 glhanoi->diskPos[h] = glhanoi->diskHeight * height;
1241 push(glhanoi, glhanoi->src, &glhanoi->disk[i]);
1245 static void initLights(Bool state)
1248 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1249 glLightfv(GL_LIGHT0, GL_AMBIENT, amb0);
1250 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
1251 glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1253 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1254 glLightfv(GL_LIGHT1, GL_AMBIENT, amb1);
1255 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
1256 glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1258 glEnable(GL_LIGHTING);
1259 glEnable(GL_LIGHT0);
1260 glEnable(GL_LIGHT1);
1262 glDisable(GL_LIGHTING);
1266 static void drawFloor(glhcfg *glhanoi)
1268 glCallList(glhanoi->floorList);
1271 static void drawTowers(glhcfg *glhanoi)
1273 glCallList(glhanoi->baseList);
1274 glCallList(glhanoi->poleList);
1277 /* Window management, etc
1279 ENTRYPOINT void reshape_glhanoi(ModeInfo * mi, int width, int height)
1281 glViewport(0, 0, (GLint) width, (GLint) height);
1283 glMatrixMode(GL_PROJECTION);
1285 gluPerspective(30.0, (GLdouble) width / (GLdouble) height, 1.0,
1286 2 * MAX_CAMERA_RADIUS);
1288 glMatrixMode(GL_MODELVIEW);
1291 glClear(GL_COLOR_BUFFER_BIT);
1294 ENTRYPOINT void init_glhanoi(ModeInfo * mi)
1299 (glhcfg *) calloc(MI_NUM_SCREENS(mi), sizeof(glhcfg));
1301 fprintf(stderr, "%s: out of memory creating configs\n",
1307 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1308 glhanoi->glx_context = init_GL(mi);
1309 glhanoi->numberOfDisks = MI_BATCHCOUNT(mi);
1311 if (glhanoi->numberOfDisks <= 1)
1312 glhanoi->numberOfDisks = 3 + (int) BELLRAND(9);
1314 /* magicnumber is a bitfield, so we can't have more than 31 discs
1315 on a system with 4-byte ints. */
1316 if (glhanoi->numberOfDisks >= 8 * sizeof(int))
1317 glhanoi->numberOfDisks = (8 * sizeof(int)) - 1;
1319 glhanoi->maxDiskIdx = glhanoi->numberOfDisks - 1;
1320 glhanoi->wire = MI_IS_WIREFRAME(mi);
1321 glhanoi->light = light;
1323 glhanoi->texture = texture;
1325 reshape_glhanoi(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1328 glhanoi->light = False;
1329 glhanoi->fog = False;
1330 glhanoi->texture = False;
1333 initLights(!glhanoi->wire && glhanoi->light);
1334 if(makeTextures(glhanoi) != 0) {
1335 fprintf(stderr, "can't allocate memory for marble texture\n");
1342 initTowers(glhanoi);
1345 glEnable(GL_DEPTH_TEST);
1346 glEnable(GL_NORMALIZE);
1347 glEnable(GL_CULL_FACE);
1348 glShadeModel(GL_SMOOTH);
1350 glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], 1.0);
1351 glFogi(GL_FOG_MODE, GL_LINEAR);
1352 glFogfv(GL_FOG_COLOR, fogcolor);
1353 glFogf(GL_FOG_DENSITY, 0.35f);
1354 glHint(GL_FOG_HINT, GL_NICEST);
1355 glFogf(GL_FOG_START, MIN_CAMERA_RADIUS);
1356 glFogf(GL_FOG_END, MAX_CAMERA_RADIUS / 1.9);
1361 glhanoi->duration = START_DURATION;
1362 changeState(glhanoi, START);
1365 ENTRYPOINT void draw_glhanoi(ModeInfo * mi)
1367 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1368 Display *dpy = MI_DISPLAY(mi);
1369 Window window = MI_WINDOW(mi);
1371 if(!glhanoi->glx_context)
1374 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(glhanoi->glx_context));
1376 glPolygonMode(GL_FRONT, glhanoi->wire ? GL_LINE : GL_FILL);
1378 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1382 update_glhanoi(glhanoi);
1383 updateView(glhanoi);
1385 if(!glhanoi->wire && glhanoi->texture) {
1386 glEnable(GL_TEXTURE_2D);
1389 glDisable(GL_TEXTURE_2D);
1391 drawTowers(glhanoi);
1399 glXSwapBuffers(dpy, window);
1402 ENTRYPOINT Bool glhanoi_handle_event(ModeInfo * mi, XEvent * event)
1404 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1406 if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
1407 glhanoi->button_down_p = True;
1408 glhanoi->drag_x = event->xbutton.x;
1409 glhanoi->drag_y = event->xbutton.y;
1411 } else if(event->xany.type == ButtonRelease
1412 && event->xbutton.button == Button1) {
1413 glhanoi->button_down_p = False;
1415 } else if(event->xany.type == ButtonPress &&
1416 (event->xbutton.button == Button4
1417 || event->xbutton.button == Button5)) {
1418 switch (event->xbutton.button) {
1420 glhanoi->camera[2] += 0.01;
1423 glhanoi->camera[2] -= 0.01;
1427 "glhanoi: unknown button in mousewheel handler\n");
1430 } else if(event->xany.type == MotionNotify
1431 && glhanoi_cfg->button_down_p) {
1434 x_diff = event->xbutton.x - glhanoi->drag_x;
1435 y_diff = event->xbutton.y - glhanoi->drag_y;
1437 glhanoi->camera[0] = (float)x_diff / (float)MI_WIDTH(mi);
1438 glhanoi->camera[1] = (float)y_diff / (float)MI_HEIGHT(mi);
1445 ENTRYPOINT void release_glhanoi(ModeInfo * mi)
1447 if(glhanoi_cfg != NULL) {
1449 for(screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1452 glhcfg *glh = &glhanoi_cfg[screen];
1453 glDeleteLists(glh->floorList, 1);
1454 glDeleteLists(glh->baseList, 1);
1455 glDeleteLists(glh->poleList, 1);
1456 glDeleteLists(glh->textureNames[0], 2);
1457 for(j = 0; j < glh->numberOfDisks; ++j) {
1458 glDeleteLists(glh->disk[j].displayList, 1);
1461 for(i = 0; i < 3; i++) {
1462 if(glh->pole[i].data != NULL) {
1463 free(glh->pole[i].data);
1472 XSCREENSAVER_MODULE ("GLHanoi", glhanoi)