1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* glhanoi, Copyright (c) 2005, 2009 Dave Atkinson <da@davea.org.uk>
3 * except noise function code Copyright (c) 2002 Ken Perlin
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];
153 static glhcfg *glhanoi_cfg = NULL;
158 static XrmOptionDescRec opts[] = {
159 {"-light", ".glhanoi.light", XrmoptionNoArg, "true"},
160 {"+light", ".glhanoi.light", XrmoptionNoArg, "false"},
161 {"-fog", ".glhanoi.fog", XrmoptionNoArg, "true"},
162 {"+fog", ".glhanoi.fog", XrmoptionNoArg, "false"},
163 {"-texture", ".glhanoi.texture", XrmoptionNoArg, "true"},
164 {"+texture", ".glhanoi.texture", XrmoptionNoArg, "false"}
167 static argtype vars[] = {
168 {&light, "light", "Light", DEF_LIGHT, t_Bool},
169 {&fog, "fog", "Fog", DEF_FOG, t_Bool},
170 {&texture, "texture", "Texture", DEF_TEXTURE, t_Bool}
173 static OptionStruct desc[] = {
174 {"+/-light", "whether to light the scene"},
175 {"+/-fog", "whether to apply fog to the scene"},
176 {"+/-texture", "whether to apply texture to the scene"}
179 ENTRYPOINT ModeSpecOpt glhanoi_opts = { countof(opts), opts, countof(vars), vars, desc };
183 ModStruct glhanoi_description = {
184 "glhanoi", "init_glhanoi", "draw_glhanoi", "release_glhanoi",
185 "draw_glhanoi", "init_glhanoi", NULL, &glhanoi_opts,
186 1000, 1, 2, 1, 4, 1.0, "",
187 "Towers of Hanoi", 0, NULL
192 static const GLfloat cBlack[] = { 0.0, 0.0, 0.0, 1.0 };
193 static const GLfloat cWhite[] = { 1.0, 1.0, 1.0, 1.0 };
194 static const GLfloat poleColor[] = { 0.545, 0.137, 0.137 };
195 static const GLfloat baseColor[] = { 0.34, 0.34, 0.48 };
196 static const GLfloat fogcolor[] = { 0.5, 0.5, 0.5 };
198 static const float left[] = { 1.0, 0.0, 0.0 };
199 static const float up[] = { 0.0, 1.0, 0.0 };
200 static const float front[] = { 0.0, 0.0, 1.0 };
201 static const float right[] = { -1.0, 0.0, 0.0 };
202 static const float down[] = { 0.0, -1.0, 0.0 };
203 static const float back[] = { 0.0, 0.0, -1.0 };
205 static const GLfloat pos0[4] = { 50.0, 50.0, 50.0, 0.0 };
206 static const GLfloat amb0[4] = { 0.0, 0.0, 0.0, 1.0 };
207 static const GLfloat dif0[4] = { 1.0, 1.0, 1.0, 1.0 };
208 static const GLfloat spc0[4] = { 0.0, 1.0, 1.0, 1.0 };
210 static const GLfloat pos1[4] = { -50.0, 50.0, -50.0, 0.0 };
211 static const GLfloat amb1[4] = { 0.0, 0.0, 0.0, 1.0 };
212 static const GLfloat dif1[4] = { 1.0, 1.0, 1.0, 1.0 };
213 static const GLfloat spc1[4] = { 1.0, 1.0, 1.0, 1.0 };
215 static float g = 3.0 * 9.80665; /* hmm, looks like we need more gravity, Scotty... */
217 #define DOPUSH(X, Y) (((X)->count) >= ((X)->size)) ? NULL : ((X)->data[(X)->count++] = (Y))
218 #define DOPOP(X) (X)->count <= 0 ? NULL : ((X)->data[--((X)->count)])
220 static Disk *push(glhcfg *glhanoi, int idx, Disk * d)
222 return DOPUSH(&glhanoi->pole[idx], d);
225 static Disk *pop(glhcfg *glhanoi, int idx)
227 return DOPOP(&glhanoi->pole[idx]);
230 static inline void swap(int *x, int *y)
238 * magic - it's magic...
240 static int magic(int i)
245 while((i & 0x01) == 0) {
249 return count % 2 == 0;
253 static float distance(float *p0, float *p1)
259 return (float)sqrt(x * x + y * y + z * z);
263 static GLfloat A(GLfloat a, GLfloat b, GLfloat c)
266 return c / (a * b - 0.25 * sum * sum);
269 static void moveSetup(glhcfg *glhanoi, Disk * disk)
273 int src = glhanoi->src;
274 int dst = glhanoi->dst;
276 GLfloat sintheta, costheta;
279 if(glhanoi->state != FINISHED) {
280 double xxx = -180.0 * (dst - src >= 0 ? 1.0 : -1.0);
281 if(random() % 6 == 0) {
282 disk->rotAngle = xxx * (2 - 2 * random() % 2) * (random() % 3 + 1);
284 disk->rotAngle = xxx;
286 if(random() % 4 == 0) {
287 disk->rotAngle = -disk->rotAngle;
290 disk->rotAngle = -180.0;
293 disk->base0 = glhanoi->diskPos[glhanoi->pole[src].count];
296 FINISHED ? disk->base0 : glhanoi->diskPos[glhanoi->pole[dst].
299 disk->xmin = glhanoi->poleOffset * (src - 1);
300 disk->xmax = glhanoi->poleOffset * (dst - 1);
301 disk->ymin = glhanoi->poleHeight;
303 absx = fabs(disk->xmax - disk->xmin);
304 ymax = glhanoi->poleHeight + absx;
305 if(glhanoi->state == FINISHED) {
306 ymax += absx * (double)(glhanoi->numberOfDisks - disk->id);
308 h = ymax - disk->ymin;
309 theta = atan((disk->xmin - disk->xmax) * A(disk->xmin, disk->xmax, h));
312 costheta = cos(theta);
313 sintheta = sin(theta);
317 (2.0 * A(disk->xmin, disk->xmax, h) * costheta * costheta)));
318 disk->usintheta = u * sintheta;
319 disk->ucostheta = u * costheta;
321 (-u + sqrt(u * u + 2.0 * g * fabs(disk->ymin - disk->base0))) / g;
322 disk->u1 = u + g * disk->t1;
323 disk->t2 = 2.0 * disk->usintheta / g;
324 disk->u2 = disk->usintheta - g * disk->t2;
327 static void makeMove(glhcfg *glhanoi)
329 int fudge = glhanoi->move + 2;
330 int magicNumber = magic(fudge);
332 glhanoi->currentDisk = pop(glhanoi, glhanoi->src);
333 moveSetup(glhanoi, glhanoi->currentDisk);
334 push(glhanoi, glhanoi->dst, glhanoi->currentDisk);
337 if(fudge == 1 || magicNumber) {
338 swap(&glhanoi->src, &glhanoi->tmp);
340 if(fudge == 0 || glhanoi->magicNumber) {
341 swap(&glhanoi->dst, &glhanoi->tmp);
343 glhanoi->magicNumber = magicNumber;
346 static double lerp(double alpha, double start, double end)
348 return start + alpha * (end - start);
351 static void upfunc(GLdouble t, Disk * d)
353 d->position[0] = d->xmin;
354 d->position[1] = d->base0 + (d->u1 - 0.5 * g * t) * t;
356 d->rotation[1] = 0.0;
359 static void parafunc(GLdouble t, Disk * d)
361 d->position[0] = d->xmin + d->ucostheta * t;
362 d->position[1] = d->ymin + (d->usintheta - 0.5 * g * t) * t;
365 d->rotAngle * (d->position[0] - d->xmin) / (d->xmax - d->xmin);
368 static void downfunc(GLdouble t, Disk * d)
370 d->position[0] = d->xmax;
371 d->position[1] = d->ymin + (d->u2 - 0.5 * g * t) * t;
373 d->rotation[1] = 0.0;
376 static Bool computePosition(GLfloat t, Disk * d)
378 Bool finished = False;
382 } else if(t < d->t1 + d->t2) {
383 parafunc(t - d->t1, d);
385 downfunc(t - d->t1 - d->t2, d);
386 if(d->position[1] <= d->base1) {
387 d->position[1] = d->base1;
394 static void updateView(glhcfg *glhanoi)
396 double longitude, latitude, radius;
397 double a, b, c, A, B;
399 get_position(glhanoi->the_rotator, NULL, NULL, &radius,
400 !glhanoi->button_down_p);
401 get_rotation(glhanoi->the_rotator, &longitude, &latitude, NULL,
402 !glhanoi->button_down_p);
403 longitude += glhanoi->camera[0];
404 latitude += glhanoi->camera[1];
405 radius += glhanoi->camera[2];
406 longitude = longitude - floor(longitude);
407 latitude = latitude - floor(latitude);
408 radius = radius - floor(radius);
410 latitude = 1.0 - latitude;
413 radius = 1.0 - radius;
416 b = glhanoi->centre[1];
417 c = (MIN_CAMERA_RADIUS +
418 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS));
419 A = M_PI / 4.0 * (1.0 - latitude);
420 a = sqrt(b * b + c * c - 2.0 * b * c * cos(A));
421 B = asin(sin(A) * b / a);
422 glRotatef(-B * 180 / M_PI, 1.0, 0.0, 0.0);
424 glTranslatef(0.0f, 0.0f,
425 -(MIN_CAMERA_RADIUS +
426 radius * (MAX_CAMERA_RADIUS - MIN_CAMERA_RADIUS)));
427 glRotatef(longitude * 360.0, 0.0f, 1.0f, 0.0f);
428 glRotatef(latitude * 180.0, cos(longitude * 2.0 * M_PI), 0.0,
429 sin(longitude * 2.0 * M_PI));
432 static void changeState(glhcfg *glhanoi, State state)
434 glhanoi->state = state;
435 glhanoi->startTime = getTime();
438 static void update_glhanoi(glhcfg *glhanoi)
440 double t = getTime() - glhanoi->startTime;
444 switch (glhanoi->state) {
446 if(t < glhanoi->duration) {
450 if(glhanoi->numberOfDisks % 2 == 0) {
451 swap(&glhanoi->tmp, &glhanoi->dst);
453 glhanoi->magicNumber = 1;
455 changeState(glhanoi, MOVE_DISK);
459 if(computePosition(t, glhanoi->currentDisk)) {
460 changeState(glhanoi, MOVE_FINISHED);
465 if(++glhanoi->move < glhanoi->numberOfMoves) {
467 changeState(glhanoi, MOVE_DISK);
469 glhanoi->duration = FINISH_DURATION;
470 changeState(glhanoi, FINISHED);
475 while(t < glhanoi->duration) {
478 glhanoi->src = glhanoi->olddst;
479 glhanoi->dst = glhanoi->oldsrc;
480 for(i = 0; i < glhanoi->numberOfDisks; ++i) {
481 Disk *disk = pop(glhanoi, glhanoi->src);
482 assert(disk != NULL);
483 moveSetup(glhanoi, disk);
485 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
486 push(glhanoi, glhanoi->dst, &glhanoi->disk[i]);
488 changeState(glhanoi, MONEY_SHOT);
493 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
494 double delay = 0.25 * i;
502 finished = computePosition(t - delay, &glhanoi->disk[i]);
503 glhanoi->disk[i].rotation[1] = 0.0;
510 glhanoi->src = glhanoi->oldsrc;
511 glhanoi->tmp = glhanoi->oldtmp;
512 glhanoi->dst = glhanoi->olddst;
513 changeState(glhanoi, START);
519 fprintf(stderr, "Invalid state\n");
524 static void HSVtoRGBf(GLfloat h, GLfloat s, GLfloat v,
525 GLfloat * r, GLfloat * g, GLfloat * b)
532 GLfloat i, f, p, q, t;
536 h /= 60.0; /* h now in [0,6). */
537 i = floor((double)h); /* i now largest integer <= h */
538 f = h - i; /* f is no fractional part of h */
540 q = v * (1.0 - (s * f));
541 t = v * (1.0 - (s * (1.0 - f)));
577 static void HSVtoRGBv(GLfloat * hsv, GLfloat * rgb)
579 HSVtoRGBf(hsv[0], hsv[1], hsv[2], &rgb[0], &rgb[1], &rgb[2]);
582 static void setMaterial(const GLfloat color[3], const GLfloat hlite[3], int shininess)
585 glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
586 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
587 glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
591 * drawTube: I know all this stuff is available in gluQuadrics
592 * but I'd originally intended to texture the poles with a 3D wood
593 * texture, but I was having difficulty getting wood... what? Why
594 * are all you Amercians laughing..? Anyway, I don't know if enough
595 * people's hardware supports 3D textures, so I didn't bother (xorg
596 * ATI server doesn't :-( )
598 static int drawTube(GLdouble bottomRadius, GLdouble topRadius,
599 GLdouble bottomThickness, GLdouble topThickness,
600 GLdouble height, GLuint nSlice, GLuint nLoop)
604 GLfloat *cosCache = malloc(sizeof(GLfloat) * nSlice);
605 GLfloat *sinCache = malloc(sizeof(GLfloat) * nSlice);
608 GLint lastSlice = nSlice - 1;
613 if(bottomThickness > bottomRadius) {
614 bottomThickness = bottomRadius;
616 if(topThickness > topRadius) {
617 topThickness = topRadius;
619 if(bottomThickness < 0.0) {
620 bottomThickness = 0.0;
622 if(topThickness < 0.0) {
625 if(topRadius >= bottomRadius) {
626 maxRadius = topRadius;
628 maxRadius = bottomRadius;
633 radius = bottomRadius;
634 innerRadius = bottomRadius - bottomThickness;
635 /* innerTexCoordSize = texCoordSize * innerRadius / maxRadius; */
636 /* outerTexCoordSize = texCoordSize * radius / maxRadius; */
637 /* yTexCoord = minTexCoord; */
639 glBegin(GL_QUAD_STRIP);
641 glNormal3f(0.0, -1.0, 0.0);
643 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + innerTexCoordSize); */
644 glVertex3f(0.0, y, innerRadius);
646 /* glTexCoord3f(midTexCoord, yTexCoord, midTexCoord + outerTexCoordSize); */
647 glVertex3f(0.0, y, radius);
649 for(slice = lastSlice; slice >= 0; --slice) {
650 GLfloat theta = 2.0 * M_PI * (double)slice / nSlice;
652 cosCache[slice] = cos(theta);
653 sinCache[slice] = sin(theta);
655 /* glTexCoord3f(midTexCoord + sinCache[slice] * innerTexCoordSize, */
657 /* midTexCoord + cosCache[slice] * innerTexCoordSize); */
658 glVertex3f(innerRadius * sinCache[slice], y,
659 innerRadius * cosCache[slice]);
660 /* glTexCoord3f(midTexCoord + sinCache[slice] * outerTexCoordSize, */
662 /* midTexCoord + cosCache[slice] * outerTexCoordSize); */
663 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
669 for(loop = 0; loop < nLoop; ++loop) {
670 GLfloat lowerRadius =
671 bottomRadius + (topRadius -
672 bottomRadius) * (float)loop / (nLoop);
673 GLfloat upperRadius =
674 bottomRadius + (topRadius - bottomRadius) * (float)(loop +
677 GLfloat lowerY = height * (float)loop / (nLoop);
678 GLfloat upperY = height * (float)(loop + 1) / (nLoop);
679 GLfloat factor = (topRadius - topThickness) -
680 (bottomRadius - bottomThickness);
683 glBegin(GL_QUAD_STRIP);
684 for(slice = 0; slice < nSlice; ++slice) {
685 glNormal3f(sinCache[slice], 0.0, cosCache[slice]);
686 glVertex3f(upperRadius * sinCache[slice], upperY,
687 upperRadius * cosCache[slice]);
688 glVertex3f(lowerRadius * sinCache[slice], lowerY,
689 lowerRadius * cosCache[slice]);
692 glNormal3f(0.0, 0.0, 1.0);
693 glVertex3f(0.0, upperY, upperRadius);
694 glVertex3f(0.0, lowerY, lowerRadius);
699 lowerRadius = bottomRadius - bottomThickness +
700 factor * (float)loop / (nLoop);
701 upperRadius = bottomRadius - bottomThickness +
702 factor * (float)(loop + 1) / (nLoop);
704 glBegin(GL_QUAD_STRIP);
705 glNormal3f(0.0, 0.0, -1.0);
706 glVertex3f(0.0, upperY, upperRadius);
707 glVertex3f(0.0, lowerY, lowerRadius);
708 for(slice = lastSlice; slice >= 0; --slice) {
709 glNormal3f(-sinCache[slice], 0.0, -cosCache[slice]);
710 glVertex3f(upperRadius * sinCache[slice], upperY,
711 upperRadius * cosCache[slice]);
712 glVertex3f(lowerRadius * sinCache[slice], lowerY,
713 lowerRadius * cosCache[slice]);
722 innerRadius = topRadius - topThickness;
724 glBegin(GL_QUAD_STRIP);
725 glNormal3f(0.0, 1.0, 0.0);
726 for(slice = 0; slice < nSlice; ++slice) {
727 glVertex3f(innerRadius * sinCache[slice], y,
728 innerRadius * cosCache[slice]);
730 glVertex3f(radius * sinCache[slice], y, radius * cosCache[slice]);
733 glVertex3f(0.0, y, innerRadius);
734 glVertex3f(0.0, y, radius);
739 static int drawPole(GLfloat radius, GLfloat length)
741 return drawTube(radius, radius, radius, radius, length, NSLICE, NLOOPS);
744 static int drawDisk3D(GLdouble inner_radius, GLdouble outer_radius,
747 return drawTube(outer_radius, outer_radius, outer_radius - inner_radius,
748 outer_radius - inner_radius, height, NSLICE, NLOOPS);
751 static int drawCuboid(GLfloat length, GLfloat width, GLfloat height)
753 GLfloat xmin = -length / 2.0f;
754 GLfloat xmax = length / 2.0f;
755 GLfloat zmin = -width / 2.0f;
756 GLfloat zmax = width / 2.0f;
758 GLfloat ymax = height;
764 glVertex3f(xmin, ymin, zmax); /* 0 */
765 glVertex3f(xmax, ymin, zmax); /* 1 */
766 glVertex3f(xmax, ymax, zmax); /* 2 */
767 glVertex3f(xmin, ymax, zmax); /* 3 */
771 glVertex3f(xmax, ymin, zmax); /* 1 */
772 glVertex3f(xmax, ymin, zmin); /* 5 */
773 glVertex3f(xmax, ymax, zmin); /* 6 */
774 glVertex3f(xmax, ymax, zmax); /* 2 */
778 glVertex3f(xmax, ymin, zmin); /* 5 */
779 glVertex3f(xmin, ymin, zmin); /* 4 */
780 glVertex3f(xmin, ymax, zmin); /* 7 */
781 glVertex3f(xmax, ymax, zmin); /* 6 */
785 glVertex3f(xmin, ymin, zmin); /* 4 */
786 glVertex3f(xmin, ymin, zmax); /* 0 */
787 glVertex3f(xmin, ymax, zmax); /* 3 */
788 glVertex3f(xmin, ymax, zmin); /* 7 */
792 glVertex3f(xmin, ymax, zmax); /* 3 */
793 glVertex3f(xmax, ymax, zmax); /* 2 */
794 glVertex3f(xmax, ymax, zmin); /* 6 */
795 glVertex3f(xmin, ymax, zmin); /* 7 */
799 glVertex3f(xmin, ymin, zmin); /* 4 */
800 glVertex3f(xmax, ymin, zmin); /* 5 */
801 glVertex3f(xmax, ymin, zmax); /* 1 */
802 glVertex3f(xmin, ymin, zmax); /* 0 */
808 static int drawDisks(glhcfg *glhanoi)
814 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
815 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
816 Disk *disk = &glhanoi->disk[i];
817 GLfloat *pos = disk->position;
818 GLfloat *rot = disk->rotation;
821 glTranslatef(pos[0], pos[1], pos[2]);
823 glTranslatef(0.0, glhanoi->diskHeight / 2.0, 0.0);
824 glRotatef(rot[1], 0.0, 0.0, 1.0);
825 glTranslatef(0.0, -glhanoi->diskHeight / 2.0, 0.0);
827 glCallList(disk->displayList);
828 polys += disk->polys;
835 static GLfloat getDiskRadius(glhcfg *glhanoi, int i)
837 return ((GLfloat) i + 3.0) * glhanoi->poleRadius;
840 static void initData(glhcfg *glhanoi)
842 GLfloat maxDiskRadius;
845 glhanoi->baseLength = BASE_LENGTH;
846 glhanoi->poleRadius = glhanoi->baseLength /
847 (2.0 * (3 * glhanoi->numberOfDisks + 7.0));
848 maxDiskRadius = getDiskRadius(glhanoi, glhanoi->numberOfDisks);
849 glhanoi->baseWidth = 2.0 * maxDiskRadius;
850 glhanoi->poleOffset = 2.0 * getDiskRadius(glhanoi, glhanoi->maxDiskIdx);
851 glhanoi->diskHeight = 2.0 * glhanoi->poleRadius;
852 glhanoi->baseHeight = 2.0 * glhanoi->poleRadius;
853 glhanoi->poleHeight = glhanoi->numberOfDisks *
854 glhanoi->diskHeight + glhanoi->poleRadius;
855 glhanoi->numberOfMoves = (1 << glhanoi->numberOfDisks) - 1;
856 glhanoi->boardSize = glhanoi->baseLength * 0.5 * (1.0 + sqrt(5.0));
858 for(i = 0; i < 3; i++) {
859 if((glhanoi->pole[i].data =
860 calloc(glhanoi->numberOfDisks, sizeof(Disk *))) == NULL) {
861 fprintf(stderr, "%s: out of memory creating stack %d\n",
865 glhanoi->pole[i].size = glhanoi->numberOfDisks;
867 if((glhanoi->diskPos =
868 calloc(glhanoi->numberOfDisks, sizeof(double))) == NULL) {
869 fprintf(stderr, "%s: out of memory creating diskPos\n", progname);
873 glhanoi->the_rotator = make_rotator(0.1, 0.025, 0, 1, 0.005, False);
874 glhanoi->button_down_p = False;
876 glhanoi->src = glhanoi->oldsrc = 0;
877 glhanoi->tmp = glhanoi->oldtmp = 1;
878 glhanoi->dst = glhanoi->olddst = 2;
881 static void initView(glhcfg *glhanoi)
883 glhanoi->camera[0] = 0.0;
884 glhanoi->camera[1] = 0.0;
885 glhanoi->camera[2] = 0.0;
886 glhanoi->centre[0] = 0.0;
887 glhanoi->centre[1] = glhanoi->poleHeight * 3.0;
888 glhanoi->centre[2] = 0.0;
892 * noise_improved.c - based on ImprovedNoise.java
893 * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
895 static double fade(double t)
897 return t * t * t * (t * (t * 6 - 15) + 10);
900 static double grad(int hash, double x, double y, double z)
902 int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
903 double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
904 v = h < 4 ? y : h == 12 || h == 14 ? x : z;
905 return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
908 static const int permutation[] = { 151, 160, 137, 91, 90, 15,
909 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
910 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26,
911 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
912 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
913 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60,
914 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143,
915 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
916 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198,
917 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118,
918 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
919 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
920 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108,
921 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251,
922 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145,
923 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
924 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
925 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
929 static void initNoise(glhcfg *glhanoi)
932 for(i = 0; i < 256; i++)
933 glhanoi->p[256 + i] = glhanoi->p[i] = permutation[i];
936 static double improved_noise(glhcfg *glhanoi, double x, double y, double z)
939 int A, AA, AB, B, BA, BB;
940 int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
941 Y = (int)floor(y) & 255, /* CONTAINS POINT. */
942 Z = (int)floor(z) & 255;
943 if(!glhanoi->noise_initted) {
945 glhanoi->noise_initted = 1;
947 x -= floor(x); /* FIND RELATIVE X,Y,Z */
948 y -= floor(y); /* OF POINT IN CUBE. */
950 u = fade(x), /* COMPUTE FADE CURVES */
951 v = fade(y), /* FOR EACH OF X,Y,Z. */
953 A = glhanoi->p[X] + Y;
954 AA = glhanoi->p[A] + Z;
955 AB = glhanoi->p[A + 1] + Z, /* HASH COORDINATES OF */
956 B = glhanoi->p[X + 1] + Y;
957 BA = glhanoi->p[B] + Z;
958 BB = glhanoi->p[B + 1] + Z; /* THE 8 CUBE CORNERS, */
959 return lerp(w, lerp(v, lerp(u, grad(glhanoi->p[AA], x, y, z),/* AND ADD */
960 grad(glhanoi->p[BA], x - 1, y, z)),/* BLENDED */
961 lerp(u, grad(glhanoi->p[AB], x, y - 1, z),/* RESULTS */
962 grad(glhanoi->p[BB], x - 1, y - 1, z))),/* FROM 8 CORNERS */
963 lerp(v, lerp(u, grad(glhanoi->p[AA + 1], x, y, z - 1), grad(glhanoi->p[BA + 1], x - 1, y, z - 1)), /* OF CUBE */
964 lerp(u, grad(glhanoi->p[AB + 1], x, y - 1, z - 1),
965 grad(glhanoi->p[BB + 1], x - 1, y - 1, z - 1))));
969 * end noise_improved.c - based on ImprovedNoise.java
974 /* GLfloat *points; */
977 typedef struct tex_col_t tex_col_t;
979 static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
980 GLuint(*texFunc) (glhcfg *, double, double, double,
981 tex_col_t *), tex_col_t * colours)
984 GLubyte *textureData;
990 calloc(x_size * y_size * z_size, sizeof(GLuint))) == NULL) {
999 texturePtr = (void *)textureData;
1000 for(k = 0; k < z_size; k++, z += zi) {
1002 for(j = 0; j < y_size; j++, y += yi) {
1004 for(i = 0; i < x_size; i++, x += xi) {
1005 *texturePtr = texFunc(glhanoi, x, y, z, colours);
1013 static void freeTexCols(tex_col_t*p)
1019 static tex_col_t *makeMarbleColours(void)
1021 tex_col_t *marbleColours;
1024 marbleColours = malloc(sizeof(tex_col_t));
1025 if(marbleColours == NULL) return NULL;
1026 marbleColours->colours = calloc(sizeof(GLuint), ncols);
1027 if(marbleColours->colours == NULL) return NULL;
1028 marbleColours->ncols = ncols;
1030 marbleColours->colours[0] = 0x3f3f3f3f;
1031 marbleColours->colours[1] = 0xffffffff;
1033 return marbleColours;
1036 static double turb(glhcfg *glhanoi, double x, double y, double z, int octaves)
1041 for(oct = 0; oct < octaves; ++oct) {
1042 r += fabs(improved_noise(glhanoi, freq * x, freq * y, freq * z)) / freq;
1048 static void perturb(glhcfg *glhanoi, double *x, double *y, double *z, double scale)
1050 double t = scale * turb(glhanoi, *x, *y, *z, 4);
1056 static double f_m(double x, double y, double z)
1058 return sin(3.0 * M_PI * x);
1061 static GLuint C_m(double x, const tex_col_t * tex_cols)
1063 int r = tex_cols->colours[0] & 0xff;
1064 int g = tex_cols->colours[0] >> 8 & 0xff;
1065 int b = tex_cols->colours[0] >> 16 & 0xff;
1070 factor = (1.0 + sin(2.0 * M_PI * x)) / 2.0;
1072 r1 = (tex_cols->colours[1] & 0xff);
1073 g1 = (tex_cols->colours[1] >> 8 & 0xff);
1074 b1 = (tex_cols->colours[1] >> 16 & 0xff);
1076 r += (int)(factor * (r1 - r));
1077 g += (int)(factor * (g1 - g));
1078 b += (int)(factor * (b1 - b));
1080 return 0xff000000 | (b << 16) | (g << 8) | r;
1084 static GLuint makeMarbleTexture(glhcfg *glhanoi, double x, double y, double z, tex_col_t * colours)
1086 perturb(glhanoi, &x, &y, &z, MARBLE_SCALE);
1087 return C_m(f_m(x, y, z), colours);
1090 static void setTexture(glhcfg *glhanoi, int n)
1092 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[n]);
1095 static int makeTextures(glhcfg *glhanoi)
1097 GLubyte *marbleTexture;
1098 tex_col_t *marbleColours;
1100 glGenTextures(N_TEXTURES, glhanoi->textureNames);
1102 if((marbleColours = makeMarbleColours()) == NULL) {
1106 makeTexture(glhanoi, MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 1,
1107 makeMarbleTexture, marbleColours)) == NULL) {
1111 glBindTexture(GL_TEXTURE_2D, glhanoi->textureNames[0]);
1112 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1113 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1116 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
1117 MARBLE_TEXTURE_SIZE, MARBLE_TEXTURE_SIZE, 0,
1118 GL_RGBA, GL_UNSIGNED_BYTE, marbleTexture);
1119 free(marbleTexture);
1120 freeTexCols(marbleColours);
1125 static void initFloor(glhcfg *glhanoi)
1128 float tileSize = glhanoi->boardSize / BOARD_SQUARES;
1129 float x0, x1, z0, z1;
1130 float tx0, tx1, tz0, tz1;
1131 const float *col = cWhite;
1132 float texIncr = 1.0 / BOARD_SQUARES;
1134 glhanoi->floorpolys = 0;
1135 if((glhanoi->floorList = glGenLists(1)) == 0) {
1136 fprintf(stderr, "can't allocate memory for floor display list\n");
1139 glNewList(glhanoi->floorList, GL_COMPILE);
1140 x0 = -glhanoi->boardSize / 2.0;
1142 setMaterial(col, cWhite, 128);
1143 setTexture(glhanoi, 0);
1145 for(i = 0; i < BOARD_SQUARES; i++, x0 += tileSize, tx0 += texIncr) {
1147 tx1 = tx0 + texIncr;
1148 z0 = -glhanoi->boardSize / 2.0;
1150 for(j = 0; j < BOARD_SQUARES; j++, z0 += tileSize, tz0 += texIncr) {
1151 int colIndex = (i + j) & 0x1;
1154 tz1 = tz0 + texIncr;
1161 setMaterial(col, cWhite, 100);
1165 glTexCoord2d(tx0, tz0);
1166 glVertex3f(x0, 0.0, z0);
1168 glTexCoord2d(tx0, tz1);
1169 glVertex3f(x0, 0.0, z1);
1171 glTexCoord2d(tx1, tz1);
1172 glVertex3f(x1, 0.0, z1);
1174 glTexCoord2d(tx1, tz0);
1175 glVertex3f(x1, 0.0, z0);
1176 glhanoi->floorpolys++;
1183 static void initTowers(glhcfg *glhanoi)
1185 if((glhanoi->baseList = glGenLists(1)) == 0) {
1186 fprintf(stderr, "can't allocate memory for towers display list\n");
1189 glNewList(glhanoi->baseList, GL_COMPILE);
1190 setMaterial(baseColor, cWhite, 50);
1191 glhanoi->basepolys = drawCuboid(glhanoi->baseLength, glhanoi->baseWidth,
1192 glhanoi->baseHeight);
1195 if((glhanoi->poleList = glGenLists(1)) == 0) {
1196 fprintf(stderr, "can't allocate memory for towers display list\n");
1199 glNewList(glhanoi->poleList, GL_COMPILE);
1201 glTranslatef(0.0f, glhanoi->baseHeight, 0.0f);
1202 setMaterial(poleColor, cWhite, 50);
1203 glhanoi->polepolys = drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1205 glTranslatef(-glhanoi->poleOffset, 0.0, 0.0);
1206 glhanoi->polepolys += drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1208 glTranslatef(glhanoi->poleOffset, 0.0, 0.0);
1209 glhanoi->polepolys += drawPole(glhanoi->poleRadius, glhanoi->poleHeight);
1214 static double cfunc(double x)
1218 return (1.0 / 12.0) / (1.0 / 7.0) * x;
1221 return (1.0 + 1.0 / 6.0) * x - 1.0 / 6.0;
1224 return (2.0 + 1.0 / 3.0) * x - 2.0 / 3.0;
1227 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1229 return (1.0 / 12.0) / (1.0 / 7.0) * x + 1.0 / 3.0;
1232 static void initDisks(glhcfg *glhanoi)
1236 (Disk *) calloc(glhanoi->numberOfDisks, sizeof(Disk))) == NULL) {
1237 perror("initDisks");
1241 for(i = glhanoi->maxDiskIdx; i >= 0; i--) {
1242 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1243 double f = cfunc((GLfloat) i / (GLfloat) glhanoi->numberOfDisks);
1244 GLfloat diskColor = f * 360.0;
1246 Disk *disk = &glhanoi->disk[i];
1249 disk->position[0] = -glhanoi->poleOffset;
1250 disk->position[1] = glhanoi->diskHeight * height;
1251 disk->position[2] = 0.0;
1252 disk->rotation[0] = 0.0;
1253 disk->rotation[1] = 0.0;
1254 disk->rotation[2] = 0.0;
1257 color[0] = diskColor;
1260 HSVtoRGBv(color, color);
1262 if((disk->displayList = glGenLists(1)) == 0) {
1264 "can't allocate memory for disk %d display list\n", i);
1267 glNewList(disk->displayList, GL_COMPILE);
1268 setMaterial(color, cWhite, 100.0);
1269 disk->polys += drawDisk3D(glhanoi->poleRadius,
1270 getDiskRadius(glhanoi, i),
1271 glhanoi->diskHeight);
1274 for(i = glhanoi->maxDiskIdx; i >= 0; --i) {
1275 GLfloat height = (GLfloat) (glhanoi->maxDiskIdx - i);
1276 int h = glhanoi->maxDiskIdx - i;
1277 glhanoi->diskPos[h] = glhanoi->diskHeight * height;
1278 push(glhanoi, glhanoi->src, &glhanoi->disk[i]);
1282 static void initLights(Bool state)
1285 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
1286 glLightfv(GL_LIGHT0, GL_AMBIENT, amb0);
1287 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif0);
1288 glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1290 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
1291 glLightfv(GL_LIGHT1, GL_AMBIENT, amb1);
1292 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif1);
1293 glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1295 glEnable(GL_LIGHTING);
1296 glEnable(GL_LIGHT0);
1297 glEnable(GL_LIGHT1);
1299 glDisable(GL_LIGHTING);
1303 static int drawFloor(glhcfg *glhanoi)
1305 glCallList(glhanoi->floorList);
1306 return glhanoi->floorpolys;
1309 static int drawTowers(glhcfg *glhanoi)
1311 glCallList(glhanoi->baseList);
1312 glCallList(glhanoi->poleList);
1313 return glhanoi->basepolys + glhanoi->polepolys;
1316 /* Window management, etc
1318 ENTRYPOINT void reshape_glhanoi(ModeInfo * mi, int width, int height)
1320 glViewport(0, 0, (GLint) width, (GLint) height);
1322 glMatrixMode(GL_PROJECTION);
1324 gluPerspective(30.0, (GLdouble) width / (GLdouble) height, 1.0,
1325 2 * MAX_CAMERA_RADIUS);
1327 glMatrixMode(GL_MODELVIEW);
1330 glClear(GL_COLOR_BUFFER_BIT);
1333 ENTRYPOINT void init_glhanoi(ModeInfo * mi)
1338 (glhcfg *) calloc(MI_NUM_SCREENS(mi), sizeof(glhcfg));
1340 fprintf(stderr, "%s: out of memory creating configs\n",
1346 glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1347 glhanoi->glx_context = init_GL(mi);
1348 glhanoi->numberOfDisks = MI_BATCHCOUNT(mi);
1350 if (glhanoi->numberOfDisks <= 1)
1351 glhanoi->numberOfDisks = 3 + (int) BELLRAND(9);
1353 /* magicnumber is a bitfield, so we can't have more than 31 discs
1354 on a system with 4-byte ints. */
1355 if (glhanoi->numberOfDisks >= 8 * sizeof(int))
1356 glhanoi->numberOfDisks = (8 * sizeof(int)) - 1;
1358 glhanoi->maxDiskIdx = glhanoi->numberOfDisks - 1;
1359 glhanoi->wire = MI_IS_WIREFRAME(mi);
1360 glhanoi->light = light;
1362 glhanoi->texture = texture;
1364 reshape_glhanoi(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1367 glhanoi->light = False;
1368 glhanoi->fog = False;
1369 glhanoi->texture = False;
1372 initLights(!glhanoi->wire && glhanoi->light);
1373 if(makeTextures(glhanoi) != 0) {
1374 fprintf(stderr, "can't allocate memory for marble texture\n");
1381 initTowers(glhanoi);
1384 glEnable(GL_DEPTH_TEST);
1385 glEnable(GL_NORMALIZE);
1386 glEnable(GL_CULL_FACE);
1387 glShadeModel(GL_SMOOTH);
1389 glClearColor(fogcolor[0], fogcolor[1], fogcolor[2], 1.0);
1390 glFogi(GL_FOG_MODE, GL_LINEAR);
1391 glFogfv(GL_FOG_COLOR, fogcolor);
1392 glFogf(GL_FOG_DENSITY, 0.35f);
1393 glHint(GL_FOG_HINT, GL_NICEST);
1394 glFogf(GL_FOG_START, MIN_CAMERA_RADIUS);
1395 glFogf(GL_FOG_END, MAX_CAMERA_RADIUS / 1.9);
1400 glhanoi->duration = START_DURATION;
1401 changeState(glhanoi, START);
1404 ENTRYPOINT void draw_glhanoi(ModeInfo * mi)
1406 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1407 Display *dpy = MI_DISPLAY(mi);
1408 Window window = MI_WINDOW(mi);
1410 if(!glhanoi->glx_context)
1413 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(glhanoi->glx_context));
1415 glPolygonMode(GL_FRONT, glhanoi->wire ? GL_LINE : GL_FILL);
1417 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1418 mi->polygon_count = 0;
1422 update_glhanoi(glhanoi);
1423 updateView(glhanoi);
1425 if(!glhanoi->wire && glhanoi->texture) {
1426 glEnable(GL_TEXTURE_2D);
1428 mi->polygon_count += drawFloor(glhanoi);
1429 glDisable(GL_TEXTURE_2D);
1431 mi->polygon_count += drawTowers(glhanoi);
1432 mi->polygon_count += drawDisks(glhanoi);
1439 glXSwapBuffers(dpy, window);
1442 ENTRYPOINT Bool glhanoi_handle_event(ModeInfo * mi, XEvent * event)
1444 glhcfg *glhanoi = &glhanoi_cfg[MI_SCREEN(mi)];
1446 if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
1447 glhanoi->button_down_p = True;
1448 glhanoi->drag_x = event->xbutton.x;
1449 glhanoi->drag_y = event->xbutton.y;
1451 } else if(event->xany.type == ButtonRelease
1452 && event->xbutton.button == Button1) {
1453 glhanoi->button_down_p = False;
1455 } else if(event->xany.type == ButtonPress &&
1456 (event->xbutton.button == Button4
1457 || event->xbutton.button == Button5)) {
1458 switch (event->xbutton.button) {
1460 glhanoi->camera[2] += 0.01;
1463 glhanoi->camera[2] -= 0.01;
1467 "glhanoi: unknown button in mousewheel handler\n");
1470 } else if(event->xany.type == MotionNotify
1471 && glhanoi_cfg->button_down_p) {
1474 x_diff = event->xbutton.x - glhanoi->drag_x;
1475 y_diff = event->xbutton.y - glhanoi->drag_y;
1477 glhanoi->camera[0] = (float)x_diff / (float)MI_WIDTH(mi);
1478 glhanoi->camera[1] = (float)y_diff / (float)MI_HEIGHT(mi);
1485 ENTRYPOINT void release_glhanoi(ModeInfo * mi)
1487 if(glhanoi_cfg != NULL) {
1489 for(screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1492 glhcfg *glh = &glhanoi_cfg[screen];
1493 glDeleteLists(glh->floorList, 1);
1494 glDeleteLists(glh->baseList, 1);
1495 glDeleteLists(glh->poleList, 1);
1496 glDeleteLists(glh->textureNames[0], 2);
1497 for(j = 0; j < glh->numberOfDisks; ++j) {
1498 glDeleteLists(glh->disk[j].displayList, 1);
1501 for(i = 0; i < 3; i++) {
1502 if(glh->pole[i].data != NULL) {
1503 free(glh->pole[i].data);
1512 XSCREENSAVER_MODULE ("GLHanoi", glhanoi)