summaryrefslogtreecommitdiffstats
path: root/hacks/glx/maze3d.c
diff options
context:
space:
mode:
authorSimon Rettberg2018-10-16 10:08:48 +0200
committerSimon Rettberg2018-10-16 10:08:48 +0200
commitd3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch)
treecbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/glx/maze3d.c
downloadxscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip
Original 5.40
Diffstat (limited to 'hacks/glx/maze3d.c')
-rwxr-xr-xhacks/glx/maze3d.c1955
1 files changed, 1955 insertions, 0 deletions
diff --git a/hacks/glx/maze3d.c b/hacks/glx/maze3d.c
new file mode 100755
index 0000000..255839e
--- /dev/null
+++ b/hacks/glx/maze3d.c
@@ -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