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