From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / glx / maze3d.c
diff --git a/hacks/glx/maze3d.c b/hacks/glx/maze3d.c
new file mode 100755 (executable)
index 0000000..255839e
--- /dev/null
@@ -0,0 +1,1955 @@
+/* -*- Mode: C; tab-width: 4 -*- */
+/* maze3d --- A recreation of the old 3D maze screensaver from Windows 95.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * Revision History:
+ *
+ * 03-Apr-2018: Released initial version of "3D Maze"
+ * (sudoer@riseup.net)
+ */
+
+#undef USE_FLOATING_IMAGES
+#undef USE_FRACTAL_IMAGES
+
+#ifdef STANDALONE
+#define DEFAULTS       "*delay:                        20000   \n"     \
+                                       "*showFPS:                      False   \n" \
+
+#define release_maze 0
+# include "xlockmore.h"                                /* from the xscreensaver distribution */
+#else  /* !STANDALONE */
+# include "xlock.h"                                    /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+#include <math.h>
+
+#ifdef USE_GL /* whole file */
+
+#define DEF_ANGULAR_CONVERSION_FACTOR 90
+#define DEF_SPEED "1.0"
+#define DEF_NUM_ROWS "12"
+#define DEF_NUM_COLUMNS "12"
+#define DEF_NUM_RATS "1"
+#define DEF_NUM_INVERTERS "10"
+#define DEF_SHOW_OVERLAY "False"
+#define DEF_DROP_ACID "False"
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "../images/gen/brick1_png.h"
+#include "../images/gen/brick2_png.h"
+#include "../images/gen/wood2_png.h"
+#include "../images/gen/start_png.h"
+#include "../images/gen/bob_png.h"
+#include "../images/gen/logo-32_png.h"
+
+#ifdef USE_FLOATING_IMAGES
+# include "../images/gen/opengltxt_png.h"
+# include "../images/gen/openglbook_png.h"
+#endif
+#ifdef USE_FRACTAL_IMAGES
+# include "../images/gen/fractal1_png.h"
+# include "../images/gen/fractal2_png.h"
+# include "../images/gen/fractal3_png.h"
+# include "../images/gen/fractal4_png.h"
+#endif
+
+#include "ximage-loader.h"
+
+static int dropAcid, dropAcidWalls, dropAcidCeiling, dropAcidFloor, numInverters, numRats;
+#ifdef USE_FLOATING_IMAGES
+static int numGl3dTexts, numGlRedbooks;
+#endif
+static int shouldDrawOverlay, numRows, numColumns;
+static char *wallTexture, *floorTexture, *ceilingTexture;
+static GLfloat speed;
+
+static XrmOptionDescRec opts[] = {
+       {"-drop-acid", ".maze3d.dropAcid", XrmoptionNoArg, "true"},
+       {"-drop-acid-walls", ".maze3d.dropAcidWalls", XrmoptionNoArg, "true"},
+       {"-drop-acid-floor", ".maze3d.dropAcidFloor", XrmoptionNoArg, "true"},
+       {"-drop-acid-ceiling", ".maze3d.dropAcidCeiling", XrmoptionNoArg, "true"},
+       {"-wall-texture", ".maze3d.wallTexture", XrmoptionSepArg, 0},
+       {"-floor-texture", ".maze3d.floorTexture", XrmoptionSepArg, 0},
+       {"-ceiling-texture", ".maze3d.ceilingTexture", XrmoptionSepArg, 0},
+       {"-rows", ".maze3d.numRows", XrmoptionSepArg, 0},
+       {"-columns", ".maze3d.numColumns", XrmoptionSepArg, 0},
+       {"-inverters", ".maze3d.numInverters", XrmoptionSepArg, 0},
+       {"-rats", ".maze3d.numRats", XrmoptionSepArg, 0},
+# ifdef USE_FLOATING_IMAGES
+       {"-gl-3d-texts", ".maze3d.numGl3dTexts", XrmoptionSepArg, 0},
+       {"-gl-redbooks", ".maze3d.numGlRedbooks", XrmoptionSepArg, 0},
+# endif
+       {"-overlay", ".maze3d.showOverlay", XrmoptionNoArg, "true"},
+       {"-speed", ".maze3d.speed", XrmoptionSepArg, 0},
+};
+
+static argtype vars[] = {
+       {&dropAcid, "dropAcid", "Drop Acid", DEF_DROP_ACID, t_Bool},
+       {&dropAcidWalls, "dropAcidWalls", "Drop Acid Walls", "False", t_Bool},
+       {&dropAcidFloor, "dropAcidFloor", "Drop Acid Floor", "False", t_Bool},
+       {&dropAcidCeiling, "dropAcidCeiling", "Drop Acid Ceiling", "False", t_Bool},
+       {&wallTexture, "wallTexture", "Wall Texture", "brick-wall", t_String},
+       {&floorTexture, "floorTexture", "Floor Texture", "wood-floor", t_String},
+       {&ceilingTexture, "ceilingTexture", "Ceiling Texture", "ceiling-tiles",
+               t_String},
+       {&numRows, "numRows", "Number of Rows", DEF_NUM_ROWS, t_Int},
+       {&numColumns, "numColumns", "Number of Columns", DEF_NUM_COLUMNS, t_Int},
+       {&numInverters, "numInverters", "Number of Inverters", DEF_NUM_INVERTERS, t_Int},
+       {&numRats, "numRats", "Number of Rats", DEF_NUM_RATS, t_Int},
+# ifdef USE_FLOATING_IMAGES
+       {&numGl3dTexts, "numGl3dTexts", "Number of GL 3D Texts", "3", t_Int},
+       {&numGlRedbooks, "numGlRedbooks", "Number of GL Redbooks", "3", t_Int},
+# endif
+       {&shouldDrawOverlay, "showOverlay", "Show Overlay", DEF_SHOW_OVERLAY, t_Bool},
+       {&speed, "speed", "speed", DEF_SPEED, t_Float},
+};
+
+ENTRYPOINT ModeSpecOpt maze_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+enum cellTypes
+{
+       WALL, CELL_UNVISITED, CELL, START, FINISH, GL_3D_TEXT, INVERTER_TETRAHEDRON,
+       INVERTER_OCTAHEDRON, INVERTER_DODECAHEDRON, INVERTER_ICOSAHEDRON,
+       WALL_GL_REDBOOK
+};
+
+enum programStates
+{
+       STARTING, WALKING, TURNING_LEFT, TURNING_RIGHT, TURNING_AROUND, INVERTING,
+       FINISHING
+};
+
+enum overlayLists
+{
+       ARROW = 15, SQUARE, STAR, TRIANGLE
+};
+
+enum directions
+{
+       NORTH = 0, EAST = 90, SOUTH = 180, WEST = 270
+};
+
+typedef struct
+{
+       unsigned row, column;
+} Tuple;
+
+typedef struct
+{
+       GLfloat x, z;
+} Tuplef;
+
+typedef struct
+{
+       GLfloat red, green, blue;
+} Color;
+
+typedef struct
+{
+       Tuplef position;
+       GLfloat rotation, desiredRotation, inversion, remainingDistanceToTravel;
+       unsigned char state, isCamera;
+} Rat;
+
+
+/* structure for holding the maze data */
+typedef struct
+{
+       GLXContext *glx_context;
+
+       unsigned char **mazeGrid;
+       Tuple *wallList;
+       unsigned wallListSize;
+       Tuple startPosition, finishPosition, *inverterPosition,
+       *gl3dTextPosition;
+    GLuint  wallTexture, floorTexture, ceilingTexture, startTexture,
+      finishTexture, ratTexture;
+       Rat camera;
+       Rat *rats;
+# ifdef USE_FLOATING_IMAGES
+       GLuint gl3dTextTexture, glTextbookTexture;
+# endif
+# ifdef USE_FRACTAL_IMAGES
+       GLuint fractal1Texture, fractal2Texture, fractal3Texture, fractal4Texture;
+# endif
+       Color acidColor;
+       float acidHue;
+       GLfloat wallHeight, inverterRotation;
+    Bool button_down_p;
+    int numRows, numColumns, numGlRedbooks;
+    GLuint dlists[30];  /* ARROW etc index into this */
+
+} maze_configuration;
+
+static maze_configuration *mazes = NULL;
+
+static void newMaze(maze_configuration* maze);
+static void constructLists(ModeInfo *);
+static void initializeGrid(maze_configuration* maze);
+static float roundToNearestHalf(float num);
+static unsigned isOdd(unsigned num);
+static unsigned isEven(unsigned num);
+static void buildMaze(maze_configuration* maze);
+static void addWallsToList(Tuple cell, maze_configuration* maze);
+static unsigned char isRemovableWall(Tuple coordinates,
+               maze_configuration* maze);
+static void addCells(Tuple cellToAdd, Tuple currentWall,
+               maze_configuration* maze);
+static void removeWallFromList(unsigned index, maze_configuration* maze);
+static void placeMiscObjects(maze_configuration* maze);
+static Tuple placeObject(maze_configuration* maze, unsigned char type);
+static void shiftAcidColor(maze_configuration* maze);
+static void refreshRemainingDistanceToTravel(ModeInfo * mi);
+static void step(Rat* rat, maze_configuration* maze);
+static void walk(Rat* rat, char axis, int sign, maze_configuration* maze);
+static void turn(Rat* rat, maze_configuration* maze);
+static void turnAround(Rat* rat, maze_configuration* maze);
+static void invert(maze_configuration* maze);
+static void changeState(Rat* rat, maze_configuration* maze);
+static void updateInverterRotation(maze_configuration* maze);
+static void drawInverter(maze_configuration* maze, Tuple coordinates);
+static void drawWalls(ModeInfo * mi);
+static void drawWall(Tuple startCoordinates, Tuple endCoordinates,
+               maze_configuration* maze);
+static void drawCeiling(ModeInfo * mi);
+static void drawFloor(ModeInfo * mi);
+static void drawPane(ModeInfo *, GLuint texture, Tuple position);
+static void drawRat(Tuplef position, maze_configuration* maze);
+static void drawOverlay(ModeInfo *);
+
+/* Set up and enable texturing on our object */
+static void
+setup_png_texture (ModeInfo *mi, const unsigned char *png_data,
+                   unsigned long data_size)
+{
+    XImage *image = image_data_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
+                                          png_data, data_size);
+       char buf[1024];
+       clear_gl_error();
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+       /* iOS invalid enum:
+       glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
+       */
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                                image->width, image->height, 0,
+                                GL_RGBA,
+                                /* GL_UNSIGNED_BYTE, */
+                                GL_UNSIGNED_INT_8_8_8_8_REV,
+                                image->data);
+       sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
+                        check_gl_error(buf);
+}
+
+
+static Bool
+setup_file_texture (ModeInfo *mi, char *filename)
+{
+       Display *dpy = mi->dpy;
+       Visual *visual = mi->xgwa.visual;
+       char buf[1024];
+
+       XImage *image = file_to_ximage (dpy, visual, filename);
+       if (!image) return False;
+
+       clear_gl_error();
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+       glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                                image->width, image->height, 0,
+                                GL_RGBA,
+                                GL_UNSIGNED_BYTE, image->data);
+       sprintf (buf, "texture: %.100s (%dx%d)",
+                        filename, image->width, image->height);
+       check_gl_error(buf);
+       return True;
+}
+
+static void
+setup_textures(ModeInfo * mi)
+{
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+    GLint mag = GL_NEAREST;  /* GL_LINEAR */
+
+       glGenTextures(1, &maze->finishTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->finishTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, logo_32_png, sizeof(logo_32_png));
+
+       glGenTextures(1, &maze->ratTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->ratTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, bob_png, sizeof(bob_png));
+
+# ifdef USE_FLOATING_IMAGES
+       glGenTextures(1, &maze->glTextbookTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->glTextbookTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, openglbook_png, sizeof(openglbook_png));
+
+       glGenTextures(1, &maze->gl3dTextTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->gl3dTextTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, opengltxt_png, sizeof(opengltxt_png));
+# endif
+
+# ifdef USE_FRACTAL_IMAGES
+       glGenTextures(1, &maze->fractal1Texture);
+       glBindTexture(GL_TEXTURE_2D, maze->fractal1Texture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
+
+       glGenTextures(1, &maze->fractal2Texture);
+       glBindTexture(GL_TEXTURE_2D, maze->fractal2Texture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
+
+       glGenTextures(1, &maze->fractal3Texture);
+       glBindTexture(GL_TEXTURE_2D, maze->fractal3Texture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
+
+       glGenTextures(1, &maze->fractal4Texture);
+       glBindTexture(GL_TEXTURE_2D, maze->fractal4Texture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
+# endif
+
+       glGenTextures(1, &maze->startTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->startTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       setup_png_texture(mi, start_png, sizeof(start_png));
+
+       glGenTextures(1, &maze->ceilingTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->ceilingTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       if (!ceilingTexture || !*ceilingTexture
+                       || !strcmp(ceilingTexture, "ceiling-tiles")) {
+               DEFAULT_CEILING_TEXTURE:
+               setup_png_texture(mi, brick2_png, sizeof(brick2_png));
+       } else if (!strcmp(ceilingTexture, "brick-wall")) {
+               setup_png_texture(mi, brick1_png, sizeof(brick1_png));
+       } else if (!strcmp(ceilingTexture, "wood-floor")) {
+               setup_png_texture(mi, wood2_png, sizeof(wood2_png));
+# ifdef USE_FRACTAL_IMAGES
+       } else if (!strcmp(ceilingTexture, "fractal-1")) {
+               setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
+               dropAcidCeiling = 1;
+       } else if (!strcmp(ceilingTexture, "fractal-2")) {
+               setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
+               dropAcidCeiling = 1;
+       } else if (!strcmp(ceilingTexture, "fractal-3")) {
+               setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
+               dropAcidCeiling = 1;
+       } else if (!strcmp(ceilingTexture, "fractal-4")) {
+               setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
+               dropAcidCeiling = 1;
+# endif
+       } else {
+               if (!setup_file_texture(mi, ceilingTexture))
+                       goto DEFAULT_CEILING_TEXTURE;
+       }
+
+       glGenTextures(1, &maze->floorTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->floorTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       if (!floorTexture || !*floorTexture
+                       || !strcmp(floorTexture, "wood-floor")) {
+               DEFAULT_FLOOR_TEXTURE:
+               setup_png_texture(mi, wood2_png, sizeof(wood2_png));
+       } else if (!strcmp(floorTexture, "ceiling-tiles")) {
+               setup_png_texture(mi, brick2_png, sizeof(brick2_png));
+       } else if (!strcmp(floorTexture, "brick-wall")) {
+               setup_png_texture(mi, brick1_png, sizeof(brick1_png));
+# ifdef USE_FRACTAL_IMAGES
+       } else if (!strcmp(floorTexture, "fractal-1")) {
+               setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
+               dropAcidFloor = 1;
+       } else if (!strcmp(floorTexture, "fractal-2")) {
+               setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
+               dropAcidFloor = 1;
+       } else if (!strcmp(floorTexture, "fractal-3")) {
+               setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
+               dropAcidFloor = 1;
+       } else if (!strcmp(floorTexture, "fractal-4")) {
+               setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
+               dropAcidFloor = 1;
+# endif
+       } else {
+               if (!setup_file_texture(mi, floorTexture))
+                       goto DEFAULT_FLOOR_TEXTURE;
+       }
+
+       glGenTextures(1, &maze->wallTexture);
+       glBindTexture(GL_TEXTURE_2D, maze->wallTexture);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
+       if (!wallTexture || !*wallTexture || !strcmp(wallTexture, "brick-wall")) {
+               DEFAULT_WALL_TEXTURE:
+               setup_png_texture(mi, brick1_png, sizeof(brick1_png));
+       } else if (!strcmp(wallTexture, "ceiling-tiles")) {
+               setup_png_texture(mi, brick2_png, sizeof(brick2_png));
+       } else if (!strcmp(wallTexture, "wood-floor")) {
+               setup_png_texture(mi, wood2_png, sizeof(wood2_png));
+# ifdef USE_FRACTAL_IMAGES
+       } else if (!strcmp(wallTexture, "fractal-1")) {
+               setup_png_texture(mi, fractal1_png, sizeof(fractal1_png));
+               dropAcidWalls = 1;
+       } else if (!strcmp(wallTexture, "fractal-2")) {
+               setup_png_texture(mi, fractal2_png, sizeof(fractal2_png));
+               dropAcidWalls = 1;
+       } else if (!strcmp(wallTexture, "fractal-3")) {
+               setup_png_texture(mi, fractal3_png, sizeof(fractal3_png));
+               dropAcidWalls = 1;
+       } else if (!strcmp(wallTexture, "fractal-4")) {
+               setup_png_texture(mi, fractal4_png, sizeof(fractal4_png));
+               dropAcidWalls = 1;
+# endif
+       } else {
+               if (!setup_file_texture(mi, wallTexture))
+                       goto DEFAULT_WALL_TEXTURE;
+       }
+}
+
+static void
+initializeGrid(maze_configuration* maze)
+{
+       unsigned i, j;
+
+       for (i = 0; i < maze->numRows; i++) {
+               for (j = 0; j < maze->numColumns; j++) {
+                       if (isOdd(i) && isOdd(j))
+                               maze->mazeGrid[i][j] = CELL_UNVISITED;
+                       else
+                               maze->mazeGrid[i][j] = WALL;
+               }
+       }
+}
+
+static float
+roundToNearestHalf(float num)
+{
+       return roundf(2.0 * num) / 2.0;
+}
+
+static unsigned
+isOdd(unsigned num)
+{
+       return num % 2;
+}
+
+static unsigned
+isEven(unsigned num)
+{
+       return !isOdd(num);
+}
+
+/*This is the randomized Prim's algorithm.*/
+static void
+buildMaze(maze_configuration* maze)
+{
+       Tuple cellToAdd, firstCell = {1, 1};
+       maze->mazeGrid[1][1] = CELL;
+
+       addWallsToList(firstCell, maze);
+
+       while (maze->wallListSize > 0) {
+               unsigned randomNum = random() % maze->wallListSize;
+               Tuple currentWall = maze->wallList[randomNum];
+
+               if (isEven(currentWall.row)) {
+                       if (maze->mazeGrid[currentWall.row - 1][currentWall.column]
+                               == CELL
+                               && maze->mazeGrid[currentWall.row + 1][currentWall.column]
+                               == CELL_UNVISITED
+                       ) {
+                               cellToAdd.row = currentWall.row + 1;
+                               cellToAdd.column = currentWall.column;
+                               addCells(cellToAdd, currentWall, maze);
+                       }
+                       else if (maze->mazeGrid[currentWall.row + 1][currentWall.column]
+                               == CELL
+                               && maze->mazeGrid[currentWall.row - 1][currentWall.column]
+                               == CELL_UNVISITED
+                       ) {
+                               cellToAdd.row = currentWall.row - 1;
+                               cellToAdd.column = currentWall.column;
+                               addCells(cellToAdd, currentWall, maze);
+                       }
+               } else {
+                       if (maze->mazeGrid[currentWall.row][currentWall.column - 1]
+                               == CELL
+                               && maze->mazeGrid[currentWall.row][currentWall.column + 1]
+                               == CELL_UNVISITED
+                       ) {
+                               cellToAdd.row = currentWall.row;
+                               cellToAdd.column = currentWall.column + 1;
+                               addCells(cellToAdd, currentWall, maze);
+                       }
+                       else if (maze->mazeGrid[currentWall.row][currentWall.column + 1]
+                               == CELL
+                               && maze->mazeGrid[currentWall.row][currentWall.column - 1]
+                               == CELL_UNVISITED
+                       ) {
+                               cellToAdd.row = currentWall.row;
+                               cellToAdd.column = currentWall.column - 1;
+                               addCells(cellToAdd, currentWall, maze);
+                       }
+               }
+
+               removeWallFromList(randomNum, maze);
+       }
+}
+
+static void
+addWallsToList(Tuple cell, maze_configuration* maze)
+{
+       unsigned i;
+       Tuple walls[4];
+       walls[0].row = cell.row - 1;
+       walls[0].column = cell.column;
+       walls[1].row = cell.row + 1;
+       walls[1].column = cell.column;
+       walls[2].row = cell.row;
+       walls[2].column = cell.column - 1;
+       walls[3].row = cell.row;
+       walls[3].column = cell.column + 1;
+
+       for (i = 0; i < 4; i++) {
+               if (isRemovableWall(walls[i], maze)) {
+                       maze->wallList[maze->wallListSize] = walls[i];
+                       maze->wallListSize++;
+               }
+       }
+}
+
+static unsigned char
+isRemovableWall(Tuple coordinates, maze_configuration* maze)
+{
+       if (maze->mazeGrid[coordinates.row][coordinates.column] == WALL
+               && coordinates.row > 0
+               && coordinates.row < maze->numRows - 1
+               && coordinates.column > 0
+               && coordinates.column < maze->numColumns - 1
+       )
+               return 1;
+       else
+               return 0;
+}
+
+static void
+addCells(Tuple cellToAdd, Tuple currentWall, maze_configuration* maze)
+{
+       maze->mazeGrid[currentWall.row][currentWall.column] = CELL;
+       maze->mazeGrid[cellToAdd.row][cellToAdd.column] = CELL;
+       addWallsToList(cellToAdd, maze);
+}
+
+static void
+removeWallFromList(unsigned index, maze_configuration* maze)
+{
+       unsigned i;
+       for (i = index + 1; i < maze->wallListSize; i++)
+               maze->wallList[i - 1] = maze->wallList[i];
+
+       maze->wallListSize--;
+}
+
+static void
+placeMiscObjects(maze_configuration* maze)
+{
+       Rat* rat;
+       Tuple temp;
+       unsigned char object;
+    unsigned numSurroundingWalls = 3;
+       unsigned i;
+
+       while (numSurroundingWalls >= 3) {
+               numSurroundingWalls = 0;
+               maze->startPosition = placeObject(maze, CELL);
+
+               object = maze->mazeGrid[maze->startPosition.row]
+                       [maze->startPosition.column + 1];
+               if (object == WALL || object == WALL_GL_REDBOOK)
+                       numSurroundingWalls++;
+               object = maze->mazeGrid[maze->startPosition.row - 1]
+                       [maze->startPosition.column];
+               if (object == WALL || object == WALL_GL_REDBOOK)
+                       numSurroundingWalls++;
+               object = maze->mazeGrid[maze->startPosition.row]
+                       [maze->startPosition.column - 1];
+               if (object == WALL || object == WALL_GL_REDBOOK)
+                       numSurroundingWalls++;
+               object = maze->mazeGrid[maze->startPosition.row + 1]
+                       [maze->startPosition.column];
+               if (object == WALL || object == WALL_GL_REDBOOK)
+                       numSurroundingWalls++;
+       }
+       maze->mazeGrid[maze->startPosition.row][maze->startPosition.column] = START;
+
+       if (maze->mazeGrid[maze->startPosition.row][maze->startPosition.column + 1]
+                       != WALL && maze->mazeGrid[maze->startPosition.row]
+                       [maze->startPosition.column + 1] != WALL_GL_REDBOOK) {
+               maze->camera.position.x = (maze->startPosition.column + 1) / 2.0;
+               maze->camera.position.z = maze->startPosition.row / 2.0;
+               maze->camera.rotation = WEST;
+       }
+       else if (maze->mazeGrid[maze->startPosition.row - 1]
+                       [maze->startPosition.column] != WALL
+                       && maze->mazeGrid[maze->startPosition.row - 1]
+                       [maze->startPosition.column] != WALL_GL_REDBOOK) {
+               maze->camera.position.x = maze->startPosition.column / 2.0;
+               maze->camera.position.z = (maze->startPosition.row - 1) / 2.0;
+               maze->camera.rotation = SOUTH;
+       }
+       else if (maze->mazeGrid[maze->startPosition.row]
+                       [maze->startPosition.column - 1] != WALL
+                       && maze->mazeGrid[maze->startPosition.row]
+                       [maze->startPosition.column - 1] != WALL_GL_REDBOOK) {
+               maze->camera.position.x = (maze->startPosition.column - 1) / 2.0;
+               maze->camera.position.z = maze->startPosition.row / 2.0;
+               maze->camera.rotation = EAST;
+       }
+       else {
+               maze->camera.position.x = maze->startPosition.column / 2.0;
+               maze->camera.position.z = (maze->startPosition.row + 1) / 2.0;
+               maze->camera.rotation = NORTH;
+       }
+
+       maze->finishPosition = placeObject(maze, FINISH);
+
+       for (i = 0; i < numInverters; i++)
+               maze->inverterPosition[i] =
+                       placeObject(maze, random() % 4 + INVERTER_TETRAHEDRON);
+
+       temp.row = 0;
+       temp.column = 0;
+
+# ifdef USE_FLOATING_IMAGES
+       for (i = 0; i < numGl3dTexts; i++)
+               maze->gl3dTextPosition[i] =
+                       placeObject(maze, GL_3D_TEXT);
+# endif
+
+       for (i = 0; i < numRats; i++) {
+               rat = &(maze->rats[i]);
+               temp = placeObject(maze, CELL);
+               rat->position.x = temp.column / 2.0;
+               rat->position.z = temp.row / 2.0;
+               rat->state = WALKING;
+
+               if (temp.row == 0 && temp.column == 0) {
+                       continue;
+               }
+
+               if (maze->mazeGrid[(int)(rat->position.z * 2)]
+                               [(int)(rat->position.x * 2) + 1]
+                               != WALL && maze->mazeGrid[(int)(rat->position.z * 2)]
+                               [(int)(rat->position.x * 2) + 1] != WALL_GL_REDBOOK)
+                       rat->rotation = EAST;
+               else if (maze->mazeGrid[(int)(rat->position.z * 2) - 1]
+                               [(int)(rat->position.x * 2)]
+                               != WALL && maze->mazeGrid[(int)(rat->position.z * 2) - 1]
+                               [(int)(rat->position.x * 2)] != WALL_GL_REDBOOK)
+                       rat->rotation = NORTH;
+               else if (maze->mazeGrid[(int)(rat->position.z * 2)]
+                               [(int)(rat->position.x * 2) - 1]
+                               != WALL && maze->mazeGrid[(int)(rat->position.z * 2)]
+                               [(int)(rat->position.x * 2) - 1] != WALL_GL_REDBOOK)
+                       rat->rotation = WEST;
+               else
+                       rat->rotation = SOUTH;
+       }
+
+# ifdef USE_FLOATING_IMAGES
+       for (i = 0; i < numGlRedbooks; i++) {
+               while (!(((isOdd(temp.row) && isEven(temp.column))
+                                       || (isEven(temp.row) && isOdd(temp.column)))
+                                       && maze->mazeGrid[temp.row][temp.column] == WALL)) {
+                       temp.row = random() % maze->numRows;
+                       temp.column = random() % maze->numColumns;
+               }
+
+               maze->mazeGrid[temp.row][temp.column] = WALL_GL_REDBOOK;
+       }
+# endif
+}
+
+static Tuple
+placeObject(maze_configuration* maze, unsigned char type)
+{
+       Tuple position = {0, 0};
+
+       while (!(maze->mazeGrid[position.row][position.column] == CELL
+                       && isOdd(position.row) && isOdd(position.column))) {
+               position.row = random() % maze->numRows;
+               position.column = random() % maze->numColumns;
+       }
+
+       maze->mazeGrid[position.row][position.column] = type;
+       return position;
+}
+
+ENTRYPOINT void
+reshape_maze (ModeInfo *mi, int width, int height)
+{
+       glViewport(0, 0, (GLint) width, (GLint) height);
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+
+ENTRYPOINT Bool
+maze_handle_event (ModeInfo *mi, XEvent *event)
+{
+  maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+  if (event->xany.type == ButtonPress)
+    {
+      maze->button_down_p = True;
+      return True;
+    }
+  else if (event->xany.type == ButtonRelease)
+    {
+      maze->button_down_p = False;
+      return True;
+    }
+  return False;
+}
+
+ENTRYPOINT void
+init_maze (ModeInfo * mi)
+{
+       unsigned i;
+       maze_configuration *maze;
+       GLfloat ambient[] = {0, 0, 0, 1},
+                       diffuse[] = {1, 1, 1, 1},
+                       position[] = {0, 2, 0, 0},
+                       mcolor[] = {1, 1, 1, 1};
+
+       MI_INIT(mi, mazes);
+       maze = &mazes[MI_SCREEN(mi)];
+
+       maze->glx_context = init_GL(mi);
+
+       reshape_maze(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+    for (i = 0; i < countof(maze->dlists); i++)
+      maze->dlists[i] = glGenLists (1);
+
+       maze->numRows = (numRows < 2 ? 5 : numRows * 2 + 1);
+       maze->numColumns = (numColumns < 2 ? 5 : numColumns * 2 + 1);
+
+       i = (maze->numRows / 2) * (maze->numColumns / 2) - 2;
+       if (i < numInverters) {
+               numInverters = i;
+               i = 0;
+       } else i -= numInverters;
+
+       if (i < numRats) {
+               numRats = i;
+               i = 0;
+       } else i -= numRats;
+# ifdef USE_FLOATING_IMAGES
+       if (i < numGl3dTexts) {
+               numGl3dTexts = i;
+               i = 0;
+       } else i -= numGl3dTexts;
+
+       if (((maze->numRows - 1) + (maze->numColumns - 1)
+                               + ((maze->numRows / 2 - 1) * (maze->numColumns / 2 - 1))) < maze->numGlRedbooks)
+               maze->numGlRedbooks = (maze->numRows - 1) + (maze->numColumns - 1)
+                               + ((maze->numRows / 2 - 1) * (maze->numColumns / 2 - 1));
+# endif
+
+       glEnable(GL_DEPTH_TEST);
+       glEnable(GL_TEXTURE_2D);
+       glEnable(GL_LIGHT0);
+
+       glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
+       glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
+       glLightfv(GL_LIGHT0, GL_POSITION, position);
+       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mcolor);
+
+       glShadeModel(GL_FLAT);
+
+       maze->mazeGrid = calloc(maze->numRows, sizeof(unsigned char*));
+       for (i = 0; i < maze->numRows; i++)
+               maze->mazeGrid[i] = calloc(maze->numColumns, sizeof(unsigned char));
+       maze->wallList = calloc(((maze->numColumns - 2) / 2) * ((maze->numRows - 2) / 2 + 1)
+                       + ((maze->numColumns - 2) / 2 + 1) * ((maze->numRows - 2) / 2), sizeof(Tuple));
+       maze->inverterPosition = calloc(numInverters, sizeof(Tuple));
+       maze->rats = calloc(numRats, sizeof(Rat));
+# ifdef USE_FLOATING_IMAGES
+       maze->gl3dTextPosition = calloc(numGl3dTexts, sizeof(Tuple));
+#endif
+
+       setup_textures(mi);
+
+       newMaze(maze);
+
+       constructLists(mi);
+       refreshRemainingDistanceToTravel(mi);
+
+       maze->camera.isCamera = 1;
+       for (i = 0; i < numRats; i++)
+               maze->rats[i].isCamera = 0;
+}
+
+static void
+newMaze(maze_configuration* maze)
+{
+       maze->camera.state = STARTING;
+       maze->camera.inversion = 0;
+       maze->wallHeight = 0;
+       maze->inverterRotation = 0;
+       maze->acidHue = 0;
+
+       initializeGrid(maze);
+       buildMaze(maze);
+       placeMiscObjects(maze);
+}
+
+static void
+constructLists(ModeInfo *mi)
+{
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+
+       glNewList(maze->dlists[ARROW], GL_COMPILE);
+               glBegin(GL_POLYGON);
+               glVertex2f(0, -0.25);
+               glVertex2f(0.146946313, 0.202254249);
+               glVertex2f(0, 0.125);
+               glVertex2f(-0.146946313, 0.202254249);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[SQUARE], GL_COMPILE);
+               glBegin(GL_QUADS);
+               glVertex2f(-0.176776695, -0.176776695);
+               glVertex2f(0.176776695, -0.176776695);
+               glVertex2f(0.176776695, 0.176776695);
+               glVertex2f(-0.176776695, 0.176776695);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[STAR], GL_COMPILE);
+               glBegin(GL_TRIANGLE_FAN);
+               glVertex2f(0, 0);
+               glVertex2f(0, -0.25);
+               glVertex2f(0.073473157, -0.101127124);
+               glVertex2f(0.237764129, -0.077254249);
+               glVertex2f(0.118882065, 0.038627124);
+               glVertex2f(0.146946313, 0.202254249);
+               glVertex2f(0, 0.125);
+               glVertex2f(-0.146946313, 0.202254249);
+               glVertex2f(-0.118882065, 0.038627124);
+               glVertex2f(-0.237764129, -0.077254249);
+               glVertex2f(-0.073473157, -0.101127124);
+               glVertex2f(0, -0.25);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[TRIANGLE], GL_COMPILE);
+               glBegin(GL_POLYGON);
+               glVertex2f(0, -0.25);
+               glVertex2f(0.216506351, 0.125);
+               glVertex2f(-0.216506351, 0.125);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[INVERTER_TETRAHEDRON], GL_COMPILE);
+               glBegin(GL_TRIANGLES);
+               glNormal3f(0.471404521, 0.816496581, 0.333333333);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(0.23570226, 0, -0.083333333);
+               glVertex3f(-0.11785113, 0.204124145, -0.083333333);
+
+               glNormal3f(-0.942809042, 0, 0.333333333);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(-0.11785113, 0.204124145, -0.083333333);
+               glVertex3f(-0.11785113, -0.204124145, -0.083333333);
+
+               glNormal3f(0.471404521, -0.816496581, 0.333333333);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(-0.11785113, -0.204124145, -0.083333333);
+               glVertex3f(0.23570226, 0, -0.083333333);
+
+               glNormal3f(0, 0, -1);
+               glVertex3f(0.23570226, 0, -0.083333333);
+               glVertex3f(-0.11785113, -0.204124145, -0.083333333);
+               glVertex3f(-0.11785113, 0.204124145, -0.083333333);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[INVERTER_OCTAHEDRON], GL_COMPILE);
+               glBegin(GL_TRIANGLES);
+               glNormal3f(0.577350269, 0.577350269, 0.577350269);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(0.25, 0, 0);
+               glVertex3f(0, 0.25, 0);
+
+               glNormal3f(-0.577350269, 0.577350269, 0.577350269);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(0, 0.25, 0);
+               glVertex3f(-0.25, 0, 0);
+
+               glNormal3f(-0.577350269, -0.577350269, 0.577350269);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(-0.25, 0, 0);
+               glVertex3f(0, -0.25, 0);
+
+               glNormal3f(0.577350269, -0.577350269, 0.577350269);
+               glVertex3f(0, 0, 0.25);
+               glVertex3f(0, -0.25, 0);
+               glVertex3f(0.25, 0, 0);
+
+               glNormal3f(0.577350269, -0.577350269, -0.577350269);
+               glVertex3f(0.25, 0, 0);
+               glVertex3f(0, -0.25, 0);
+               glVertex3f(0, 0, -0.25);
+
+               glNormal3f(0.577350269, 0.577350269, -0.577350269);
+               glVertex3f(0.25, 0, 0);
+               glVertex3f(0, 0, -0.25);
+               glVertex3f(0, 0.25, 0);
+
+               glNormal3f(-0.577350269, 0.577350269, -0.577350269);
+               glVertex3f(0, 0.25, 0);
+               glVertex3f(0, 0, -0.25);
+               glVertex3f(-0.25, 0, 0);
+
+               glNormal3f(-0.577350269, -0.577350269, -0.577350269);
+               glVertex3f(-0.25, 0, 0);
+               glVertex3f(0, 0, -0.25);
+               glVertex3f(0, -0.25, 0);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[INVERTER_DODECAHEDRON], GL_COMPILE);
+               glBegin(GL_POLYGON);
+               glNormal3f(0.000000000, 0.000000000, 1.000000000);
+               glVertex3f(0.122780868, 0.089205522, 0.198663618);
+               glVertex3f(-0.046898119, 0.144337567, 0.198663618);
+               glVertex3f(-0.151765500, 0.000000000, 0.198663618);
+               glVertex3f(-0.046898119, -0.144337567, 0.198663618);
+               glVertex3f(0.122780868, -0.089205522, 0.198663618);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(0.894427191, 0.000000000, 0.447213595);
+               glVertex3f(0.198663618, -0.144337567, 0.046898119);
+               glVertex3f(0.245561737, 0.000000000, -0.046898119);
+               glVertex3f(0.198663618, 0.144337567, 0.046898119);
+               glVertex3f(0.122780868, 0.089205522, 0.198663618);
+               glVertex3f(0.122780868, -0.089205522, 0.198663618);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(0.276393202, 0.850650808, 0.447213595);
+               glVertex3f(0.198663618, 0.144337567, 0.046898119);
+               glVertex3f(0.075882750, 0.233543090, -0.046898119);
+               glVertex3f(-0.075882750, 0.233543090, 0.046898119);
+               glVertex3f(-0.046898119, 0.144337567, 0.198663618);
+               glVertex3f(0.122780868, 0.089205522, 0.198663618);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(-0.723606798, 0.525731112, 0.447213595);
+               glVertex3f(-0.075882750, 0.233543090, 0.046898119);
+               glVertex3f(-0.198663618, 0.144337567, -0.046898119);
+               glVertex3f(-0.245561737, 0.000000000, 0.046898119);
+               glVertex3f(-0.151765500, 0.000000000, 0.198663618);
+               glVertex3f(-0.046898119, 0.144337567, 0.198663618);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(-0.723606798, -0.525731112, 0.447213595);
+               glVertex3f(-0.245561737, 0.000000000, 0.046898119);
+               glVertex3f(-0.198663618, -0.144337567, -0.046898119);
+               glVertex3f(-0.075882750, -0.233543090, 0.046898119);
+               glVertex3f(-0.046898119, -0.144337567, 0.198663618);
+               glVertex3f(-0.151765500, 0.000000000, 0.198663618);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(0.276393202, -0.850650808, 0.447213595);
+               glVertex3f(-0.075882750, -0.233543090, 0.046898119);
+               glVertex3f(0.075882750, -0.233543090, -0.046898119);
+               glVertex3f(0.198663618, -0.144337567, 0.046898119);
+               glVertex3f(0.122780868, -0.089205522, 0.198663618);
+               glVertex3f(-0.046898119, -0.144337567, 0.198663618);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(0.723606798, 0.525731112, -0.447213595);
+               glVertex3f(0.245561737, 0.000000000, -0.046898119);
+               glVertex3f(0.151765500, 0.000000000, -0.198663618);
+               glVertex3f(0.046898119, 0.144337567, -0.198663618);
+               glVertex3f(0.075882750, 0.233543090, -0.046898119);
+               glVertex3f(0.198663618, 0.144337567, 0.046898119);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(0.723606798, -0.525731112, -0.447213595);
+               glVertex3f(0.198663618, -0.144337567, 0.046898119);
+               glVertex3f(0.075882750, -0.233543090, -0.046898119);
+               glVertex3f(0.046898119, -0.144337567, -0.198663618);
+               glVertex3f(0.151765500, 0.000000000, -0.198663618);
+               glVertex3f(0.245561737, 0.000000000, -0.046898119);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(-0.276393202, 0.850650808, -0.447213595);
+               glVertex3f(0.075882750, 0.233543090, -0.046898119);
+               glVertex3f(0.046898119, 0.144337567, -0.198663618);
+               glVertex3f(-0.122780868, 0.089205522, -0.198663618);
+               glVertex3f(-0.198663618, 0.144337567, -0.046898119);
+               glVertex3f(-0.075882750, 0.233543090, 0.046898119);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(-0.894427191, 0.000000000, -0.447213595);
+               glVertex3f(-0.198663618, 0.144337567, -0.046898119);
+               glVertex3f(-0.122780868, 0.089205522, -0.198663618);
+               glVertex3f(-0.122780868, -0.089205522, -0.198663618);
+               glVertex3f(-0.198663618, -0.144337567, -0.046898119);
+               glVertex3f(-0.245561737, 0.000000000, 0.046898119);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(-0.276393202, -0.850650808, -0.447213595);
+               glVertex3f(-0.198663618, -0.144337567, -0.046898119);
+               glVertex3f(-0.122780868, -0.089205522, -0.198663618);
+               glVertex3f(0.046898119, -0.144337567, -0.198663618);
+               glVertex3f(0.075882750, -0.233543090, -0.046898119);
+               glVertex3f(-0.075882750, -0.233543090, 0.046898119);
+               glEnd();
+
+               glBegin(GL_POLYGON);
+               glNormal3f(0.000000000, 0.000000000, -1.000000000);
+               glVertex3f(0.046898119, -0.144337567, -0.198663618);
+               glVertex3f(-0.122780868, -0.089205522, -0.198663618);
+               glVertex3f(-0.122780868, 0.089205522, -0.198663618);
+               glVertex3f(0.046898119, 0.144337567, -0.198663618);
+               glVertex3f(0.151765500, 0.000000000, -0.198663618);
+               glEnd();
+       glEndList();
+
+       glNewList(maze->dlists[INVERTER_ICOSAHEDRON], GL_COMPILE);
+               glBegin(GL_TRIANGLES);
+               glNormal3f(0.491123473, 0.356822090, 0.794654473);
+               glVertex3f(0.000000000, 0.000000000, 0.250000000);
+               glVertex3f(0.223606798, 0.000000000, 0.111803399);
+               glVertex3f(0.069098301, 0.212662702, 0.111803399);
+
+               glNormal3f(-0.187592474, 0.577350269, 0.794654473);
+               glVertex3f(0.000000000, 0.000000000, 0.250000000);
+               glVertex3f(0.069098301, 0.212662702, 0.111803399);
+               glVertex3f(-0.180901699, 0.131432778, 0.111803399);
+
+               glNormal3f(-0.607061998, 0.000000000, 0.794654473);
+               glVertex3f(0.000000000, 0.000000000, 0.250000000);
+               glVertex3f(-0.180901699, 0.131432778, 0.111803399);
+               glVertex3f(-0.180901699, -0.131432778, 0.111803399);
+
+               glNormal3f(-0.187592474, -0.577350269, 0.794654473);
+               glVertex3f(0.000000000, 0.000000000, 0.250000000);
+               glVertex3f(-0.180901699, -0.131432778, 0.111803399);
+               glVertex3f(0.069098301, -0.212662702, 0.111803399);
+
+               glNormal3f(0.491123473, -0.356822090, 0.794654473);
+               glVertex3f(0.000000000, 0.000000000, 0.250000000);
+               glVertex3f(0.069098301, -0.212662702, 0.111803399);
+               glVertex3f(0.223606798, 0.000000000, 0.111803399);
+
+               glNormal3f(0.794654473, -0.577350269, 0.187592474);
+               glVertex3f(0.223606798, 0.000000000, 0.111803399);
+               glVertex3f(0.069098301, -0.212662702, 0.111803399);
+               glVertex3f(0.180901699, -0.131432778, -0.111803399);
+
+               glNormal3f(0.982246947, 0.000000000, -0.187592474);
+               glVertex3f(0.223606798, 0.000000000, 0.111803399);
+               glVertex3f(0.180901699, -0.131432778, -0.111803399);
+               glVertex3f(0.180901699, 0.131432778, -0.111803399);
+
+               glNormal3f(0.794654473, 0.577350269, 0.187592474);
+               glVertex3f(0.223606798, 0.000000000, 0.111803399);
+               glVertex3f(0.180901699, 0.131432778, -0.111803399);
+               glVertex3f(0.069098301, 0.212662702, 0.111803399);
+
+               glNormal3f(0.303530999, 0.934172359, -0.187592474);
+               glVertex3f(0.069098301, 0.212662702, 0.111803399);
+               glVertex3f(0.180901699, 0.131432778, -0.111803399);
+               glVertex3f(-0.069098301, 0.212662702, -0.111803399);
+
+               glNormal3f(-0.303530999, 0.934172359, 0.187592474);
+               glVertex3f(0.069098301, 0.212662702, 0.111803399);
+               glVertex3f(-0.069098301, 0.212662702, -0.111803399);
+               glVertex3f(-0.180901699, 0.131432778, 0.111803399);
+
+               glNormal3f(-0.794654473, 0.577350269, -0.187592474);
+               glVertex3f(-0.180901699, 0.131432778, 0.111803399);
+               glVertex3f(-0.069098301, 0.212662702, -0.111803399);
+               glVertex3f(-0.223606798, 0.000000000, -0.111803399);
+
+               glNormal3f(-0.982246947, 0.000000000, 0.187592474);
+               glVertex3f(-0.180901699, 0.131432778, 0.111803399);
+               glVertex3f(-0.223606798, 0.000000000, -0.111803399);
+               glVertex3f(-0.180901699, -0.131432778, 0.111803399);
+
+               glNormal3f(-0.794654473, -0.577350269, -0.187592474);
+               glVertex3f(-0.180901699, -0.131432778, 0.111803399);
+               glVertex3f(-0.223606798, 0.000000000, -0.111803399);
+               glVertex3f(-0.069098301, -0.212662702, -0.111803399);
+
+               glNormal3f(-0.303530999, -0.934172359, 0.187592474);
+               glVertex3f(-0.180901699, -0.131432778, 0.111803399);
+               glVertex3f(-0.069098301, -0.212662702, -0.111803399);
+               glVertex3f(0.069098301, -0.212662702, 0.111803399);
+
+               glNormal3f(0.303530999, -0.934172359, -0.187592474);
+               glVertex3f(0.069098301, -0.212662702, 0.111803399);
+               glVertex3f(-0.069098301, -0.212662702, -0.111803399);
+               glVertex3f(0.180901699, -0.131432778, -0.111803399);
+
+               glNormal3f(0.607061998, 0.000000000, -0.794654473);
+               glVertex3f(0.180901699, 0.131432778, -0.111803399);
+               glVertex3f(0.180901699, -0.131432778, -0.111803399);
+               glVertex3f(0.000000000, 0.000000000, -0.250000000);
+
+               glNormal3f(0.187592474, 0.577350269, -0.794654473);
+               glVertex3f(0.180901699, 0.131432778, -0.111803399);
+               glVertex3f(0.000000000, 0.000000000, -0.250000000);
+               glVertex3f(-0.069098301, 0.212662702, -0.111803399);
+
+               glNormal3f(0.187592474, -0.577350269, -0.794654473);
+               glVertex3f(0.180901699, -0.131432778, -0.111803399);
+               glVertex3f(-0.069098301, -0.212662702, -0.111803399);
+               glVertex3f(0.000000000, 0.000000000, -0.250000000);
+
+               glNormal3f(-0.491123473, 0.356822090, -0.794654473);
+               glVertex3f(-0.069098301, 0.212662702, -0.111803399);
+               glVertex3f(0.000000000, 0.000000000, -0.250000000);
+               glVertex3f(-0.223606798, 0.000000000, -0.111803399);
+
+               glNormal3f(-0.491123473, -0.356822090, -0.794654473);
+               glVertex3f(-0.223606798, 0.000000000, -0.111803399);
+               glVertex3f(0.000000000, 0.000000000, -0.250000000);
+               glVertex3f(-0.069098301, -0.212662702, -0.111803399);
+               glEnd();
+       glEndList();
+}
+
+ENTRYPOINT void
+draw_maze (ModeInfo * mi)
+{
+       unsigned i;
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+       GLfloat h = (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi);
+
+    if (!maze->glx_context)
+      return;
+       glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(maze->glx_context));
+
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+       gluPerspective(90, 1/h, 0.05, 100);
+
+    if (MI_WIDTH(mi) > 2560)  /* Retina displays */
+      glLineWidth (6);
+    else
+# ifdef HAVE_MOBILE
+      glLineWidth (4);
+# else
+      glLineWidth (2);
+# endif
+
+       glRotatef(maze->camera.inversion, 0, 0, 1);
+       glRotatef(maze->camera.rotation, 0, 1, 0);
+       glTranslatef(-1 * maze->camera.position.x, -0.5,
+                       -1 * maze->camera.position.z);
+
+       refreshRemainingDistanceToTravel(mi);
+
+       updateInverterRotation(maze);
+       shiftAcidColor(maze);
+       step(&maze->camera, maze);
+
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+       drawWalls(mi);
+       drawCeiling(mi);
+       drawFloor(mi);
+
+       for (i = 0; i < numInverters; i++)
+               drawInverter(maze, maze->inverterPosition[i]);
+
+# ifdef USE_FLOATING_IMAGES
+       for (i = 0; i < numGl3dTexts; i++)
+               drawPane(mi, maze->gl3dTextTexture, maze->gl3dTextPosition[i]);
+# endif
+
+       for (i = 0; i < numRats; i++) {
+               step(&maze->rats[i], maze);
+               drawRat(maze->rats[i].position, maze);
+       }
+
+       drawPane(mi, maze->finishTexture, maze->finishPosition);
+       drawPane(mi, maze->startTexture, maze->startPosition);
+
+       if (shouldDrawOverlay || maze->button_down_p)
+      drawOverlay(mi);
+
+       if (mi->fps_p) do_fps(mi);
+    glFinish();
+       glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
+}
+
+static void
+shiftAcidColor(maze_configuration* maze)
+{
+       GLfloat x = 1 - fabs(fmod(maze->acidHue / 60.0, 2) - 1);
+
+       if (0 <= maze->acidHue && maze->acidHue <= 60) {
+               maze->acidColor.red = 1;
+               maze->acidColor.green = x;
+               maze->acidColor.blue = 0;
+       } else if (60 <= maze->acidHue && maze->acidHue <= 120) {
+               maze->acidColor.red = x;
+               maze->acidColor.green = 1;
+               maze->acidColor.blue = 0;
+       } else if (120 <= maze->acidHue && maze->acidHue <= 180) {
+               maze->acidColor.red = 0;
+               maze->acidColor.green = 1;
+               maze->acidColor.blue = x;
+       } else if (180 <= maze->acidHue && maze->acidHue <= 240) {
+               maze->acidColor.red = 0;
+               maze->acidColor.green = x;
+               maze->acidColor.blue = 1;
+       } else if (240 <= maze->acidHue && maze->acidHue <= 300) {
+               maze->acidColor.red = x;
+               maze->acidColor.green = 0;
+               maze->acidColor.blue = 1;
+       } else {
+               maze->acidColor.red = 1;
+               maze->acidColor.green = 0;
+               maze->acidColor.blue = x;
+       }
+
+       maze->acidHue += 75 * maze->camera.remainingDistanceToTravel;
+       if (maze->acidHue >= 360) maze->acidHue -= 360;
+}
+
+static void
+refreshRemainingDistanceToTravel(ModeInfo * mi)
+{
+       unsigned i;
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+       maze->camera.remainingDistanceToTravel
+               = speed * 1.6 * (MI_DELAY(mi) / 1000000.0);
+       for (i = 0; i < numRats; i++)
+               maze->rats[i].remainingDistanceToTravel =
+                       maze->camera.remainingDistanceToTravel;
+}
+
+static void
+step(Rat* rat, maze_configuration* maze)
+{
+       GLfloat previousWallHeight = maze->wallHeight;
+
+       if (!rat->isCamera && (maze->wallHeight < 1
+                               || (rat->position.x == 0 && rat->position.z == 0)))
+               return;
+
+       while (rat->remainingDistanceToTravel > 0) {
+               switch(rat->state) {
+                       case WALKING:
+                               switch((int)rat->rotation) {
+                                       case NORTH:
+                                               walk(rat, 'z', -1, maze);
+                                               break;
+                                       case EAST:
+                                               walk(rat, 'x', 1, maze);
+                                               break;
+                                       case SOUTH:
+                                               walk(rat, 'z', 1, maze);
+                                               break;
+                                       case WEST:
+                                               walk(rat, 'x', -1, maze);
+                                               break;
+                                       default:
+                                               rat->rotation = 90 * roundf(rat->rotation / 90.0);
+                                               break;
+                               }
+                               break;
+                       case TURNING_LEFT:
+                               turn(rat, maze);
+                               break;
+                       case TURNING_RIGHT:
+                               turn(rat, maze);
+                               break;
+                       case TURNING_AROUND:
+                               turnAround(rat, maze);
+                               break;
+                       case INVERTING:
+                               invert(maze);
+                               break;
+                       case STARTING:
+                               maze->wallHeight += 0.48 * rat->remainingDistanceToTravel;
+                               if (maze->wallHeight > 1.0) {
+                                       maze->wallHeight = 1.0;
+                                       rat->remainingDistanceToTravel =
+                                               fabs(previousWallHeight - maze->wallHeight);
+                                       changeState(&maze->camera, maze);
+                               } else
+                                       rat->remainingDistanceToTravel = 0;
+                               break;
+                       case FINISHING:
+                               if (maze->wallHeight == 0) {
+                                       newMaze(maze);
+                                       rat->remainingDistanceToTravel = 0;
+                               }
+                               else if (maze->wallHeight < 0) {
+                                       maze->wallHeight = 0;
+                                       rat->remainingDistanceToTravel =
+                                               fabs(previousWallHeight - maze->wallHeight);
+                               }
+                               else {
+                                       maze->wallHeight -= 0.48 * rat->remainingDistanceToTravel;
+                                       rat->remainingDistanceToTravel = 0;
+                               }
+                               break;
+                       default:
+                               break;
+               }
+       }
+}
+
+static void
+walk(Rat* rat, char axis, int sign, maze_configuration* maze)
+{
+       GLfloat* component = (axis == 'x' ? &rat->position.x : &rat->position.z);
+       GLfloat previousPosition = *component;
+       int isMultipleOfOneHalf = 0;
+       unsigned temp = (unsigned)((*component) * 2.0);
+
+       if (((*component) * 2) == roundf((*component) * 2))
+               isMultipleOfOneHalf = 1;
+       *component += sign * rat->remainingDistanceToTravel;
+
+       if (!isMultipleOfOneHalf && ((unsigned)((*component) * 2.0)) != temp) {
+               *component = roundToNearestHalf(*component);
+               rat->remainingDistanceToTravel -=
+                       fabs((*component) - previousPosition);
+               changeState(rat, maze);
+       } else
+               rat->remainingDistanceToTravel = 0;
+       
+}
+
+static void
+turn(Rat* rat, maze_configuration* maze)
+{
+       Tuplef rotatingAround;
+       GLfloat tangentVectorDirection, previousRotation
+               = rat->rotation;
+
+       if (rat->state == TURNING_LEFT) {
+               tangentVectorDirection = rat->rotation * (M_PI / 180) + M_PI;
+               rotatingAround.x = roundToNearestHalf(rat->position.x
+                               + 0.5 * cos(tangentVectorDirection));
+               rotatingAround.z = roundToNearestHalf(rat->position.z
+                               + 0.5 * sin(tangentVectorDirection));
+
+               rat->rotation -= DEF_ANGULAR_CONVERSION_FACTOR
+                       * rat->remainingDistanceToTravel;
+
+               if (previousRotation > WEST && rat->rotation <= WEST) {
+                       rat->rotation = WEST;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else if (previousRotation > SOUTH && rat->rotation <= SOUTH) {
+                       rat->rotation = SOUTH;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else if (previousRotation > EAST && rat->rotation <= EAST) {
+                       rat->rotation = EAST;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else if (previousRotation > NORTH && rat->rotation <= NORTH) {
+                       rat->rotation = NORTH;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else
+                       rat->remainingDistanceToTravel = 0;
+
+               tangentVectorDirection = rat->rotation * (M_PI / 180);
+       }
+       else {
+               tangentVectorDirection = rat->rotation * (M_PI / 180);
+               rotatingAround.x = roundToNearestHalf(rat->position.x
+                               + 0.5 * cos(tangentVectorDirection));
+               rotatingAround.z = roundToNearestHalf(rat->position.z
+                               + 0.5 * sin(tangentVectorDirection));
+
+               rat->rotation += DEF_ANGULAR_CONVERSION_FACTOR
+                       * rat->remainingDistanceToTravel;
+
+               if (rat->rotation >= 360) {
+                       rat->rotation = NORTH;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - 360);
+               } else if (previousRotation < WEST && rat->rotation >= WEST) {
+                       rat->rotation = WEST;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else if (previousRotation < SOUTH && rat->rotation >= SOUTH) {
+                       rat->rotation = SOUTH;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else if (previousRotation < EAST && rat->rotation >= EAST) {
+                       rat->rotation = EAST;
+                       rat->remainingDistanceToTravel -= (M_PI / 180)
+                               * fabs(previousRotation - rat->rotation);
+               } else
+                       rat->remainingDistanceToTravel = 0;
+
+               tangentVectorDirection = rat->rotation * (M_PI / 180) + M_PI;
+       }
+
+       rat->position.x = rotatingAround.x + 0.5 * cos(tangentVectorDirection);
+       rat->position.z = rotatingAround.z + 0.5 * sin(tangentVectorDirection);
+
+       if (rat->rotation < 0)
+               rat->rotation += 360;
+
+       if (rat->rotation == NORTH || rat->rotation == EAST
+                       || rat->rotation == SOUTH || rat->rotation == WEST) {
+               rat->position.x = roundToNearestHalf(rat->position.x);
+               rat->position.z = roundToNearestHalf(rat->position.z);
+               changeState(rat, maze);
+       }
+}
+
+static void
+turnAround(Rat* rat, maze_configuration* maze)
+{
+       GLfloat previousRotation = rat->rotation;
+
+       rat->rotation -= 1.5 * DEF_ANGULAR_CONVERSION_FACTOR
+               * rat->remainingDistanceToTravel;
+
+       if (previousRotation > rat->desiredRotation
+                       && rat->rotation <= rat->desiredRotation) {
+               rat->rotation = rat->desiredRotation;
+               rat->remainingDistanceToTravel -= (M_PI / 180)
+                       * fabs(previousRotation - rat->rotation);
+               changeState(rat, maze);
+       }
+       else {
+               rat->remainingDistanceToTravel = 0;
+               if (rat->rotation < 0) rat->rotation += 360;
+       }
+}
+
+static void
+invert(maze_configuration* maze)
+{
+       GLfloat previousInversion = maze->camera.inversion;
+       int shouldChangeState = 0;
+       unsigned cameraX = (unsigned)roundf(maze->camera.position.x * 2),
+                        cameraZ = (unsigned)roundf(maze->camera.position.z * 2);
+
+       maze->camera.inversion += 1.5 * DEF_ANGULAR_CONVERSION_FACTOR
+               * maze->camera.remainingDistanceToTravel;
+       if (previousInversion < 180 && maze->camera.inversion >= 180) {
+               maze->camera.inversion = 180;
+               maze->camera.remainingDistanceToTravel -= (M_PI / 180)
+                       * fabs(previousInversion - maze->camera.inversion);
+               shouldChangeState = 1;
+       }
+       else if (maze->camera.inversion >= 360) {
+               maze->camera.inversion = 0;
+               maze->camera.remainingDistanceToTravel -= (M_PI / 180)
+                       * fabs(previousInversion - maze->camera.inversion);
+               shouldChangeState = 1;
+       } else
+               maze->camera.remainingDistanceToTravel = 0;
+
+       if (shouldChangeState) {
+               switch ((int)maze->camera.rotation) {
+                       case NORTH:
+                               maze->mazeGrid[cameraZ - 1][cameraX] = CELL;
+                               break;
+                       case EAST:
+                               maze->mazeGrid[cameraZ][cameraX + 1] = CELL;
+                               break;
+                       case SOUTH:
+                               maze->mazeGrid[cameraZ + 1][cameraX] = CELL;
+                               break;
+                       case WEST:
+                               maze->mazeGrid[cameraZ][cameraX - 1] = CELL;
+                               break;
+                       default:
+                               break;
+               }
+
+               changeState(&maze->camera, maze);
+       }
+}
+
+static void
+changeState(Rat* rat, maze_configuration* maze)
+{
+       unsigned char inFrontOfRat, toTheLeft, straightAhead, toTheRight;
+       unsigned ratX = (unsigned)roundf(rat->position.x * 2),
+                        ratZ = (unsigned)roundf(rat->position.z * 2);
+
+       switch ((int)rat->rotation) {
+               case NORTH:
+                       inFrontOfRat = maze->mazeGrid[ratZ - 1][ratX];
+                       toTheLeft = maze->mazeGrid[ratZ - 1][ratX - 1];
+                       straightAhead = maze->mazeGrid[ratZ - 2][ratX];
+                       toTheRight = maze->mazeGrid[ratZ - 1][ratX + 1];
+                       break;
+               case EAST:
+                       inFrontOfRat = maze->mazeGrid[ratZ][ratX + 1];
+                       toTheLeft = maze->mazeGrid[ratZ - 1][ratX + 1];
+                       straightAhead = maze->mazeGrid[ratZ][ratX + 2];
+                       toTheRight = maze->mazeGrid[ratZ + 1][ratX + 1];
+                       break;
+               case SOUTH:
+                       inFrontOfRat = maze->mazeGrid[ratZ + 1][ratX];
+                       toTheLeft = maze->mazeGrid[ratZ + 1][ratX + 1];
+                       straightAhead = maze->mazeGrid[ratZ + 2][ratX];
+                       toTheRight = maze->mazeGrid[ratZ + 1][ratX - 1];
+                       break;
+               case WEST:
+                       inFrontOfRat = maze->mazeGrid[ratZ][ratX - 1];
+                       toTheLeft = maze->mazeGrid[ratZ + 1][ratX - 1];
+                       straightAhead = maze->mazeGrid[ratZ][ratX - 2];
+                       toTheRight = maze->mazeGrid[ratZ - 1][ratX - 1];
+                       break;
+               default:
+                       inFrontOfRat = toTheLeft = straightAhead = toTheRight = CELL;
+                       break;
+       }
+
+       if (rat->isCamera && inFrontOfRat == FINISH)
+               rat->state = FINISHING;
+       else if (rat->isCamera && inFrontOfRat >= INVERTER_TETRAHEDRON
+                       && inFrontOfRat <= INVERTER_ICOSAHEDRON)
+               rat->state = INVERTING;
+       else if (toTheLeft != WALL && toTheLeft != WALL_GL_REDBOOK)
+               rat->state = TURNING_LEFT;
+       else if (straightAhead != WALL && straightAhead != WALL_GL_REDBOOK)
+               rat->state = WALKING;
+       else if (toTheRight != WALL && toTheRight != WALL_GL_REDBOOK)
+               rat->state = TURNING_RIGHT;
+       else {
+               rat->state = TURNING_AROUND;
+
+               switch ((int)rat->rotation) {
+                       case NORTH:
+                               rat->desiredRotation = SOUTH;
+                               break;
+                       case EAST:
+                               rat->desiredRotation = WEST;
+                               break;
+                       case SOUTH:
+                               rat->desiredRotation = NORTH;
+                               break;
+                       case WEST:
+                               rat->desiredRotation = EAST;
+                               break;
+                       default:
+                               break;
+               }
+       }
+}
+
+static void
+updateInverterRotation(maze_configuration* maze)
+{
+       maze->inverterRotation += 45 * maze->camera.remainingDistanceToTravel;
+}
+
+static void drawInverter(maze_configuration* maze, Tuple coordinates)
+{
+       unsigned char type = maze->mazeGrid[coordinates.row][coordinates.column];
+
+       if (maze->wallHeight < 1 ||
+                       type < INVERTER_TETRAHEDRON || type > INVERTER_ICOSAHEDRON
+                       || (coordinates.row == 0 && coordinates.column == 0))
+               return;
+
+       glEnable(GL_LIGHTING);
+       glEnable(GL_CULL_FACE);
+
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+
+       glTranslatef(coordinates.column / 2.0, 0.25, coordinates.row / 2.0);
+       glRotatef(0.618033989 * maze->inverterRotation, 0, 1, 0);
+       glRotatef(maze->inverterRotation, 1, 0, 0);
+
+    if (type >= countof(maze->dlists)) abort();
+       glCallList(maze->dlists[type]);
+
+       glPopMatrix();
+
+       glDisable(GL_LIGHTING);
+       glDisable(GL_CULL_FACE);
+}
+
+static void
+drawWalls(ModeInfo * mi)
+{
+       unsigned i, j;
+       Tuple startCoordinates, endCoordinates;
+
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+
+       for (i = 0; i < maze->numRows; i++) {
+               for (j = 0; j < maze->numColumns; j++) {
+                       if (maze->mazeGrid[i][j] == WALL
+                                       || maze->mazeGrid[i][j] == WALL_GL_REDBOOK) {
+                               if (maze->mazeGrid[i][j] == WALL) {
+                                       glBindTexture(GL_TEXTURE_2D, maze->wallTexture);
+                                       if (dropAcid || dropAcidWalls)
+                                               glColor3f(maze->acidColor.red, maze->acidColor.green,
+                                                               maze->acidColor.blue);
+# ifdef USE_FLOATING_IMAGES
+                               } else {
+                                       glBindTexture(GL_TEXTURE_2D, maze->glTextbookTexture);
+                                       glColor3f(1, 1, 1);
+# endif
+                               }
+
+                               if (isOdd(i) && isEven(j)) {
+                                       startCoordinates.row = i / 2;
+                                       startCoordinates.column = j / 2;
+                                       endCoordinates.row = i / 2 + 1;
+                                       endCoordinates.column = j / 2;
+                                       drawWall(startCoordinates, endCoordinates, maze);
+                               } else if (isEven(i) && isOdd(j)) {
+                                       startCoordinates.row = i / 2;
+                                       startCoordinates.column = j / 2;
+                                       endCoordinates.row = i / 2;
+                                       endCoordinates.column = j / 2 + 1;
+                                       drawWall(startCoordinates, endCoordinates, maze);
+                               }
+                       }
+               }
+       }
+
+       glBindTexture(GL_TEXTURE_2D, 0);
+       glColor3f(1, 1, 1);
+}
+
+static void
+drawWall(Tuple startCoordinates, Tuple endCoordinates, maze_configuration* maze)
+{
+       GLfloat wallHeight = maze->wallHeight;
+
+       glBegin(GL_QUADS);
+               if (startCoordinates.row == endCoordinates.row) {
+                       glTexCoord2f(0, 0);
+                       glVertex3f(startCoordinates.column, 0, startCoordinates.row);
+                       glTexCoord2f(1, 0);
+                       glVertex3f(endCoordinates.column, 0, startCoordinates.row);
+                       glTexCoord2f(1, 1);
+                       glVertex3f(endCoordinates.column, wallHeight, endCoordinates.row);
+                       glTexCoord2f(0, 1);
+                       glVertex3f(startCoordinates.column, wallHeight, endCoordinates.row);
+               } else {
+                       glTexCoord2f(0, 0);
+                       glVertex3f(startCoordinates.column, 0, startCoordinates.row);
+                       glTexCoord2f(1, 0);
+                       glVertex3f(startCoordinates.column, 0, endCoordinates.row);
+                       glTexCoord2f(1, 1);
+                       glVertex3f(endCoordinates.column, wallHeight, endCoordinates.row);
+                       glTexCoord2f(0, 1);
+                       glVertex3f(endCoordinates.column, wallHeight, startCoordinates.row);
+               }
+       glEnd();
+}
+
+static void
+drawCeiling(ModeInfo * mi)
+{
+       Tuple farRightCorner;
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+       farRightCorner.row = maze->numRows / 2;
+       farRightCorner.column = maze->numColumns / 2;
+       glBindTexture(GL_TEXTURE_2D, maze->ceilingTexture);
+
+       glBegin(GL_QUADS);
+               if (dropAcid || dropAcidCeiling)
+                       glColor3f(maze->acidColor.red, maze->acidColor.green,
+                                       maze->acidColor.blue);
+               glTexCoord2f(0, 0);
+               glVertex3f(0, 1, 0);
+               glTexCoord2f(farRightCorner.column, 0);
+               glVertex3f(farRightCorner.column, 1, 0);
+               glTexCoord2f(farRightCorner.column, farRightCorner.row);
+               glVertex3f(farRightCorner.column, 1, farRightCorner.row);
+               glTexCoord2f(0, farRightCorner.row);
+               glVertex3f(0, 1, farRightCorner.row);
+               glColor3f(1, 1, 1);
+       glEnd();
+
+       glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+static void
+drawFloor(ModeInfo * mi)
+{
+       Tuple farRightCorner;
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+       farRightCorner.row = maze->numRows / 2;
+       farRightCorner.column = maze->numColumns / 2;
+       glBindTexture(GL_TEXTURE_2D, maze->floorTexture);
+
+       glBegin(GL_QUADS);
+               if (dropAcid || dropAcidFloor)
+                       glColor3f(maze->acidColor.red, maze->acidColor.green,
+                                       maze->acidColor.blue);
+               glTexCoord2f(0, 0);
+               glVertex3f(0, 0, 0);
+               glTexCoord2f(farRightCorner.column, 0);
+               glVertex3f(farRightCorner.column, 0, 0);
+               glTexCoord2f(farRightCorner.column, farRightCorner.row);
+               glVertex3f(farRightCorner.column, 0, farRightCorner.row);
+               glTexCoord2f(0, farRightCorner.row);
+               glVertex3f(0, 0, farRightCorner.row);
+               glColor3f(1, 1, 1);
+       glEnd();
+
+       glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+static void
+drawPane(ModeInfo *mi, GLuint texture, Tuple position)
+{
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+       if (position.row == 0 && position.column == 0) return;
+
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+       glBindTexture(GL_TEXTURE_2D, texture);
+
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+       glTranslatef(position.column / 2.0, 0, position.row / 2.0);
+       glRotatef(-1 * maze->camera.rotation - 90, 0, 1, 0);
+
+    if (MI_WIDTH(mi) < MI_HEIGHT(mi))
+      {
+        /* Keep the Start button readable in phone portrait mode. */
+        glScalef (0.5, 0.5, 0.5);
+        glTranslatef (0, 0.5, 0);
+      }
+
+       glBegin(GL_QUADS);
+               glColor4f(1, 1, 1, 0.9);
+               glTexCoord2f(0, 0);
+               glVertex3f(0, 0, 0.5);
+               glTexCoord2f(1, 0);
+               glVertex3f(0, 0, -0.5);
+               glTexCoord2f(1, 1);
+               glVertex3f(0, maze->wallHeight, -0.5);
+               glTexCoord2f(0, 1);
+               glVertex3f(0, maze->wallHeight, 0.5);
+               glColor3f(1, 1, 1);
+       glEnd();
+
+       glPopMatrix();
+       glBindTexture(GL_TEXTURE_2D, 0);
+       glDisable(GL_BLEND);
+}
+
+static void
+drawRat(Tuplef position, maze_configuration* maze)
+{
+       if (position.x == 0 && position.z == 0) return;
+
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+       glBindTexture(GL_TEXTURE_2D, maze->ratTexture);
+
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+       glTranslatef(position.x, 0, position.z);
+       glRotatef(-1 * maze->camera.rotation - 90, 0, 1, 0);
+
+    glScalef (0.25, 0.25, 0.25);
+       glBegin(GL_QUADS);
+               glTexCoord2f(0, 0);
+               glVertex3f(0, 0, 0.5);
+               glTexCoord2f(1, 0);
+               glVertex3f(0, 0, -0.5);
+               glTexCoord2f(1, 1);
+               glVertex3f(0, maze->wallHeight, -0.5);
+               glTexCoord2f(0, 1);
+               glVertex3f(0, maze->wallHeight, 0.5);
+       glEnd();
+
+       glPopMatrix();
+       glBindTexture(GL_TEXTURE_2D, 0);
+       glDisable(GL_BLEND);
+}
+
+
+static void drawOverlay(ModeInfo *mi)
+{
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+       unsigned i, j;
+    GLfloat h = (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi);
+
+       glMatrixMode(GL_PROJECTION);
+       glPushMatrix();
+       glLoadIdentity();
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+       glLoadIdentity();
+    glOrtho(-1/h, 1/h, 1, -1, -1, 1);
+
+       glEnable(GL_BLEND);
+       glColor4f(0, 0, 1, 0.75);
+       glScalef(0.25, 0.25, 0.25);
+
+       glCallList(maze->dlists[ARROW]);
+
+       glRotatef(maze->camera.inversion, 0, 1, 0);
+       glRotatef(maze->camera.rotation, 0, 0, -1);
+       glTranslatef(-maze->camera.position.x, -maze->camera.position.z, 0);
+       glColor4f(1, 1, 1, 0.75);
+       
+       glBegin(GL_LINES);
+       for (i = 0; i < maze->numRows; i++) {
+               for (j = 0; j < maze->numColumns; j++) {
+                       if (maze->mazeGrid[i][j] == WALL
+                                       || maze->mazeGrid[i][j] == WALL_GL_REDBOOK) {
+                               if (isOdd(i) && isEven(j)) {
+                                               glVertex2f(j / 2, i / 2);
+                                               glVertex2f(j / 2, i / 2 + 1);
+                               } else if (isEven(i) && isOdd(j)) {
+                                               glVertex2f(j / 2, i / 2);
+                                               glVertex2f(j / 2 + 1, i / 2);
+                               }
+                       }
+               }
+       }
+       glEnd();
+
+       glColor4f(1, 0, 0, 0.75);
+
+       glPushMatrix();
+       glTranslatef(maze->startPosition.column / 2.0,
+                       maze->startPosition.row / 2.0, 0);
+       glCallList(maze->dlists[SQUARE]);
+       glPopMatrix();
+
+       glColor4f(1, 1, 0, 0.75);
+
+       glPushMatrix();
+       glTranslatef(maze->finishPosition.column / 2.0,
+                       maze->finishPosition.row / 2.0, 0);
+       glCallList(maze->dlists[STAR]);
+       glPopMatrix();
+
+       glColor4f(1, 0.607843137, 0, 0.75);
+
+       for (i = 0; i < numRats; i++) {
+               if (maze->rats[i].position.x == 0 && maze->rats[i].position.z == 0)
+                       continue;
+               glPushMatrix();
+               glTranslatef(maze->rats[i].position.x, maze->rats[i].position.z, 0);
+               glRotatef(maze->rats[i].rotation, 0, 0, 1);
+               glCallList(maze->dlists[ARROW]);
+               glPopMatrix();
+       }
+
+       glColor4f(1, 1, 1, 1);
+
+       for (i = 0; i < numInverters; i++) {
+               j = maze->mazeGrid[maze->inverterPosition[i].row]
+                       [maze->inverterPosition[i].column];
+               if (j >= INVERTER_TETRAHEDRON && j <= INVERTER_ICOSAHEDRON) {
+                       glPushMatrix();
+                       glTranslatef(maze->inverterPosition[i].column / 2.0,
+                                       maze->inverterPosition[i].row / 2.0, 0);
+                       glRotatef(1.5 * maze->inverterRotation, 0, 0, 1);
+                       glCallList(maze->dlists[TRIANGLE]);
+                       glPopMatrix();
+               }
+       }
+
+       glDisable(GL_BLEND);
+
+       glPopMatrix();
+       glMatrixMode(GL_PROJECTION);
+       glPopMatrix();
+}
+
+ENTRYPOINT void
+free_maze (ModeInfo * mi)
+{
+       maze_configuration *maze = &mazes[MI_SCREEN(mi)];
+
+    glDeleteTextures(1, &maze->wallTexture);
+    glDeleteTextures(1, &maze->floorTexture);
+    glDeleteTextures(1, &maze->ceilingTexture);
+    glDeleteTextures(1, &maze->startTexture);
+    glDeleteTextures(1, &maze->ratTexture);
+    glDeleteTextures(1, &maze->finishTexture);
+# ifdef USE_FLOATING_IMAGES
+    glDeleteTextures(1, &maze->glTextbookTexture);
+    glDeleteTextures(1, &maze->gl3dTextTexture);
+# endif
+# ifdef USE_FRACTAL_IMAGES
+    glDeleteTextures(1, &maze->fractal1Texture);
+    glDeleteTextures(1, &maze->fractal2Texture);
+    glDeleteTextures(1, &maze->fractal3Texture);
+    glDeleteTextures(1, &maze->fractal4Texture);
+# endif
+
+    glDeleteLists(maze->dlists[ARROW], 4);
+    glDeleteLists(maze->dlists[INVERTER_TETRAHEDRON], 4);
+
+    free(maze->mazeGrid);
+    free(maze->wallList);
+    free(maze->inverterPosition);
+    free(maze->gl3dTextPosition);
+    free(maze->rats);
+
+    memset(maze, 0, sizeof(*maze));
+}
+
+
+XSCREENSAVER_MODULE_2 ("Maze3D", maze3d, maze)
+
+#endif