/* -*- 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 #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)]; int i; if (!maze->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *maze->glx_context); 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 for (i = 0; i < countof(maze->dlists); i++) if (glIsList(maze->dlists[i])) glDeleteLists(maze->dlists[i], 1); free(maze->mazeGrid); free(maze->wallList); free(maze->inverterPosition); free(maze->gl3dTextPosition); free(maze->rats); } XSCREENSAVER_MODULE_2 ("Maze3D", maze3d, maze) #endif