From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / glx / maze3d.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* maze3d --- A recreation of the old 3D maze screensaver from Windows 95.
3  *
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation for any purpose and without fee is hereby granted,
6  * provided that the above copyright notice appear in all copies and that
7  * both that copyright notice and this permission notice appear in
8  * supporting documentation.
9  *
10  * This file is provided AS IS with no warranties of any kind.  The author
11  * shall have no liability with respect to the infringement of copyrights,
12  * trade secrets or any patents by this file or any part thereof.  In no
13  * event will the author be liable for any lost revenue or profits or
14  * other special, indirect and consequential damages.
15  *
16  * Revision History:
17  *
18  * 03-Apr-2018: Released initial version of "3D Maze"
19  * (sudoer@riseup.net)
20  */
21
22 #undef USE_FLOATING_IMAGES
23 #undef USE_FRACTAL_IMAGES
24
25 #ifdef STANDALONE
26 #define DEFAULTS        "*delay:                        20000   \n"     \
27                                         "*showFPS:                      False   \n" \
28
29 #define release_maze 0
30 # include "xlockmore.h"                         /* from the xscreensaver distribution */
31 #else  /* !STANDALONE */
32 # include "xlock.h"                                     /* from the xlockmore distribution */
33 #endif /* !STANDALONE */
34 #include <math.h>
35
36 #ifdef USE_GL /* whole file */
37
38 #define DEF_ANGULAR_CONVERSION_FACTOR 90
39 #define DEF_SPEED "1.0"
40 #define DEF_NUM_ROWS "12"
41 #define DEF_NUM_COLUMNS "12"
42 #define DEF_NUM_RATS "1"
43 #define DEF_NUM_INVERTERS "10"
44 #define DEF_SHOW_OVERLAY "False"
45 #define DEF_DROP_ACID "False"
46
47 #undef countof
48 #define countof(x) (sizeof((x))/sizeof((*x)))
49
50 #include "../images/gen/brick1_png.h"
51 #include "../images/gen/brick2_png.h"
52 #include "../images/gen/wood2_png.h"
53 #include "../images/gen/start_png.h"
54 #include "../images/gen/bob_png.h"
55 #include "../images/gen/logo-32_png.h"
56
57 #ifdef USE_FLOATING_IMAGES
58 # include "../images/gen/opengltxt_png.h"
59 # include "../images/gen/openglbook_png.h"
60 #endif
61 #ifdef USE_FRACTAL_IMAGES
62 # include "../images/gen/fractal1_png.h"
63 # include "../images/gen/fractal2_png.h"
64 # include "../images/gen/fractal3_png.h"
65 # include "../images/gen/fractal4_png.h"
66 #endif
67
68 #include "ximage-loader.h"
69
70 static int dropAcid, dropAcidWalls, dropAcidCeiling, dropAcidFloor, numInverters, numRats;
71 #ifdef USE_FLOATING_IMAGES
72 static int numGl3dTexts, numGlRedbooks;
73 #endif
74 static int shouldDrawOverlay, numRows, numColumns;
75 static char *wallTexture, *floorTexture, *ceilingTexture;
76 static GLfloat speed;
77
78 static XrmOptionDescRec opts[] = {
79         {"-drop-acid", ".maze3d.dropAcid", XrmoptionNoArg, "true"},
80         {"-drop-acid-walls", ".maze3d.dropAcidWalls", XrmoptionNoArg, "true"},
81         {"-drop-acid-floor", ".maze3d.dropAcidFloor", XrmoptionNoArg, "true"},
82         {"-drop-acid-ceiling", ".maze3d.dropAcidCeiling", XrmoptionNoArg, "true"},
83         {"-wall-texture", ".maze3d.wallTexture", XrmoptionSepArg, 0},
84         {"-floor-texture", ".maze3d.floorTexture", XrmoptionSepArg, 0},
85         {"-ceiling-texture", ".maze3d.ceilingTexture", XrmoptionSepArg, 0},
86         {"-rows", ".maze3d.numRows", XrmoptionSepArg, 0},
87         {"-columns", ".maze3d.numColumns", XrmoptionSepArg, 0},
88         {"-inverters", ".maze3d.numInverters", XrmoptionSepArg, 0},
89         {"-rats", ".maze3d.numRats", XrmoptionSepArg, 0},
90 # ifdef USE_FLOATING_IMAGES
91         {"-gl-3d-texts", ".maze3d.numGl3dTexts", XrmoptionSepArg, 0},
92         {"-gl-redbooks", ".maze3d.numGlRedbooks", XrmoptionSepArg, 0},
93 # endif
94         {"-overlay", ".maze3d.showOverlay", XrmoptionNoArg, "true"},
95         {"-speed", ".maze3d.speed", XrmoptionSepArg, 0},
96 };
97
98 static argtype vars[] = {
99         {&dropAcid, "dropAcid", "Drop Acid", DEF_DROP_ACID, t_Bool},
100         {&dropAcidWalls, "dropAcidWalls", "Drop Acid Walls", "False", t_Bool},
101         {&dropAcidFloor, "dropAcidFloor", "Drop Acid Floor", "False", t_Bool},
102         {&dropAcidCeiling, "dropAcidCeiling", "Drop Acid Ceiling", "False", t_Bool},
103         {&wallTexture, "wallTexture", "Wall Texture", "brick-wall", t_String},
104         {&floorTexture, "floorTexture", "Floor Texture", "wood-floor", t_String},
105         {&ceilingTexture, "ceilingTexture", "Ceiling Texture", "ceiling-tiles",
106                 t_String},
107         {&numRows, "numRows", "Number of Rows", DEF_NUM_ROWS, t_Int},
108         {&numColumns, "numColumns", "Number of Columns", DEF_NUM_COLUMNS, t_Int},
109         {&numInverters, "numInverters", "Number of Inverters", DEF_NUM_INVERTERS, t_Int},
110         {&numRats, "numRats", "Number of Rats", DEF_NUM_RATS, t_Int},
111 # ifdef USE_FLOATING_IMAGES
112         {&numGl3dTexts, "numGl3dTexts", "Number of GL 3D Texts", "3", t_Int},
113         {&numGlRedbooks, "numGlRedbooks", "Number of GL Redbooks", "3", t_Int},
114 # endif
115         {&shouldDrawOverlay, "showOverlay", "Show Overlay", DEF_SHOW_OVERLAY, t_Bool},
116         {&speed, "speed", "speed", DEF_SPEED, t_Float},
117 };
118
119 ENTRYPOINT ModeSpecOpt maze_opts = {countof(opts), opts, countof(vars), vars, NULL};
120
121 enum cellTypes
122 {
123         WALL, CELL_UNVISITED, CELL, START, FINISH, GL_3D_TEXT, INVERTER_TETRAHEDRON,
124         INVERTER_OCTAHEDRON, INVERTER_DODECAHEDRON, INVERTER_ICOSAHEDRON,
125         WALL_GL_REDBOOK
126 };
127
128 enum programStates
129 {
130         STARTING, WALKING, TURNING_LEFT, TURNING_RIGHT, TURNING_AROUND, INVERTING,
131         FINISHING
132 };
133
134 enum overlayLists
135 {
136         ARROW = 15, SQUARE, STAR, TRIANGLE
137 };
138
139 enum directions
140 {
141         NORTH = 0, EAST = 90, SOUTH = 180, WEST = 270
142 };
143
144 typedef struct
145 {
146         unsigned row, column;
147 } Tuple;
148
149 typedef struct
150 {
151         GLfloat x, z;
152 } Tuplef;
153
154 typedef struct
155 {
156         GLfloat red, green, blue;
157 } Color;
158
159 typedef struct
160 {
161         Tuplef position;
162         GLfloat rotation, desiredRotation, inversion, remainingDistanceToTravel;
163         unsigned char state, isCamera;
164 } Rat;
165
166
167 /* structure for holding the maze data */
168 typedef struct
169 {
170         GLXContext *glx_context;
171
172         unsigned char **mazeGrid;
173         Tuple *wallList;
174         unsigned wallListSize;
175         Tuple startPosition, finishPosition, *inverterPosition,
176         *gl3dTextPosition;
177     GLuint  wallTexture, floorTexture, ceilingTexture, startTexture,
178       finishTexture, ratTexture;
179         Rat camera;
180         Rat *rats;
181 # ifdef USE_FLOATING_IMAGES
182         GLuint gl3dTextTexture, glTextbookTexture;
183 # endif
184 # ifdef USE_FRACTAL_IMAGES
185         GLuint fractal1Texture, fractal2Texture, fractal3Texture, fractal4Texture;
186 # endif
187         Color acidColor;
188         float acidHue;
189         GLfloat wallHeight, inverterRotation;
190     Bool button_down_p;
191     int numRows, numColumns, numGlRedbooks;
192     GLuint dlists[30];  /* ARROW etc index into this */
193
194 } maze_configuration;
195
196 static maze_configuration *mazes = NULL;
197
198 static void newMaze(maze_configuration* maze);
199 static void constructLists(ModeInfo *);
200 static void initializeGrid(maze_configuration* maze);
201 static float roundToNearestHalf(float num);
202 static unsigned isOdd(unsigned num);
203 static unsigned isEven(unsigned num);
204 static void buildMaze(maze_configuration* maze);
205 static void addWallsToList(Tuple cell, maze_configuration* maze);
206 static unsigned char isRemovableWall(Tuple coordinates,
207                 maze_configuration* maze);
208 static void addCells(Tuple cellToAdd, Tuple currentWall,
209                 maze_configuration* maze);
210 static void removeWallFromList(unsigned index, maze_configuration* maze);
211 static void placeMiscObjects(maze_configuration* maze);
212 static Tuple placeObject(maze_configuration* maze, unsigned char type);
213 static void shiftAcidColor(maze_configuration* maze);
214 static void refreshRemainingDistanceToTravel(ModeInfo * mi);
215 static void step(Rat* rat, maze_configuration* maze);
216 static void walk(Rat* rat, char axis, int sign, maze_configuration* maze);
217 static void turn(Rat* rat, maze_configuration* maze);
218 static void turnAround(Rat* rat, maze_configuration* maze);
219 static void invert(maze_configuration* maze);
220 static void changeState(Rat* rat, maze_configuration* maze);
221 static void updateInverterRotation(maze_configuration* maze);
222 static void drawInverter(maze_configuration* maze, Tuple coordinates);
223 static void drawWalls(ModeInfo * mi);
224 static void drawWall(Tuple startCoordinates, Tuple endCoordinates,
225                 maze_configuration* maze);
226 static void drawCeiling(ModeInfo * mi);
227 static void drawFloor(ModeInfo * mi);
228 static void drawPane(ModeInfo *, GLuint texture, Tuple position);
229 static void drawRat(Tuplef position, maze_configuration* maze);
230 static void drawOverlay(ModeInfo *);
231
232 /* Set up and enable texturing on our object */
233 static void
234 setup_png_texture (ModeInfo *mi, const unsigned char *png_data,
235                    unsigned long data_size)
236 {
237     XImage *image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
238                                           png_data, data_size);
239         char buf[1024];
240         clear_gl_error();
241         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
242         /* iOS invalid enum:
243         glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
244         */
245         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
246                                  image->width, image->height, 0,
247                                  GL_RGBA,
248                                  /* GL_UNSIGNED_BYTE, */
249                                  GL_UNSIGNED_INT_8_8_8_8_REV,
250                                  image->data);
251         sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
252                          check_gl_error(buf);
253 }
254
255
256 static Bool
257 setup_file_texture (ModeInfo *mi, char *filename)
258 {
259         Display *dpy = mi->dpy;
260         Visual *visual = mi->xgwa.visual;
261         char buf[1024];
262
263         XImage *image = file_to_ximage (dpy, visual, filename);
264         if (!image) return False;
265
266         clear_gl_error();
267         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
268         glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
269         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
270                                  image->width, image->height, 0,
271                                  GL_RGBA,
272                                  GL_UNSIGNED_BYTE, image->data);
273         sprintf (buf, "texture: %.100s (%dx%d)",
274                          filename, image->width, image->height);
275         check_gl_error(buf);
276         return True;
277 }
278
279 static void
280 setup_textures(ModeInfo * mi)
281 {
282         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
283     GLint mag = GL_NEAREST;  /* GL_LINEAR */
284
285         glGenTextures(1, &maze->finishTexture);
286         glBindTexture(GL_TEXTURE_2D, maze->finishTexture);
287         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
288         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
289         setup_png_texture(mi, logo_32_png, sizeof(logo_32_png));
290
291         glGenTextures(1, &maze->ratTexture);
292         glBindTexture(GL_TEXTURE_2D, maze->ratTexture);
293         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
294         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
295         setup_png_texture(mi, bob_png, sizeof(bob_png));
296
297 # ifdef USE_FLOATING_IMAGES
298         glGenTextures(1, &maze->glTextbookTexture);
299         glBindTexture(GL_TEXTURE_2D, maze->glTextbookTexture);
300         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
301         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
302         setup_png_texture(mi, openglbook_png, sizeof(openglbook_png));
303
304         glGenTextures(1, &maze->gl3dTextTexture);
305         glBindTexture(GL_TEXTURE_2D, maze->gl3dTextTexture);
306         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
307         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
308         setup_png_texture(mi, opengltxt_png, sizeof(opengltxt_png));
309 # endif
310
311 # ifdef USE_FRACTAL_IMAGES
312         glGenTextures(1, &maze->fractal1Texture);
313         glBindTexture(GL_TEXTURE_2D, maze->fractal1Texture);
314         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
315         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
316         setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
317
318         glGenTextures(1, &maze->fractal2Texture);
319         glBindTexture(GL_TEXTURE_2D, maze->fractal2Texture);
320         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
321         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
322         setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
323
324         glGenTextures(1, &maze->fractal3Texture);
325         glBindTexture(GL_TEXTURE_2D, maze->fractal3Texture);
326         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
327         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
328         setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
329
330         glGenTextures(1, &maze->fractal4Texture);
331         glBindTexture(GL_TEXTURE_2D, maze->fractal4Texture);
332         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
333         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
334         setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
335 # endif
336
337         glGenTextures(1, &maze->startTexture);
338         glBindTexture(GL_TEXTURE_2D, maze->startTexture);
339         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
340         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
341         setup_png_texture(mi, start_png, sizeof(start_png));
342
343         glGenTextures(1, &maze->ceilingTexture);
344         glBindTexture(GL_TEXTURE_2D, maze->ceilingTexture);
345         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
346         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
347         if (!ceilingTexture || !*ceilingTexture
348                         || !strcmp(ceilingTexture, "ceiling-tiles")) {
349                 DEFAULT_CEILING_TEXTURE:
350                 setup_png_texture(mi, brick2_png, sizeof(brick2_png));
351         } else if (!strcmp(ceilingTexture, "brick-wall")) {
352                 setup_png_texture(mi, brick1_png, sizeof(brick1_png));
353         } else if (!strcmp(ceilingTexture, "wood-floor")) {
354                 setup_png_texture(mi, wood2_png, sizeof(wood2_png));
355 # ifdef USE_FRACTAL_IMAGES
356         } else if (!strcmp(ceilingTexture, "fractal-1")) {
357                 setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
358                 dropAcidCeiling = 1;
359         } else if (!strcmp(ceilingTexture, "fractal-2")) {
360                 setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
361                 dropAcidCeiling = 1;
362         } else if (!strcmp(ceilingTexture, "fractal-3")) {
363                 setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
364                 dropAcidCeiling = 1;
365         } else if (!strcmp(ceilingTexture, "fractal-4")) {
366                 setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
367                 dropAcidCeiling = 1;
368 # endif
369         } else {
370                 if (!setup_file_texture(mi, ceilingTexture))
371                         goto DEFAULT_CEILING_TEXTURE;
372         }
373
374         glGenTextures(1, &maze->floorTexture);
375         glBindTexture(GL_TEXTURE_2D, maze->floorTexture);
376         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
377         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
378         if (!floorTexture || !*floorTexture
379                         || !strcmp(floorTexture, "wood-floor")) {
380                 DEFAULT_FLOOR_TEXTURE:
381                 setup_png_texture(mi, wood2_png, sizeof(wood2_png));
382         } else if (!strcmp(floorTexture, "ceiling-tiles")) {
383                 setup_png_texture(mi, brick2_png, sizeof(brick2_png));
384         } else if (!strcmp(floorTexture, "brick-wall")) {
385                 setup_png_texture(mi, brick1_png, sizeof(brick1_png));
386 # ifdef USE_FRACTAL_IMAGES
387         } else if (!strcmp(floorTexture, "fractal-1")) {
388                 setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
389                 dropAcidFloor = 1;
390         } else if (!strcmp(floorTexture, "fractal-2")) {
391                 setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
392                 dropAcidFloor = 1;
393         } else if (!strcmp(floorTexture, "fractal-3")) {
394                 setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
395                 dropAcidFloor = 1;
396         } else if (!strcmp(floorTexture, "fractal-4")) {
397                 setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
398                 dropAcidFloor = 1;
399 # endif
400         } else {
401                 if (!setup_file_texture(mi, floorTexture))
402                         goto DEFAULT_FLOOR_TEXTURE;
403         }
404
405         glGenTextures(1, &maze->wallTexture);
406         glBindTexture(GL_TEXTURE_2D, maze->wallTexture);
407         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
408         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
409         if (!wallTexture || !*wallTexture || !strcmp(wallTexture, "brick-wall")) {
410                 DEFAULT_WALL_TEXTURE:
411                 setup_png_texture(mi, brick1_png, sizeof(brick1_png));
412         } else if (!strcmp(wallTexture, "ceiling-tiles")) {
413                 setup_png_texture(mi, brick2_png, sizeof(brick2_png));
414         } else if (!strcmp(wallTexture, "wood-floor")) {
415                 setup_png_texture(mi, wood2_png, sizeof(wood2_png));
416 # ifdef USE_FRACTAL_IMAGES
417         } else if (!strcmp(wallTexture, "fractal-1")) {
418                 setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
419                 dropAcidWalls = 1;
420         } else if (!strcmp(wallTexture, "fractal-2")) {
421                 setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
422                 dropAcidWalls = 1;
423         } else if (!strcmp(wallTexture, "fractal-3")) {
424                 setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
425                 dropAcidWalls = 1;
426         } else if (!strcmp(wallTexture, "fractal-4")) {
427                 setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
428                 dropAcidWalls = 1;
429 # endif
430         } else {
431                 if (!setup_file_texture(mi, wallTexture))
432                         goto DEFAULT_WALL_TEXTURE;
433         }
434 }
435
436 static void
437 initializeGrid(maze_configuration* maze)
438 {
439         unsigned i, j;
440
441         for (i = 0; i < maze->numRows; i++) {
442                 for (j = 0; j < maze->numColumns; j++) {
443                         if (isOdd(i) && isOdd(j))
444                                 maze->mazeGrid[i][j] = CELL_UNVISITED;
445                         else
446                                 maze->mazeGrid[i][j] = WALL;
447                 }
448         }
449 }
450
451 static float
452 roundToNearestHalf(float num)
453 {
454         return roundf(2.0 * num) / 2.0;
455 }
456
457 static unsigned
458 isOdd(unsigned num)
459 {
460         return num % 2;
461 }
462
463 static unsigned
464 isEven(unsigned num)
465 {
466         return !isOdd(num);
467 }
468
469 /*This is the randomized Prim's algorithm.*/
470 static void
471 buildMaze(maze_configuration* maze)
472 {
473         Tuple cellToAdd, firstCell = {1, 1};
474         maze->mazeGrid[1][1] = CELL;
475
476         addWallsToList(firstCell, maze);
477
478         while (maze->wallListSize > 0) {
479                 unsigned randomNum = random() % maze->wallListSize;
480                 Tuple currentWall = maze->wallList[randomNum];
481
482                 if (isEven(currentWall.row)) {
483                         if (maze->mazeGrid[currentWall.row - 1][currentWall.column]
484                                 == CELL
485                                 && maze->mazeGrid[currentWall.row + 1][currentWall.column]
486                                 == CELL_UNVISITED
487                         ) {
488                                 cellToAdd.row = currentWall.row + 1;
489                                 cellToAdd.column = currentWall.column;
490                                 addCells(cellToAdd, currentWall, maze);
491                         }
492                         else if (maze->mazeGrid[currentWall.row + 1][currentWall.column]
493                                 == CELL
494                                 && maze->mazeGrid[currentWall.row - 1][currentWall.column]
495                                 == CELL_UNVISITED
496                         ) {
497                                 cellToAdd.row = currentWall.row - 1;
498                                 cellToAdd.column = currentWall.column;
499                                 addCells(cellToAdd, currentWall, maze);
500                         }
501                 } else {
502                         if (maze->mazeGrid[currentWall.row][currentWall.column - 1]
503                                 == CELL
504                                 && maze->mazeGrid[currentWall.row][currentWall.column + 1]
505                                 == CELL_UNVISITED
506                         ) {
507                                 cellToAdd.row = currentWall.row;
508                                 cellToAdd.column = currentWall.column + 1;
509                                 addCells(cellToAdd, currentWall, maze);
510                         }
511                         else if (maze->mazeGrid[currentWall.row][currentWall.column + 1]
512                                 == CELL
513                                 && maze->mazeGrid[currentWall.row][currentWall.column - 1]
514                                 == CELL_UNVISITED
515                         ) {
516                                 cellToAdd.row = currentWall.row;
517                                 cellToAdd.column = currentWall.column - 1;
518                                 addCells(cellToAdd, currentWall, maze);
519                         }
520                 }
521
522                 removeWallFromList(randomNum, maze);
523         }
524 }
525
526 static void
527 addWallsToList(Tuple cell, maze_configuration* maze)
528 {
529         unsigned i;
530         Tuple walls[4];
531         walls[0].row = cell.row - 1;
532         walls[0].column = cell.column;
533         walls[1].row = cell.row + 1;
534         walls[1].column = cell.column;
535         walls[2].row = cell.row;
536         walls[2].column = cell.column - 1;
537         walls[3].row = cell.row;
538         walls[3].column = cell.column + 1;
539
540         for (i = 0; i < 4; i++) {
541                 if (isRemovableWall(walls[i], maze)) {
542                         maze->wallList[maze->wallListSize] = walls[i];
543                         maze->wallListSize++;
544                 }
545         }
546 }
547
548 static unsigned char
549 isRemovableWall(Tuple coordinates, maze_configuration* maze)
550 {
551         if (maze->mazeGrid[coordinates.row][coordinates.column] == WALL
552                 && coordinates.row > 0
553                 && coordinates.row < maze->numRows - 1
554                 && coordinates.column > 0
555                 && coordinates.column < maze->numColumns - 1
556         )
557                 return 1;
558         else
559                 return 0;
560 }
561
562 static void
563 addCells(Tuple cellToAdd, Tuple currentWall, maze_configuration* maze)
564 {
565         maze->mazeGrid[currentWall.row][currentWall.column] = CELL;
566         maze->mazeGrid[cellToAdd.row][cellToAdd.column] = CELL;
567         addWallsToList(cellToAdd, maze);
568 }
569
570 static void
571 removeWallFromList(unsigned index, maze_configuration* maze)
572 {
573         unsigned i;
574         for (i = index + 1; i < maze->wallListSize; i++)
575                 maze->wallList[i - 1] = maze->wallList[i];
576
577         maze->wallListSize--;
578 }
579
580 static void
581 placeMiscObjects(maze_configuration* maze)
582 {
583         Rat* rat;
584         Tuple temp;
585         unsigned char object;
586     unsigned numSurroundingWalls = 3;
587         unsigned i;
588
589         while (numSurroundingWalls >= 3) {
590                 numSurroundingWalls = 0;
591                 maze->startPosition = placeObject(maze, CELL);
592
593                 object = maze->mazeGrid[maze->startPosition.row]
594                         [maze->startPosition.column + 1];
595                 if (object == WALL || object == WALL_GL_REDBOOK)
596                         numSurroundingWalls++;
597                 object = maze->mazeGrid[maze->startPosition.row - 1]
598                         [maze->startPosition.column];
599                 if (object == WALL || object == WALL_GL_REDBOOK)
600                         numSurroundingWalls++;
601                 object = maze->mazeGrid[maze->startPosition.row]
602                         [maze->startPosition.column - 1];
603                 if (object == WALL || object == WALL_GL_REDBOOK)
604                         numSurroundingWalls++;
605                 object = maze->mazeGrid[maze->startPosition.row + 1]
606                         [maze->startPosition.column];
607                 if (object == WALL || object == WALL_GL_REDBOOK)
608                         numSurroundingWalls++;
609         }
610         maze->mazeGrid[maze->startPosition.row][maze->startPosition.column] = START;
611
612         if (maze->mazeGrid[maze->startPosition.row][maze->startPosition.column + 1]
613                         != WALL && maze->mazeGrid[maze->startPosition.row]
614                         [maze->startPosition.column + 1] != WALL_GL_REDBOOK) {
615                 maze->camera.position.x = (maze->startPosition.column + 1) / 2.0;
616                 maze->camera.position.z = maze->startPosition.row / 2.0;
617                 maze->camera.rotation = WEST;
618         }
619         else if (maze->mazeGrid[maze->startPosition.row - 1]
620                         [maze->startPosition.column] != WALL
621                         && maze->mazeGrid[maze->startPosition.row - 1]
622                         [maze->startPosition.column] != WALL_GL_REDBOOK) {
623                 maze->camera.position.x = maze->startPosition.column / 2.0;
624                 maze->camera.position.z = (maze->startPosition.row - 1) / 2.0;
625                 maze->camera.rotation = SOUTH;
626         }
627         else if (maze->mazeGrid[maze->startPosition.row]
628                         [maze->startPosition.column - 1] != WALL
629                         && maze->mazeGrid[maze->startPosition.row]
630                         [maze->startPosition.column - 1] != WALL_GL_REDBOOK) {
631                 maze->camera.position.x = (maze->startPosition.column - 1) / 2.0;
632                 maze->camera.position.z = maze->startPosition.row / 2.0;
633                 maze->camera.rotation = EAST;
634         }
635         else {
636                 maze->camera.position.x = maze->startPosition.column / 2.0;
637                 maze->camera.position.z = (maze->startPosition.row + 1) / 2.0;
638                 maze->camera.rotation = NORTH;
639         }
640
641         maze->finishPosition = placeObject(maze, FINISH);
642
643         for (i = 0; i < numInverters; i++)
644                 maze->inverterPosition[i] =
645                         placeObject(maze, random() % 4 + INVERTER_TETRAHEDRON);
646
647         temp.row = 0;
648         temp.column = 0;
649
650 # ifdef USE_FLOATING_IMAGES
651         for (i = 0; i < numGl3dTexts; i++)
652                 maze->gl3dTextPosition[i] =
653                         placeObject(maze, GL_3D_TEXT);
654 # endif
655
656         for (i = 0; i < numRats; i++) {
657                 rat = &(maze->rats[i]);
658                 temp = placeObject(maze, CELL);
659                 rat->position.x = temp.column / 2.0;
660                 rat->position.z = temp.row / 2.0;
661                 rat->state = WALKING;
662
663                 if (temp.row == 0 && temp.column == 0) {
664                         continue;
665                 }
666
667                 if (maze->mazeGrid[(int)(rat->position.z * 2)]
668                                 [(int)(rat->position.x * 2) + 1]
669                                 != WALL && maze->mazeGrid[(int)(rat->position.z * 2)]
670                                 [(int)(rat->position.x * 2) + 1] != WALL_GL_REDBOOK)
671                         rat->rotation = EAST;
672                 else if (maze->mazeGrid[(int)(rat->position.z * 2) - 1]
673                                 [(int)(rat->position.x * 2)]
674                                 != WALL && maze->mazeGrid[(int)(rat->position.z * 2) - 1]
675                                 [(int)(rat->position.x * 2)] != WALL_GL_REDBOOK)
676                         rat->rotation = NORTH;
677                 else if (maze->mazeGrid[(int)(rat->position.z * 2)]
678                                 [(int)(rat->position.x * 2) - 1]
679                                 != WALL && maze->mazeGrid[(int)(rat->position.z * 2)]
680                                 [(int)(rat->position.x * 2) - 1] != WALL_GL_REDBOOK)
681                         rat->rotation = WEST;
682                 else
683                         rat->rotation = SOUTH;
684         }
685
686 # ifdef USE_FLOATING_IMAGES
687         for (i = 0; i < numGlRedbooks; i++) {
688                 while (!(((isOdd(temp.row) && isEven(temp.column))
689                                         || (isEven(temp.row) && isOdd(temp.column)))
690                                         && maze->mazeGrid[temp.row][temp.column] == WALL)) {
691                         temp.row = random() % maze->numRows;
692                         temp.column = random() % maze->numColumns;
693                 }
694
695                 maze->mazeGrid[temp.row][temp.column] = WALL_GL_REDBOOK;
696         }
697 # endif
698 }
699
700 static Tuple
701 placeObject(maze_configuration* maze, unsigned char type)
702 {
703         Tuple position = {0, 0};
704
705         while (!(maze->mazeGrid[position.row][position.column] == CELL
706                         && isOdd(position.row) && isOdd(position.column))) {
707                 position.row = random() % maze->numRows;
708                 position.column = random() % maze->numColumns;
709         }
710
711         maze->mazeGrid[position.row][position.column] = type;
712         return position;
713 }
714
715 ENTRYPOINT void
716 reshape_maze (ModeInfo *mi, int width, int height)
717 {
718         glViewport(0, 0, (GLint) width, (GLint) height);
719         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
720 }
721
722
723 ENTRYPOINT Bool
724 maze_handle_event (ModeInfo *mi, XEvent *event)
725 {
726   maze_configuration *maze = &mazes[MI_SCREEN(mi)];
727   if (event->xany.type == ButtonPress)
728     {
729       maze->button_down_p = True;
730       return True;
731     }
732   else if (event->xany.type == ButtonRelease)
733     {
734       maze->button_down_p = False;
735       return True;
736     }
737   return False;
738 }
739
740 ENTRYPOINT void
741 init_maze (ModeInfo * mi)
742 {
743         unsigned i;
744         maze_configuration *maze;
745         GLfloat ambient[] = {0, 0, 0, 1},
746                         diffuse[] = {1, 1, 1, 1},
747                         position[] = {0, 2, 0, 0},
748                         mcolor[] = {1, 1, 1, 1};
749
750         MI_INIT(mi, mazes);
751         maze = &mazes[MI_SCREEN(mi)];
752
753         maze->glx_context = init_GL(mi);
754
755         reshape_maze(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
756
757     for (i = 0; i < countof(maze->dlists); i++)
758       maze->dlists[i] = glGenLists (1);
759
760         maze->numRows = (numRows < 2 ? 5 : numRows * 2 + 1);
761         maze->numColumns = (numColumns < 2 ? 5 : numColumns * 2 + 1);
762
763         i = (maze->numRows / 2) * (maze->numColumns / 2) - 2;
764         if (i < numInverters) {
765                 numInverters = i;
766                 i = 0;
767         } else i -= numInverters;
768
769         if (i < numRats) {
770                 numRats = i;
771                 i = 0;
772         } else i -= numRats;
773 # ifdef USE_FLOATING_IMAGES
774         if (i < numGl3dTexts) {
775                 numGl3dTexts = i;
776                 i = 0;
777         } else i -= numGl3dTexts;
778
779         if (((maze->numRows - 1) + (maze->numColumns - 1)
780                                 + ((maze->numRows / 2 - 1) * (maze->numColumns / 2 - 1))) < maze->numGlRedbooks)
781                 maze->numGlRedbooks = (maze->numRows - 1) + (maze->numColumns - 1)
782                                 + ((maze->numRows / 2 - 1) * (maze->numColumns / 2 - 1));
783 # endif
784
785         glEnable(GL_DEPTH_TEST);
786         glEnable(GL_TEXTURE_2D);
787         glEnable(GL_LIGHT0);
788
789         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
790         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
791         glLightfv(GL_LIGHT0, GL_POSITION, position);
792         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mcolor);
793
794         glShadeModel(GL_FLAT);
795
796         maze->mazeGrid = calloc(maze->numRows, sizeof(unsigned char*));
797         for (i = 0; i < maze->numRows; i++)
798                 maze->mazeGrid[i] = calloc(maze->numColumns, sizeof(unsigned char));
799         maze->wallList = calloc(((maze->numColumns - 2) / 2) * ((maze->numRows - 2) / 2 + 1)
800                         + ((maze->numColumns - 2) / 2 + 1) * ((maze->numRows - 2) / 2), sizeof(Tuple));
801         maze->inverterPosition = calloc(numInverters, sizeof(Tuple));
802         maze->rats = calloc(numRats, sizeof(Rat));
803 # ifdef USE_FLOATING_IMAGES
804         maze->gl3dTextPosition = calloc(numGl3dTexts, sizeof(Tuple));
805 #endif
806
807         setup_textures(mi);
808
809         newMaze(maze);
810
811         constructLists(mi);
812         refreshRemainingDistanceToTravel(mi);
813
814         maze->camera.isCamera = 1;
815         for (i = 0; i < numRats; i++)
816                 maze->rats[i].isCamera = 0;
817 }
818
819 static void
820 newMaze(maze_configuration* maze)
821 {
822         maze->camera.state = STARTING;
823         maze->camera.inversion = 0;
824         maze->wallHeight = 0;
825         maze->inverterRotation = 0;
826         maze->acidHue = 0;
827
828         initializeGrid(maze);
829         buildMaze(maze);
830         placeMiscObjects(maze);
831 }
832
833 static void
834 constructLists(ModeInfo *mi)
835 {
836         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
837
838         glNewList(maze->dlists[ARROW], GL_COMPILE);
839                 glBegin(GL_POLYGON);
840                 glVertex2f(0, -0.25);
841                 glVertex2f(0.146946313, 0.202254249);
842                 glVertex2f(0, 0.125);
843                 glVertex2f(-0.146946313, 0.202254249);
844                 glEnd();
845         glEndList();
846
847         glNewList(maze->dlists[SQUARE], GL_COMPILE);
848                 glBegin(GL_QUADS);
849                 glVertex2f(-0.176776695, -0.176776695);
850                 glVertex2f(0.176776695, -0.176776695);
851                 glVertex2f(0.176776695, 0.176776695);
852                 glVertex2f(-0.176776695, 0.176776695);
853                 glEnd();
854         glEndList();
855
856         glNewList(maze->dlists[STAR], GL_COMPILE);
857                 glBegin(GL_TRIANGLE_FAN);
858                 glVertex2f(0, 0);
859                 glVertex2f(0, -0.25);
860                 glVertex2f(0.073473157, -0.101127124);
861                 glVertex2f(0.237764129, -0.077254249);
862                 glVertex2f(0.118882065, 0.038627124);
863                 glVertex2f(0.146946313, 0.202254249);
864                 glVertex2f(0, 0.125);
865                 glVertex2f(-0.146946313, 0.202254249);
866                 glVertex2f(-0.118882065, 0.038627124);
867                 glVertex2f(-0.237764129, -0.077254249);
868                 glVertex2f(-0.073473157, -0.101127124);
869                 glVertex2f(0, -0.25);
870                 glEnd();
871         glEndList();
872
873         glNewList(maze->dlists[TRIANGLE], GL_COMPILE);
874                 glBegin(GL_POLYGON);
875                 glVertex2f(0, -0.25);
876                 glVertex2f(0.216506351, 0.125);
877                 glVertex2f(-0.216506351, 0.125);
878                 glEnd();
879         glEndList();
880
881         glNewList(maze->dlists[INVERTER_TETRAHEDRON], GL_COMPILE);
882                 glBegin(GL_TRIANGLES);
883                 glNormal3f(0.471404521, 0.816496581, 0.333333333);
884                 glVertex3f(0, 0, 0.25);
885                 glVertex3f(0.23570226, 0, -0.083333333);
886                 glVertex3f(-0.11785113, 0.204124145, -0.083333333);
887
888                 glNormal3f(-0.942809042, 0, 0.333333333);
889                 glVertex3f(0, 0, 0.25);
890                 glVertex3f(-0.11785113, 0.204124145, -0.083333333);
891                 glVertex3f(-0.11785113, -0.204124145, -0.083333333);
892
893                 glNormal3f(0.471404521, -0.816496581, 0.333333333);
894                 glVertex3f(0, 0, 0.25);
895                 glVertex3f(-0.11785113, -0.204124145, -0.083333333);
896                 glVertex3f(0.23570226, 0, -0.083333333);
897
898                 glNormal3f(0, 0, -1);
899                 glVertex3f(0.23570226, 0, -0.083333333);
900                 glVertex3f(-0.11785113, -0.204124145, -0.083333333);
901                 glVertex3f(-0.11785113, 0.204124145, -0.083333333);
902                 glEnd();
903         glEndList();
904
905         glNewList(maze->dlists[INVERTER_OCTAHEDRON], GL_COMPILE);
906                 glBegin(GL_TRIANGLES);
907                 glNormal3f(0.577350269, 0.577350269, 0.577350269);
908                 glVertex3f(0, 0, 0.25);
909                 glVertex3f(0.25, 0, 0);
910                 glVertex3f(0, 0.25, 0);
911
912                 glNormal3f(-0.577350269, 0.577350269, 0.577350269);
913                 glVertex3f(0, 0, 0.25);
914                 glVertex3f(0, 0.25, 0);
915                 glVertex3f(-0.25, 0, 0);
916
917                 glNormal3f(-0.577350269, -0.577350269, 0.577350269);
918                 glVertex3f(0, 0, 0.25);
919                 glVertex3f(-0.25, 0, 0);
920                 glVertex3f(0, -0.25, 0);
921
922                 glNormal3f(0.577350269, -0.577350269, 0.577350269);
923                 glVertex3f(0, 0, 0.25);
924                 glVertex3f(0, -0.25, 0);
925                 glVertex3f(0.25, 0, 0);
926
927                 glNormal3f(0.577350269, -0.577350269, -0.577350269);
928                 glVertex3f(0.25, 0, 0);
929                 glVertex3f(0, -0.25, 0);
930                 glVertex3f(0, 0, -0.25);
931
932                 glNormal3f(0.577350269, 0.577350269, -0.577350269);
933                 glVertex3f(0.25, 0, 0);
934                 glVertex3f(0, 0, -0.25);
935                 glVertex3f(0, 0.25, 0);
936
937                 glNormal3f(-0.577350269, 0.577350269, -0.577350269);
938                 glVertex3f(0, 0.25, 0);
939                 glVertex3f(0, 0, -0.25);
940                 glVertex3f(-0.25, 0, 0);
941
942                 glNormal3f(-0.577350269, -0.577350269, -0.577350269);
943                 glVertex3f(-0.25, 0, 0);
944                 glVertex3f(0, 0, -0.25);
945                 glVertex3f(0, -0.25, 0);
946                 glEnd();
947         glEndList();
948
949         glNewList(maze->dlists[INVERTER_DODECAHEDRON], GL_COMPILE);
950                 glBegin(GL_POLYGON);
951                 glNormal3f(0.000000000, 0.000000000, 1.000000000);
952                 glVertex3f(0.122780868, 0.089205522, 0.198663618);
953                 glVertex3f(-0.046898119, 0.144337567, 0.198663618);
954                 glVertex3f(-0.151765500, 0.000000000, 0.198663618);
955                 glVertex3f(-0.046898119, -0.144337567, 0.198663618);
956                 glVertex3f(0.122780868, -0.089205522, 0.198663618);
957                 glEnd();
958
959                 glBegin(GL_POLYGON);
960                 glNormal3f(0.894427191, 0.000000000, 0.447213595);
961                 glVertex3f(0.198663618, -0.144337567, 0.046898119);
962                 glVertex3f(0.245561737, 0.000000000, -0.046898119);
963                 glVertex3f(0.198663618, 0.144337567, 0.046898119);
964                 glVertex3f(0.122780868, 0.089205522, 0.198663618);
965                 glVertex3f(0.122780868, -0.089205522, 0.198663618);
966                 glEnd();
967
968                 glBegin(GL_POLYGON);
969                 glNormal3f(0.276393202, 0.850650808, 0.447213595);
970                 glVertex3f(0.198663618, 0.144337567, 0.046898119);
971                 glVertex3f(0.075882750, 0.233543090, -0.046898119);
972                 glVertex3f(-0.075882750, 0.233543090, 0.046898119);
973                 glVertex3f(-0.046898119, 0.144337567, 0.198663618);
974                 glVertex3f(0.122780868, 0.089205522, 0.198663618);
975                 glEnd();
976
977                 glBegin(GL_POLYGON);
978                 glNormal3f(-0.723606798, 0.525731112, 0.447213595);
979                 glVertex3f(-0.075882750, 0.233543090, 0.046898119);
980                 glVertex3f(-0.198663618, 0.144337567, -0.046898119);
981                 glVertex3f(-0.245561737, 0.000000000, 0.046898119);
982                 glVertex3f(-0.151765500, 0.000000000, 0.198663618);
983                 glVertex3f(-0.046898119, 0.144337567, 0.198663618);
984                 glEnd();
985
986                 glBegin(GL_POLYGON);
987                 glNormal3f(-0.723606798, -0.525731112, 0.447213595);
988                 glVertex3f(-0.245561737, 0.000000000, 0.046898119);
989                 glVertex3f(-0.198663618, -0.144337567, -0.046898119);
990                 glVertex3f(-0.075882750, -0.233543090, 0.046898119);
991                 glVertex3f(-0.046898119, -0.144337567, 0.198663618);
992                 glVertex3f(-0.151765500, 0.000000000, 0.198663618);
993                 glEnd();
994
995                 glBegin(GL_POLYGON);
996                 glNormal3f(0.276393202, -0.850650808, 0.447213595);
997                 glVertex3f(-0.075882750, -0.233543090, 0.046898119);
998                 glVertex3f(0.075882750, -0.233543090, -0.046898119);
999                 glVertex3f(0.198663618, -0.144337567, 0.046898119);
1000                 glVertex3f(0.122780868, -0.089205522, 0.198663618);
1001                 glVertex3f(-0.046898119, -0.144337567, 0.198663618);
1002                 glEnd();
1003
1004                 glBegin(GL_POLYGON);
1005                 glNormal3f(0.723606798, 0.525731112, -0.447213595);
1006                 glVertex3f(0.245561737, 0.000000000, -0.046898119);
1007                 glVertex3f(0.151765500, 0.000000000, -0.198663618);
1008                 glVertex3f(0.046898119, 0.144337567, -0.198663618);
1009                 glVertex3f(0.075882750, 0.233543090, -0.046898119);
1010                 glVertex3f(0.198663618, 0.144337567, 0.046898119);
1011                 glEnd();
1012
1013                 glBegin(GL_POLYGON);
1014                 glNormal3f(0.723606798, -0.525731112, -0.447213595);
1015                 glVertex3f(0.198663618, -0.144337567, 0.046898119);
1016                 glVertex3f(0.075882750, -0.233543090, -0.046898119);
1017                 glVertex3f(0.046898119, -0.144337567, -0.198663618);
1018                 glVertex3f(0.151765500, 0.000000000, -0.198663618);
1019                 glVertex3f(0.245561737, 0.000000000, -0.046898119);
1020                 glEnd();
1021
1022                 glBegin(GL_POLYGON);
1023                 glNormal3f(-0.276393202, 0.850650808, -0.447213595);
1024                 glVertex3f(0.075882750, 0.233543090, -0.046898119);
1025                 glVertex3f(0.046898119, 0.144337567, -0.198663618);
1026                 glVertex3f(-0.122780868, 0.089205522, -0.198663618);
1027                 glVertex3f(-0.198663618, 0.144337567, -0.046898119);
1028                 glVertex3f(-0.075882750, 0.233543090, 0.046898119);
1029                 glEnd();
1030
1031                 glBegin(GL_POLYGON);
1032                 glNormal3f(-0.894427191, 0.000000000, -0.447213595);
1033                 glVertex3f(-0.198663618, 0.144337567, -0.046898119);
1034                 glVertex3f(-0.122780868, 0.089205522, -0.198663618);
1035                 glVertex3f(-0.122780868, -0.089205522, -0.198663618);
1036                 glVertex3f(-0.198663618, -0.144337567, -0.046898119);
1037                 glVertex3f(-0.245561737, 0.000000000, 0.046898119);
1038                 glEnd();
1039
1040                 glBegin(GL_POLYGON);
1041                 glNormal3f(-0.276393202, -0.850650808, -0.447213595);
1042                 glVertex3f(-0.198663618, -0.144337567, -0.046898119);
1043                 glVertex3f(-0.122780868, -0.089205522, -0.198663618);
1044                 glVertex3f(0.046898119, -0.144337567, -0.198663618);
1045                 glVertex3f(0.075882750, -0.233543090, -0.046898119);
1046                 glVertex3f(-0.075882750, -0.233543090, 0.046898119);
1047                 glEnd();
1048
1049                 glBegin(GL_POLYGON);
1050                 glNormal3f(0.000000000, 0.000000000, -1.000000000);
1051                 glVertex3f(0.046898119, -0.144337567, -0.198663618);
1052                 glVertex3f(-0.122780868, -0.089205522, -0.198663618);
1053                 glVertex3f(-0.122780868, 0.089205522, -0.198663618);
1054                 glVertex3f(0.046898119, 0.144337567, -0.198663618);
1055                 glVertex3f(0.151765500, 0.000000000, -0.198663618);
1056                 glEnd();
1057         glEndList();
1058
1059         glNewList(maze->dlists[INVERTER_ICOSAHEDRON], GL_COMPILE);
1060                 glBegin(GL_TRIANGLES);
1061                 glNormal3f(0.491123473, 0.356822090, 0.794654473);
1062                 glVertex3f(0.000000000, 0.000000000, 0.250000000);
1063                 glVertex3f(0.223606798, 0.000000000, 0.111803399);
1064                 glVertex3f(0.069098301, 0.212662702, 0.111803399);
1065
1066                 glNormal3f(-0.187592474, 0.577350269, 0.794654473);
1067                 glVertex3f(0.000000000, 0.000000000, 0.250000000);
1068                 glVertex3f(0.069098301, 0.212662702, 0.111803399);
1069                 glVertex3f(-0.180901699, 0.131432778, 0.111803399);
1070
1071                 glNormal3f(-0.607061998, 0.000000000, 0.794654473);
1072                 glVertex3f(0.000000000, 0.000000000, 0.250000000);
1073                 glVertex3f(-0.180901699, 0.131432778, 0.111803399);
1074                 glVertex3f(-0.180901699, -0.131432778, 0.111803399);
1075
1076                 glNormal3f(-0.187592474, -0.577350269, 0.794654473);
1077                 glVertex3f(0.000000000, 0.000000000, 0.250000000);
1078                 glVertex3f(-0.180901699, -0.131432778, 0.111803399);
1079                 glVertex3f(0.069098301, -0.212662702, 0.111803399);
1080
1081                 glNormal3f(0.491123473, -0.356822090, 0.794654473);
1082                 glVertex3f(0.000000000, 0.000000000, 0.250000000);
1083                 glVertex3f(0.069098301, -0.212662702, 0.111803399);
1084                 glVertex3f(0.223606798, 0.000000000, 0.111803399);
1085
1086                 glNormal3f(0.794654473, -0.577350269, 0.187592474);
1087                 glVertex3f(0.223606798, 0.000000000, 0.111803399);
1088                 glVertex3f(0.069098301, -0.212662702, 0.111803399);
1089                 glVertex3f(0.180901699, -0.131432778, -0.111803399);
1090
1091                 glNormal3f(0.982246947, 0.000000000, -0.187592474);
1092                 glVertex3f(0.223606798, 0.000000000, 0.111803399);
1093                 glVertex3f(0.180901699, -0.131432778, -0.111803399);
1094                 glVertex3f(0.180901699, 0.131432778, -0.111803399);
1095
1096                 glNormal3f(0.794654473, 0.577350269, 0.187592474);
1097                 glVertex3f(0.223606798, 0.000000000, 0.111803399);
1098                 glVertex3f(0.180901699, 0.131432778, -0.111803399);
1099                 glVertex3f(0.069098301, 0.212662702, 0.111803399);
1100
1101                 glNormal3f(0.303530999, 0.934172359, -0.187592474);
1102                 glVertex3f(0.069098301, 0.212662702, 0.111803399);
1103                 glVertex3f(0.180901699, 0.131432778, -0.111803399);
1104                 glVertex3f(-0.069098301, 0.212662702, -0.111803399);
1105
1106                 glNormal3f(-0.303530999, 0.934172359, 0.187592474);
1107                 glVertex3f(0.069098301, 0.212662702, 0.111803399);
1108                 glVertex3f(-0.069098301, 0.212662702, -0.111803399);
1109                 glVertex3f(-0.180901699, 0.131432778, 0.111803399);
1110
1111                 glNormal3f(-0.794654473, 0.577350269, -0.187592474);
1112                 glVertex3f(-0.180901699, 0.131432778, 0.111803399);
1113                 glVertex3f(-0.069098301, 0.212662702, -0.111803399);
1114                 glVertex3f(-0.223606798, 0.000000000, -0.111803399);
1115
1116                 glNormal3f(-0.982246947, 0.000000000, 0.187592474);
1117                 glVertex3f(-0.180901699, 0.131432778, 0.111803399);
1118                 glVertex3f(-0.223606798, 0.000000000, -0.111803399);
1119                 glVertex3f(-0.180901699, -0.131432778, 0.111803399);
1120
1121                 glNormal3f(-0.794654473, -0.577350269, -0.187592474);
1122                 glVertex3f(-0.180901699, -0.131432778, 0.111803399);
1123                 glVertex3f(-0.223606798, 0.000000000, -0.111803399);
1124                 glVertex3f(-0.069098301, -0.212662702, -0.111803399);
1125
1126                 glNormal3f(-0.303530999, -0.934172359, 0.187592474);
1127                 glVertex3f(-0.180901699, -0.131432778, 0.111803399);
1128                 glVertex3f(-0.069098301, -0.212662702, -0.111803399);
1129                 glVertex3f(0.069098301, -0.212662702, 0.111803399);
1130
1131                 glNormal3f(0.303530999, -0.934172359, -0.187592474);
1132                 glVertex3f(0.069098301, -0.212662702, 0.111803399);
1133                 glVertex3f(-0.069098301, -0.212662702, -0.111803399);
1134                 glVertex3f(0.180901699, -0.131432778, -0.111803399);
1135
1136                 glNormal3f(0.607061998, 0.000000000, -0.794654473);
1137                 glVertex3f(0.180901699, 0.131432778, -0.111803399);
1138                 glVertex3f(0.180901699, -0.131432778, -0.111803399);
1139                 glVertex3f(0.000000000, 0.000000000, -0.250000000);
1140
1141                 glNormal3f(0.187592474, 0.577350269, -0.794654473);
1142                 glVertex3f(0.180901699, 0.131432778, -0.111803399);
1143                 glVertex3f(0.000000000, 0.000000000, -0.250000000);
1144                 glVertex3f(-0.069098301, 0.212662702, -0.111803399);
1145
1146                 glNormal3f(0.187592474, -0.577350269, -0.794654473);
1147                 glVertex3f(0.180901699, -0.131432778, -0.111803399);
1148                 glVertex3f(-0.069098301, -0.212662702, -0.111803399);
1149                 glVertex3f(0.000000000, 0.000000000, -0.250000000);
1150
1151                 glNormal3f(-0.491123473, 0.356822090, -0.794654473);
1152                 glVertex3f(-0.069098301, 0.212662702, -0.111803399);
1153                 glVertex3f(0.000000000, 0.000000000, -0.250000000);
1154                 glVertex3f(-0.223606798, 0.000000000, -0.111803399);
1155
1156                 glNormal3f(-0.491123473, -0.356822090, -0.794654473);
1157                 glVertex3f(-0.223606798, 0.000000000, -0.111803399);
1158                 glVertex3f(0.000000000, 0.000000000, -0.250000000);
1159                 glVertex3f(-0.069098301, -0.212662702, -0.111803399);
1160                 glEnd();
1161         glEndList();
1162 }
1163
1164 ENTRYPOINT void
1165 draw_maze (ModeInfo * mi)
1166 {
1167         unsigned i;
1168         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1169         GLfloat h = (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi);
1170
1171     if (!maze->glx_context)
1172       return;
1173         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(maze->glx_context));
1174
1175         glMatrixMode(GL_PROJECTION);
1176     glLoadIdentity();
1177         gluPerspective(90, 1/h, 0.05, 100);
1178
1179     if (MI_WIDTH(mi) > 2560)  /* Retina displays */
1180       glLineWidth (6);
1181     else
1182 # ifdef HAVE_MOBILE
1183       glLineWidth (4);
1184 # else
1185       glLineWidth (2);
1186 # endif
1187
1188         glRotatef(maze->camera.inversion, 0, 0, 1);
1189         glRotatef(maze->camera.rotation, 0, 1, 0);
1190         glTranslatef(-1 * maze->camera.position.x, -0.5,
1191                         -1 * maze->camera.position.z);
1192
1193         refreshRemainingDistanceToTravel(mi);
1194
1195         updateInverterRotation(maze);
1196         shiftAcidColor(maze);
1197         step(&maze->camera, maze);
1198
1199         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1200         drawWalls(mi);
1201         drawCeiling(mi);
1202         drawFloor(mi);
1203
1204         for (i = 0; i < numInverters; i++)
1205                 drawInverter(maze, maze->inverterPosition[i]);
1206
1207 # ifdef USE_FLOATING_IMAGES
1208         for (i = 0; i < numGl3dTexts; i++)
1209                 drawPane(mi, maze->gl3dTextTexture, maze->gl3dTextPosition[i]);
1210 # endif
1211
1212         for (i = 0; i < numRats; i++) {
1213                 step(&maze->rats[i], maze);
1214                 drawRat(maze->rats[i].position, maze);
1215         }
1216
1217         drawPane(mi, maze->finishTexture, maze->finishPosition);
1218         drawPane(mi, maze->startTexture, maze->startPosition);
1219
1220         if (shouldDrawOverlay || maze->button_down_p)
1221       drawOverlay(mi);
1222
1223         if (mi->fps_p) do_fps(mi);
1224     glFinish();
1225         glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1226 }
1227
1228 static void
1229 shiftAcidColor(maze_configuration* maze)
1230 {
1231         GLfloat x = 1 - fabs(fmod(maze->acidHue / 60.0, 2) - 1);
1232
1233         if (0 <= maze->acidHue && maze->acidHue <= 60) {
1234                 maze->acidColor.red = 1;
1235                 maze->acidColor.green = x;
1236                 maze->acidColor.blue = 0;
1237         } else if (60 <= maze->acidHue && maze->acidHue <= 120) {
1238                 maze->acidColor.red = x;
1239                 maze->acidColor.green = 1;
1240                 maze->acidColor.blue = 0;
1241         } else if (120 <= maze->acidHue && maze->acidHue <= 180) {
1242                 maze->acidColor.red = 0;
1243                 maze->acidColor.green = 1;
1244                 maze->acidColor.blue = x;
1245         } else if (180 <= maze->acidHue && maze->acidHue <= 240) {
1246                 maze->acidColor.red = 0;
1247                 maze->acidColor.green = x;
1248                 maze->acidColor.blue = 1;
1249         } else if (240 <= maze->acidHue && maze->acidHue <= 300) {
1250                 maze->acidColor.red = x;
1251                 maze->acidColor.green = 0;
1252                 maze->acidColor.blue = 1;
1253         } else {
1254                 maze->acidColor.red = 1;
1255                 maze->acidColor.green = 0;
1256                 maze->acidColor.blue = x;
1257         }
1258
1259         maze->acidHue += 75 * maze->camera.remainingDistanceToTravel;
1260         if (maze->acidHue >= 360) maze->acidHue -= 360;
1261 }
1262
1263 static void
1264 refreshRemainingDistanceToTravel(ModeInfo * mi)
1265 {
1266         unsigned i;
1267         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1268         maze->camera.remainingDistanceToTravel
1269                 = speed * 1.6 * (MI_DELAY(mi) / 1000000.0);
1270         for (i = 0; i < numRats; i++)
1271                 maze->rats[i].remainingDistanceToTravel =
1272                         maze->camera.remainingDistanceToTravel;
1273 }
1274
1275 static void
1276 step(Rat* rat, maze_configuration* maze)
1277 {
1278         GLfloat previousWallHeight = maze->wallHeight;
1279
1280         if (!rat->isCamera && (maze->wallHeight < 1
1281                                 || (rat->position.x == 0 && rat->position.z == 0)))
1282                 return;
1283
1284         while (rat->remainingDistanceToTravel > 0) {
1285                 switch(rat->state) {
1286                         case WALKING:
1287                                 switch((int)rat->rotation) {
1288                                         case NORTH:
1289                                                 walk(rat, 'z', -1, maze);
1290                                                 break;
1291                                         case EAST:
1292                                                 walk(rat, 'x', 1, maze);
1293                                                 break;
1294                                         case SOUTH:
1295                                                 walk(rat, 'z', 1, maze);
1296                                                 break;
1297                                         case WEST:
1298                                                 walk(rat, 'x', -1, maze);
1299                                                 break;
1300                                         default:
1301                                                 rat->rotation = 90 * roundf(rat->rotation / 90.0);
1302                                                 break;
1303                                 }
1304                                 break;
1305                         case TURNING_LEFT:
1306                                 turn(rat, maze);
1307                                 break;
1308                         case TURNING_RIGHT:
1309                                 turn(rat, maze);
1310                                 break;
1311                         case TURNING_AROUND:
1312                                 turnAround(rat, maze);
1313                                 break;
1314                         case INVERTING:
1315                                 invert(maze);
1316                                 break;
1317                         case STARTING:
1318                                 maze->wallHeight += 0.48 * rat->remainingDistanceToTravel;
1319                                 if (maze->wallHeight > 1.0) {
1320                                         maze->wallHeight = 1.0;
1321                                         rat->remainingDistanceToTravel =
1322                                                 fabs(previousWallHeight - maze->wallHeight);
1323                                         changeState(&maze->camera, maze);
1324                                 } else
1325                                         rat->remainingDistanceToTravel = 0;
1326                                 break;
1327                         case FINISHING:
1328                                 if (maze->wallHeight == 0) {
1329                                         newMaze(maze);
1330                                         rat->remainingDistanceToTravel = 0;
1331                                 }
1332                                 else if (maze->wallHeight < 0) {
1333                                         maze->wallHeight = 0;
1334                                         rat->remainingDistanceToTravel =
1335                                                 fabs(previousWallHeight - maze->wallHeight);
1336                                 }
1337                                 else {
1338                                         maze->wallHeight -= 0.48 * rat->remainingDistanceToTravel;
1339                                         rat->remainingDistanceToTravel = 0;
1340                                 }
1341                                 break;
1342                         default:
1343                                 break;
1344                 }
1345         }
1346 }
1347
1348 static void
1349 walk(Rat* rat, char axis, int sign, maze_configuration* maze)
1350 {
1351         GLfloat* component = (axis == 'x' ? &rat->position.x : &rat->position.z);
1352         GLfloat previousPosition = *component;
1353         int isMultipleOfOneHalf = 0;
1354         unsigned temp = (unsigned)((*component) * 2.0);
1355
1356         if (((*component) * 2) == roundf((*component) * 2))
1357                 isMultipleOfOneHalf = 1;
1358         *component += sign * rat->remainingDistanceToTravel;
1359
1360         if (!isMultipleOfOneHalf && ((unsigned)((*component) * 2.0)) != temp) {
1361                 *component = roundToNearestHalf(*component);
1362                 rat->remainingDistanceToTravel -=
1363                         fabs((*component) - previousPosition);
1364                 changeState(rat, maze);
1365         } else
1366                 rat->remainingDistanceToTravel = 0;
1367         
1368 }
1369
1370 static void
1371 turn(Rat* rat, maze_configuration* maze)
1372 {
1373         Tuplef rotatingAround;
1374         GLfloat tangentVectorDirection, previousRotation
1375                 = rat->rotation;
1376
1377         if (rat->state == TURNING_LEFT) {
1378                 tangentVectorDirection = rat->rotation * (M_PI / 180) + M_PI;
1379                 rotatingAround.x = roundToNearestHalf(rat->position.x
1380                                 + 0.5 * cos(tangentVectorDirection));
1381                 rotatingAround.z = roundToNearestHalf(rat->position.z
1382                                 + 0.5 * sin(tangentVectorDirection));
1383
1384                 rat->rotation -= DEF_ANGULAR_CONVERSION_FACTOR
1385                         * rat->remainingDistanceToTravel;
1386
1387                 if (previousRotation > WEST && rat->rotation <= WEST) {
1388                         rat->rotation = WEST;
1389                         rat->remainingDistanceToTravel -= (M_PI / 180)
1390                                 * fabs(previousRotation - rat->rotation);
1391                 } else if (previousRotation > SOUTH && rat->rotation <= SOUTH) {
1392                         rat->rotation = SOUTH;
1393                         rat->remainingDistanceToTravel -= (M_PI / 180)
1394                                 * fabs(previousRotation - rat->rotation);
1395                 } else if (previousRotation > EAST && rat->rotation <= EAST) {
1396                         rat->rotation = EAST;
1397                         rat->remainingDistanceToTravel -= (M_PI / 180)
1398                                 * fabs(previousRotation - rat->rotation);
1399                 } else if (previousRotation > NORTH && rat->rotation <= NORTH) {
1400                         rat->rotation = NORTH;
1401                         rat->remainingDistanceToTravel -= (M_PI / 180)
1402                                 * fabs(previousRotation - rat->rotation);
1403                 } else
1404                         rat->remainingDistanceToTravel = 0;
1405
1406                 tangentVectorDirection = rat->rotation * (M_PI / 180);
1407         }
1408         else {
1409                 tangentVectorDirection = rat->rotation * (M_PI / 180);
1410                 rotatingAround.x = roundToNearestHalf(rat->position.x
1411                                 + 0.5 * cos(tangentVectorDirection));
1412                 rotatingAround.z = roundToNearestHalf(rat->position.z
1413                                 + 0.5 * sin(tangentVectorDirection));
1414
1415                 rat->rotation += DEF_ANGULAR_CONVERSION_FACTOR
1416                         * rat->remainingDistanceToTravel;
1417
1418                 if (rat->rotation >= 360) {
1419                         rat->rotation = NORTH;
1420                         rat->remainingDistanceToTravel -= (M_PI / 180)
1421                                 * fabs(previousRotation - 360);
1422                 } else if (previousRotation < WEST && rat->rotation >= WEST) {
1423                         rat->rotation = WEST;
1424                         rat->remainingDistanceToTravel -= (M_PI / 180)
1425                                 * fabs(previousRotation - rat->rotation);
1426                 } else if (previousRotation < SOUTH && rat->rotation >= SOUTH) {
1427                         rat->rotation = SOUTH;
1428                         rat->remainingDistanceToTravel -= (M_PI / 180)
1429                                 * fabs(previousRotation - rat->rotation);
1430                 } else if (previousRotation < EAST && rat->rotation >= EAST) {
1431                         rat->rotation = EAST;
1432                         rat->remainingDistanceToTravel -= (M_PI / 180)
1433                                 * fabs(previousRotation - rat->rotation);
1434                 } else
1435                         rat->remainingDistanceToTravel = 0;
1436
1437                 tangentVectorDirection = rat->rotation * (M_PI / 180) + M_PI;
1438         }
1439
1440         rat->position.x = rotatingAround.x + 0.5 * cos(tangentVectorDirection);
1441         rat->position.z = rotatingAround.z + 0.5 * sin(tangentVectorDirection);
1442
1443         if (rat->rotation < 0)
1444                 rat->rotation += 360;
1445
1446         if (rat->rotation == NORTH || rat->rotation == EAST
1447                         || rat->rotation == SOUTH || rat->rotation == WEST) {
1448                 rat->position.x = roundToNearestHalf(rat->position.x);
1449                 rat->position.z = roundToNearestHalf(rat->position.z);
1450                 changeState(rat, maze);
1451         }
1452 }
1453
1454 static void
1455 turnAround(Rat* rat, maze_configuration* maze)
1456 {
1457         GLfloat previousRotation = rat->rotation;
1458
1459         rat->rotation -= 1.5 * DEF_ANGULAR_CONVERSION_FACTOR
1460                 * rat->remainingDistanceToTravel;
1461
1462         if (previousRotation > rat->desiredRotation
1463                         && rat->rotation <= rat->desiredRotation) {
1464                 rat->rotation = rat->desiredRotation;
1465                 rat->remainingDistanceToTravel -= (M_PI / 180)
1466                         * fabs(previousRotation - rat->rotation);
1467                 changeState(rat, maze);
1468         }
1469         else {
1470                 rat->remainingDistanceToTravel = 0;
1471                 if (rat->rotation < 0) rat->rotation += 360;
1472         }
1473 }
1474
1475 static void
1476 invert(maze_configuration* maze)
1477 {
1478         GLfloat previousInversion = maze->camera.inversion;
1479         int shouldChangeState = 0;
1480         unsigned cameraX = (unsigned)roundf(maze->camera.position.x * 2),
1481                          cameraZ = (unsigned)roundf(maze->camera.position.z * 2);
1482
1483         maze->camera.inversion += 1.5 * DEF_ANGULAR_CONVERSION_FACTOR
1484                 * maze->camera.remainingDistanceToTravel;
1485         if (previousInversion < 180 && maze->camera.inversion >= 180) {
1486                 maze->camera.inversion = 180;
1487                 maze->camera.remainingDistanceToTravel -= (M_PI / 180)
1488                         * fabs(previousInversion - maze->camera.inversion);
1489                 shouldChangeState = 1;
1490         }
1491         else if (maze->camera.inversion >= 360) {
1492                 maze->camera.inversion = 0;
1493                 maze->camera.remainingDistanceToTravel -= (M_PI / 180)
1494                         * fabs(previousInversion - maze->camera.inversion);
1495                 shouldChangeState = 1;
1496         } else
1497                 maze->camera.remainingDistanceToTravel = 0;
1498
1499         if (shouldChangeState) {
1500                 switch ((int)maze->camera.rotation) {
1501                         case NORTH:
1502                                 maze->mazeGrid[cameraZ - 1][cameraX] = CELL;
1503                                 break;
1504                         case EAST:
1505                                 maze->mazeGrid[cameraZ][cameraX + 1] = CELL;
1506                                 break;
1507                         case SOUTH:
1508                                 maze->mazeGrid[cameraZ + 1][cameraX] = CELL;
1509                                 break;
1510                         case WEST:
1511                                 maze->mazeGrid[cameraZ][cameraX - 1] = CELL;
1512                                 break;
1513                         default:
1514                                 break;
1515                 }
1516
1517                 changeState(&maze->camera, maze);
1518         }
1519 }
1520
1521 static void
1522 changeState(Rat* rat, maze_configuration* maze)
1523 {
1524         unsigned char inFrontOfRat, toTheLeft, straightAhead, toTheRight;
1525         unsigned ratX = (unsigned)roundf(rat->position.x * 2),
1526                          ratZ = (unsigned)roundf(rat->position.z * 2);
1527
1528         switch ((int)rat->rotation) {
1529                 case NORTH:
1530                         inFrontOfRat = maze->mazeGrid[ratZ - 1][ratX];
1531                         toTheLeft = maze->mazeGrid[ratZ - 1][ratX - 1];
1532                         straightAhead = maze->mazeGrid[ratZ - 2][ratX];
1533                         toTheRight = maze->mazeGrid[ratZ - 1][ratX + 1];
1534                         break;
1535                 case EAST:
1536                         inFrontOfRat = maze->mazeGrid[ratZ][ratX + 1];
1537                         toTheLeft = maze->mazeGrid[ratZ - 1][ratX + 1];
1538                         straightAhead = maze->mazeGrid[ratZ][ratX + 2];
1539                         toTheRight = maze->mazeGrid[ratZ + 1][ratX + 1];
1540                         break;
1541                 case SOUTH:
1542                         inFrontOfRat = maze->mazeGrid[ratZ + 1][ratX];
1543                         toTheLeft = maze->mazeGrid[ratZ + 1][ratX + 1];
1544                         straightAhead = maze->mazeGrid[ratZ + 2][ratX];
1545                         toTheRight = maze->mazeGrid[ratZ + 1][ratX - 1];
1546                         break;
1547                 case WEST:
1548                         inFrontOfRat = maze->mazeGrid[ratZ][ratX - 1];
1549                         toTheLeft = maze->mazeGrid[ratZ + 1][ratX - 1];
1550                         straightAhead = maze->mazeGrid[ratZ][ratX - 2];
1551                         toTheRight = maze->mazeGrid[ratZ - 1][ratX - 1];
1552                         break;
1553                 default:
1554                         inFrontOfRat = toTheLeft = straightAhead = toTheRight = CELL;
1555                         break;
1556         }
1557
1558         if (rat->isCamera && inFrontOfRat == FINISH)
1559                 rat->state = FINISHING;
1560         else if (rat->isCamera && inFrontOfRat >= INVERTER_TETRAHEDRON
1561                         && inFrontOfRat <= INVERTER_ICOSAHEDRON)
1562                 rat->state = INVERTING;
1563         else if (toTheLeft != WALL && toTheLeft != WALL_GL_REDBOOK)
1564                 rat->state = TURNING_LEFT;
1565         else if (straightAhead != WALL && straightAhead != WALL_GL_REDBOOK)
1566                 rat->state = WALKING;
1567         else if (toTheRight != WALL && toTheRight != WALL_GL_REDBOOK)
1568                 rat->state = TURNING_RIGHT;
1569         else {
1570                 rat->state = TURNING_AROUND;
1571
1572                 switch ((int)rat->rotation) {
1573                         case NORTH:
1574                                 rat->desiredRotation = SOUTH;
1575                                 break;
1576                         case EAST:
1577                                 rat->desiredRotation = WEST;
1578                                 break;
1579                         case SOUTH:
1580                                 rat->desiredRotation = NORTH;
1581                                 break;
1582                         case WEST:
1583                                 rat->desiredRotation = EAST;
1584                                 break;
1585                         default:
1586                                 break;
1587                 }
1588         }
1589 }
1590
1591 static void
1592 updateInverterRotation(maze_configuration* maze)
1593 {
1594         maze->inverterRotation += 45 * maze->camera.remainingDistanceToTravel;
1595 }
1596
1597 static void drawInverter(maze_configuration* maze, Tuple coordinates)
1598 {
1599         unsigned char type = maze->mazeGrid[coordinates.row][coordinates.column];
1600
1601         if (maze->wallHeight < 1 ||
1602                         type < INVERTER_TETRAHEDRON || type > INVERTER_ICOSAHEDRON
1603                         || (coordinates.row == 0 && coordinates.column == 0))
1604                 return;
1605
1606         glEnable(GL_LIGHTING);
1607         glEnable(GL_CULL_FACE);
1608
1609         glMatrixMode(GL_MODELVIEW);
1610         glPushMatrix();
1611
1612         glTranslatef(coordinates.column / 2.0, 0.25, coordinates.row / 2.0);
1613         glRotatef(0.618033989 * maze->inverterRotation, 0, 1, 0);
1614         glRotatef(maze->inverterRotation, 1, 0, 0);
1615
1616     if (type >= countof(maze->dlists)) abort();
1617         glCallList(maze->dlists[type]);
1618
1619         glPopMatrix();
1620
1621         glDisable(GL_LIGHTING);
1622         glDisable(GL_CULL_FACE);
1623 }
1624
1625 static void
1626 drawWalls(ModeInfo * mi)
1627 {
1628         unsigned i, j;
1629         Tuple startCoordinates, endCoordinates;
1630
1631         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1632
1633         for (i = 0; i < maze->numRows; i++) {
1634                 for (j = 0; j < maze->numColumns; j++) {
1635                         if (maze->mazeGrid[i][j] == WALL
1636                                         || maze->mazeGrid[i][j] == WALL_GL_REDBOOK) {
1637                                 if (maze->mazeGrid[i][j] == WALL) {
1638                                         glBindTexture(GL_TEXTURE_2D, maze->wallTexture);
1639                                         if (dropAcid || dropAcidWalls)
1640                                                 glColor3f(maze->acidColor.red, maze->acidColor.green,
1641                                                                 maze->acidColor.blue);
1642 # ifdef USE_FLOATING_IMAGES
1643                                 } else {
1644                                         glBindTexture(GL_TEXTURE_2D, maze->glTextbookTexture);
1645                                         glColor3f(1, 1, 1);
1646 # endif
1647                                 }
1648
1649                                 if (isOdd(i) && isEven(j)) {
1650                                         startCoordinates.row = i / 2;
1651                                         startCoordinates.column = j / 2;
1652                                         endCoordinates.row = i / 2 + 1;
1653                                         endCoordinates.column = j / 2;
1654                                         drawWall(startCoordinates, endCoordinates, maze);
1655                                 } else if (isEven(i) && isOdd(j)) {
1656                                         startCoordinates.row = i / 2;
1657                                         startCoordinates.column = j / 2;
1658                                         endCoordinates.row = i / 2;
1659                                         endCoordinates.column = j / 2 + 1;
1660                                         drawWall(startCoordinates, endCoordinates, maze);
1661                                 }
1662                         }
1663                 }
1664         }
1665
1666         glBindTexture(GL_TEXTURE_2D, 0);
1667         glColor3f(1, 1, 1);
1668 }
1669
1670 static void
1671 drawWall(Tuple startCoordinates, Tuple endCoordinates, maze_configuration* maze)
1672 {
1673         GLfloat wallHeight = maze->wallHeight;
1674
1675         glBegin(GL_QUADS);
1676                 if (startCoordinates.row == endCoordinates.row) {
1677                         glTexCoord2f(0, 0);
1678                         glVertex3f(startCoordinates.column, 0, startCoordinates.row);
1679                         glTexCoord2f(1, 0);
1680                         glVertex3f(endCoordinates.column, 0, startCoordinates.row);
1681                         glTexCoord2f(1, 1);
1682                         glVertex3f(endCoordinates.column, wallHeight, endCoordinates.row);
1683                         glTexCoord2f(0, 1);
1684                         glVertex3f(startCoordinates.column, wallHeight, endCoordinates.row);
1685                 } else {
1686                         glTexCoord2f(0, 0);
1687                         glVertex3f(startCoordinates.column, 0, startCoordinates.row);
1688                         glTexCoord2f(1, 0);
1689                         glVertex3f(startCoordinates.column, 0, endCoordinates.row);
1690                         glTexCoord2f(1, 1);
1691                         glVertex3f(endCoordinates.column, wallHeight, endCoordinates.row);
1692                         glTexCoord2f(0, 1);
1693                         glVertex3f(endCoordinates.column, wallHeight, startCoordinates.row);
1694                 }
1695         glEnd();
1696 }
1697
1698 static void
1699 drawCeiling(ModeInfo * mi)
1700 {
1701         Tuple farRightCorner;
1702         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1703         farRightCorner.row = maze->numRows / 2;
1704         farRightCorner.column = maze->numColumns / 2;
1705         glBindTexture(GL_TEXTURE_2D, maze->ceilingTexture);
1706
1707         glBegin(GL_QUADS);
1708                 if (dropAcid || dropAcidCeiling)
1709                         glColor3f(maze->acidColor.red, maze->acidColor.green,
1710                                         maze->acidColor.blue);
1711                 glTexCoord2f(0, 0);
1712                 glVertex3f(0, 1, 0);
1713                 glTexCoord2f(farRightCorner.column, 0);
1714                 glVertex3f(farRightCorner.column, 1, 0);
1715                 glTexCoord2f(farRightCorner.column, farRightCorner.row);
1716                 glVertex3f(farRightCorner.column, 1, farRightCorner.row);
1717                 glTexCoord2f(0, farRightCorner.row);
1718                 glVertex3f(0, 1, farRightCorner.row);
1719                 glColor3f(1, 1, 1);
1720         glEnd();
1721
1722         glBindTexture(GL_TEXTURE_2D, 0);
1723 }
1724
1725 static void
1726 drawFloor(ModeInfo * mi)
1727 {
1728         Tuple farRightCorner;
1729         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1730         farRightCorner.row = maze->numRows / 2;
1731         farRightCorner.column = maze->numColumns / 2;
1732         glBindTexture(GL_TEXTURE_2D, maze->floorTexture);
1733
1734         glBegin(GL_QUADS);
1735                 if (dropAcid || dropAcidFloor)
1736                         glColor3f(maze->acidColor.red, maze->acidColor.green,
1737                                         maze->acidColor.blue);
1738                 glTexCoord2f(0, 0);
1739                 glVertex3f(0, 0, 0);
1740                 glTexCoord2f(farRightCorner.column, 0);
1741                 glVertex3f(farRightCorner.column, 0, 0);
1742                 glTexCoord2f(farRightCorner.column, farRightCorner.row);
1743                 glVertex3f(farRightCorner.column, 0, farRightCorner.row);
1744                 glTexCoord2f(0, farRightCorner.row);
1745                 glVertex3f(0, 0, farRightCorner.row);
1746                 glColor3f(1, 1, 1);
1747         glEnd();
1748
1749         glBindTexture(GL_TEXTURE_2D, 0);
1750 }
1751
1752 static void
1753 drawPane(ModeInfo *mi, GLuint texture, Tuple position)
1754 {
1755         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1756         if (position.row == 0 && position.column == 0) return;
1757
1758         glEnable(GL_BLEND);
1759         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1760
1761         glBindTexture(GL_TEXTURE_2D, texture);
1762
1763         glMatrixMode(GL_MODELVIEW);
1764         glPushMatrix();
1765         glTranslatef(position.column / 2.0, 0, position.row / 2.0);
1766         glRotatef(-1 * maze->camera.rotation - 90, 0, 1, 0);
1767
1768     if (MI_WIDTH(mi) < MI_HEIGHT(mi))
1769       {
1770         /* Keep the Start button readable in phone portrait mode. */
1771         glScalef (0.5, 0.5, 0.5);
1772         glTranslatef (0, 0.5, 0);
1773       }
1774
1775         glBegin(GL_QUADS);
1776                 glColor4f(1, 1, 1, 0.9);
1777                 glTexCoord2f(0, 0);
1778                 glVertex3f(0, 0, 0.5);
1779                 glTexCoord2f(1, 0);
1780                 glVertex3f(0, 0, -0.5);
1781                 glTexCoord2f(1, 1);
1782                 glVertex3f(0, maze->wallHeight, -0.5);
1783                 glTexCoord2f(0, 1);
1784                 glVertex3f(0, maze->wallHeight, 0.5);
1785                 glColor3f(1, 1, 1);
1786         glEnd();
1787
1788         glPopMatrix();
1789         glBindTexture(GL_TEXTURE_2D, 0);
1790         glDisable(GL_BLEND);
1791 }
1792
1793 static void
1794 drawRat(Tuplef position, maze_configuration* maze)
1795 {
1796         if (position.x == 0 && position.z == 0) return;
1797
1798         glEnable(GL_BLEND);
1799         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1800
1801         glBindTexture(GL_TEXTURE_2D, maze->ratTexture);
1802
1803         glMatrixMode(GL_MODELVIEW);
1804         glPushMatrix();
1805         glTranslatef(position.x, 0, position.z);
1806         glRotatef(-1 * maze->camera.rotation - 90, 0, 1, 0);
1807
1808     glScalef (0.25, 0.25, 0.25);
1809         glBegin(GL_QUADS);
1810                 glTexCoord2f(0, 0);
1811                 glVertex3f(0, 0, 0.5);
1812                 glTexCoord2f(1, 0);
1813                 glVertex3f(0, 0, -0.5);
1814                 glTexCoord2f(1, 1);
1815                 glVertex3f(0, maze->wallHeight, -0.5);
1816                 glTexCoord2f(0, 1);
1817                 glVertex3f(0, maze->wallHeight, 0.5);
1818         glEnd();
1819
1820         glPopMatrix();
1821         glBindTexture(GL_TEXTURE_2D, 0);
1822         glDisable(GL_BLEND);
1823 }
1824
1825
1826 static void drawOverlay(ModeInfo *mi)
1827 {
1828         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1829         unsigned i, j;
1830     GLfloat h = (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi);
1831
1832         glMatrixMode(GL_PROJECTION);
1833         glPushMatrix();
1834         glLoadIdentity();
1835         glMatrixMode(GL_MODELVIEW);
1836         glPushMatrix();
1837         glLoadIdentity();
1838     glOrtho(-1/h, 1/h, 1, -1, -1, 1);
1839
1840         glEnable(GL_BLEND);
1841         glColor4f(0, 0, 1, 0.75);
1842         glScalef(0.25, 0.25, 0.25);
1843
1844         glCallList(maze->dlists[ARROW]);
1845
1846         glRotatef(maze->camera.inversion, 0, 1, 0);
1847         glRotatef(maze->camera.rotation, 0, 0, -1);
1848         glTranslatef(-maze->camera.position.x, -maze->camera.position.z, 0);
1849         glColor4f(1, 1, 1, 0.75);
1850         
1851         glBegin(GL_LINES);
1852         for (i = 0; i < maze->numRows; i++) {
1853                 for (j = 0; j < maze->numColumns; j++) {
1854                         if (maze->mazeGrid[i][j] == WALL
1855                                         || maze->mazeGrid[i][j] == WALL_GL_REDBOOK) {
1856                                 if (isOdd(i) && isEven(j)) {
1857                                                 glVertex2f(j / 2, i / 2);
1858                                                 glVertex2f(j / 2, i / 2 + 1);
1859                                 } else if (isEven(i) && isOdd(j)) {
1860                                                 glVertex2f(j / 2, i / 2);
1861                                                 glVertex2f(j / 2 + 1, i / 2);
1862                                 }
1863                         }
1864                 }
1865         }
1866         glEnd();
1867
1868         glColor4f(1, 0, 0, 0.75);
1869
1870         glPushMatrix();
1871         glTranslatef(maze->startPosition.column / 2.0,
1872                         maze->startPosition.row / 2.0, 0);
1873         glCallList(maze->dlists[SQUARE]);
1874         glPopMatrix();
1875
1876         glColor4f(1, 1, 0, 0.75);
1877
1878         glPushMatrix();
1879         glTranslatef(maze->finishPosition.column / 2.0,
1880                         maze->finishPosition.row / 2.0, 0);
1881         glCallList(maze->dlists[STAR]);
1882         glPopMatrix();
1883
1884         glColor4f(1, 0.607843137, 0, 0.75);
1885
1886         for (i = 0; i < numRats; i++) {
1887                 if (maze->rats[i].position.x == 0 && maze->rats[i].position.z == 0)
1888                         continue;
1889                 glPushMatrix();
1890                 glTranslatef(maze->rats[i].position.x, maze->rats[i].position.z, 0);
1891                 glRotatef(maze->rats[i].rotation, 0, 0, 1);
1892                 glCallList(maze->dlists[ARROW]);
1893                 glPopMatrix();
1894         }
1895
1896         glColor4f(1, 1, 1, 1);
1897
1898         for (i = 0; i < numInverters; i++) {
1899                 j = maze->mazeGrid[maze->inverterPosition[i].row]
1900                         [maze->inverterPosition[i].column];
1901                 if (j >= INVERTER_TETRAHEDRON && j <= INVERTER_ICOSAHEDRON) {
1902                         glPushMatrix();
1903                         glTranslatef(maze->inverterPosition[i].column / 2.0,
1904                                         maze->inverterPosition[i].row / 2.0, 0);
1905                         glRotatef(1.5 * maze->inverterRotation, 0, 0, 1);
1906                         glCallList(maze->dlists[TRIANGLE]);
1907                         glPopMatrix();
1908                 }
1909         }
1910
1911         glDisable(GL_BLEND);
1912
1913         glPopMatrix();
1914         glMatrixMode(GL_PROJECTION);
1915         glPopMatrix();
1916 }
1917
1918 ENTRYPOINT void
1919 free_maze (ModeInfo * mi)
1920 {
1921         maze_configuration *maze = &mazes[MI_SCREEN(mi)];
1922
1923     glDeleteTextures(1, &maze->wallTexture);
1924     glDeleteTextures(1, &maze->floorTexture);
1925     glDeleteTextures(1, &maze->ceilingTexture);
1926     glDeleteTextures(1, &maze->startTexture);
1927     glDeleteTextures(1, &maze->ratTexture);
1928     glDeleteTextures(1, &maze->finishTexture);
1929 # ifdef USE_FLOATING_IMAGES
1930     glDeleteTextures(1, &maze->glTextbookTexture);
1931     glDeleteTextures(1, &maze->gl3dTextTexture);
1932 # endif
1933 # ifdef USE_FRACTAL_IMAGES
1934     glDeleteTextures(1, &maze->fractal1Texture);
1935     glDeleteTextures(1, &maze->fractal2Texture);
1936     glDeleteTextures(1, &maze->fractal3Texture);
1937     glDeleteTextures(1, &maze->fractal4Texture);
1938 # endif
1939
1940     glDeleteLists(maze->dlists[ARROW], 4);
1941     glDeleteLists(maze->dlists[INVERTER_TETRAHEDRON], 4);
1942
1943     free(maze->mazeGrid);
1944     free(maze->wallList);
1945     free(maze->inverterPosition);
1946     free(maze->gl3dTextPosition);
1947     free(maze->rats);
1948
1949     memset(maze, 0, sizeof(*maze));
1950 }
1951
1952
1953 XSCREENSAVER_MODULE_2 ("Maze3D", maze3d, maze)
1954
1955 #endif