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