diff options
author | Simon Rettberg | 2018-10-16 10:08:48 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-10-16 10:08:48 +0200 |
commit | d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch) | |
tree | cbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/glx/maze3d.c | |
download | xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip |
Original 5.40
Diffstat (limited to 'hacks/glx/maze3d.c')
-rwxr-xr-x | hacks/glx/maze3d.c | 1955 |
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 |