/* Program to create a CAVE maze with OpenGL in X Windows
* maze.c
*
* Written by: Juliet Bishop
* Last modified: May 16, 1996
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "image.h"
#include "cave_ogl.h"
#define MAX 15 /* Controls size of maze */
#define NUM_ITER 100
#define HT 15.0 /* Controls dimensions (height of walls, etc) */
#define DS 5.0
#define X 0
#define Y 1
#define Z 2
/* Global variables */
GLuint MAZE_WALLS_LIST;
int twoD_array [MAX][MAX];
int start_i, start_j, exit_i, exit_j;
float old_azimuth;
int ceiling;
void Setup_Light(void);
/* Function that determines where walls should go
and puts each polygon into the display list */
void Make_Maze(void) {
int i, j, num_walls;
int floor_width, floor_height, ceiling_width, ceiling_height;
int start_width, start_height, wall_width, wall_height;
float x_pos, y_pos, z_pos;
GLfloat material_ambient[] = { 0.7, 0.7, 0.7, 1.0 };
GLfloat material_diffuse[] = { 0.1, 0.5, 0.8, 1.0 };
GLfloat material_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat material_shininess[] = { 40.0 };
GLfloat material_emission[] = { 0.3, 0.2, 0.2, 0.0 };
unsigned char *start_tex, *wall_tex, *floor_tex, *ceiling_tex;
glClearColor(0.0, 0.0, 0.0, 0.0);
Setup_Light(); /* Sets up and turns on light sources */
/* Call to load_image puts the texture map into the last argument.
* load_image can be found in image.c */
if (!load_image("marble.rgb",
&floor_width, &floor_height, &floor_tex))
printf("floor image didn't load\n");
if (!load_image("sky.rgb",
&ceiling_width, &ceiling_height, &ceiling_tex))
printf("ceiling image didn't load\n");
if (!load_image("church_marble5.rgb",
&start_width, &start_height, &start_tex))
printf("start image didn't load\n");
if (!load_image("brick7.rgb",
&wall_width, &wall_height, &wall_tex))
printf("wall image didn't load\n");
/* The following code sets up texture mapping */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
/* Start of display list */
MAZE_WALLS_LIST = glGenLists(1);
glNewList(MAZE_WALLS_LIST, GL_COMPILE);
/* Polygon for maze floor */
glColor3f(0.7, 0.7, 0.7);
glTexImage2D(GL_TEXTURE_2D, 0, 3, floor_width, floor_height, 0, GL_RGB, GL_UNSIGNED_BYTE, floor_tex);
glBegin(GL_POLYGON);
glNormal3f(0.0, 1.0, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f(-DS*HT, 0.0, -DS*HT);
glTexCoord2f(7.0, 0.0); glVertex3f((MAX-DS)*HT, 0.0, -DS*HT);
glTexCoord2f(7.0, 7.0); glVertex3f((MAX-DS)*HT, 0.0, (MAX-DS)*HT);
glTexCoord2f(0.0, 7.0); glVertex3f(-DS*HT, 0.0, (MAX-DS)*HT);
glEnd();
/* Polygon for maze ceiling */
if (ceiling) { /* Only add ceiling polygon if user wants it */
glColor3f(1.0, 1.0, 1.0);
glTexImage2D(GL_TEXTURE_2D, 0, 3, ceiling_width, ceiling_height, 0, GL_RGB, GL_UNSIGNED_BYTE, ceiling_tex);
glBegin(GL_POLYGON);
glNormal3f(0.0, -1.0, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f(-DS*HT, HT, -DS*HT);
glTexCoord2f(4.0, 0.0); glVertex3f((MAX-DS)*HT, HT, -DS*HT);
glTexCoord2f(4.0, 4.0); glVertex3f((MAX-DS)*HT, HT, (MAX-DS)*HT);
glTexCoord2f(0.0, 4.0); glVertex3f(-DS*HT, HT, (MAX-DS)*HT);
glEnd();
}
/* Polygon to mark start position of maze */
glColor3f(1.0, 1.0, 1.0);
glTexImage2D(GL_TEXTURE_2D, 0, 3, start_width, start_height, 0, GL_RGB, GL_UNSIGNED_BYTE, start_tex);
glBegin(GL_POLYGON);
glNormal3f(0.0, 1.0, 0.0);
glTexCoord2f(0.0, 0.0); glVertex3f((start_j-DS)*HT, 0.1, (start_i-DS)*HT);
glTexCoord2f(1.0, 0.0); glVertex3f((start_j-DS)*HT, 0.1, (start_i-DS)*HT+HT*2);
glTexCoord2f(1.0, 1.0); glVertex3f((start_j-DS)*HT+HT*2, 0.1, (start_i-DS)*HT+HT*2);
glTexCoord2f(0.0, 1.0); glVertex3f((start_j-DS)*HT+HT*2, 0.1, (start_i-DS)*HT);
glEnd();
/* Material properties setup for walls */
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, material_shininess);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
/* Using the array twoD_array, the positions of the walls
* are determined */
glColor3f(1.0, 1.0, 1.0);
glTexImage2D(GL_TEXTURE_2D, 0, 3, wall_width, wall_height, 0, GL_RGB, GL_UNSIGNED_BYTE, wall_tex);
num_walls = 0;
for (i=0; i < MAX; i++) {
for (j=0; j < MAX; j++) {
if (twoD_array[i][j] == 0) { /* Finds 0's in array */
if (j < MAX-1) {
if (twoD_array[i][j+1] == 1){ /* Checks if cell to the right is 1 */
glBegin(GL_POLYGON);
glNormal3f(1.0, 0.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i+1-DS));
glEnd();
num_walls++;
}
}
if (j > 0) {
if (twoD_array[i][j-1] == 1) { /* Checks if cell to the left is 1 */
glBegin(GL_POLYGON);
glNormal3f(0.0, 0.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i+1-DS));
glEnd();
num_walls++;
}
}
if (i < MAX-1) {
if (twoD_array[i+1][j] == 1) { /* Checks if cell below is 1 */
glBegin(GL_POLYGON);
glNormal3f(0.0, 0.0, -1.0);
glNormal3f(1.0, 0.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i+1-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i+1-DS));
glEnd();
num_walls++;
}
}
if (i > 0) {
if (twoD_array[i-1][j] == 1) { /* Checks if cell above is 1 */
glBegin(GL_POLYGON);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i-DS));
glEnd();
num_walls++;
}
}
} /* End of check for particular cell */
} /* End of for j loop */
} /* End of for i loop */
/* The following code generates the maze outside walls,
leaving a space for the exit */
i=0;
for (j=0; j < MAX; j++)
if ((i != exit_i) || (j != exit_j)) { /* Check for exit location */
glBegin(GL_POLYGON);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i-DS));
glEnd();
}
j=0;
for (i=0; i < MAX; i++)
if ((i != exit_i) || (j != exit_j)) { /* Check for exit location */
glBegin(GL_POLYGON);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i+1-DS));
glEnd();
}
i=MAX-1;
for (j=0; j < MAX; j++)
if ((i != exit_i) || (j != exit_j)) { /* Check for exit location */
glBegin(GL_POLYGON);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j-DS), 0.0, HT*(i+1-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i+1-DS));
glEnd();
}
j=MAX-1;
for (i=0; i < MAX; i++)
if ((i != exit_i) || (j != exit_j)) { /* Check for exit location */
glBegin(GL_POLYGON);
glTexCoord2f(1.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i-DS));
glTexCoord2f(1.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i-DS));
glTexCoord2f(0.0, 2.0); glVertex3f(HT*(j+1-DS), HT, HT*(i+1-DS));
glTexCoord2f(0.0, 0.0); glVertex3f(HT*(j+1-DS), 0.0, HT*(i+1-DS));
glEnd();
}
glEndList(); /* End of display list for maze */
} /* End of function make_maze() */
/* Set up and initialize light */
void Setup_Light(void) {
GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_position[] = { 10.0, 20.0, 10.0, 0.0 };
GLfloat light_spot_dir[] = { 0.0, -1.0, 0.0 };
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_spot_dir);
glEnable(GL_LIGHT0);
}
/* Display loop function, clears buffers and draws maze
* from display list */
void Display_Maze(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
CAVENavTransform();
glCallList(MAZE_WALLS_LIST);
}
/* Generates MAX-1 by MAX-1 array of 1's and 0's for maze */
int Calculate_Maze_Array(void) {
int visited [MAX][MAX];
int i,j, dir, num_visit;
int sum, num, rand_seed;
int exit_set;
/* Initialize arrays */
for (i=0; i < MAX; i++) {
for (j=0; j < MAX; j++) {
twoD_array[i][j] = 0;
visited[i][j] = 0;
}
}
/* Prompt for user input for the random number generator seed */
printf("Enter a number from 0 to 1000: ");
scanf("%i", &rand_seed);
if ((rand_seed < 0) || (rand_seed > 1000)) {
printf("Try again: Enter a number from 0 to 1000\n");
scanf("%i", &rand_seed);
}
if ((rand_seed < 0) || (rand_seed > 1000)) rand_seed = 1;
i=0; j=0; sum=0;
/* Set start position of maze */
start_i = MAX/2; start_j = MAX/2;
/* Initialize a square of four cells to 1 */
twoD_array[MAX/2][MAX/2] = 1;
twoD_array[MAX/2][MAX/2+1] = 1;
twoD_array[MAX/2+1][MAX/2] = 1;
twoD_array[MAX/2+1][MAX/2+1] = 1;
visited[MAX/2][MAX/2] = 1;
visited[MAX/2][MAX/2+1] = 1;
visited[MAX/2+1][MAX/2] = 1;
visited[MAX/2+1][MAX/2+1] = 1;
srand(rand_seed); /* rand_seed from user input */
num = 0;
exit_set = 0; /* Set to 1 when maze reaches an outside wall */
num_visit = 4;
while ((num_visit < (MAX*MAX) || (exit_set == 0)) && (num <= NUM_ITER)) {
num++;
for (i = 0; i < MAX; i++) {
for (j = 0; j < MAX; j++) {
/* Calculate sum of neighbor cells that have been set to 1 */
sum = 0;
if (visited[i][j] == 0) {
if (j < MAX-1) sum = sum + twoD_array[i][j+1];
if (i < MAX-1) sum = sum + twoD_array[i+1][j];
if (i > 0) sum = sum + twoD_array[i-1][j];
if (j > 0) sum = sum + twoD_array[i][j-1];
dir = rand(); /* Get a random number */
if ((sum > 0) && (sum < 3)) { /* 1 or 2 neighbors cells set to 1 */
if (dir < 21830) { /* Test random number */
twoD_array[i][j] = 1; /* Set cell to 1 */
if ((j == MAX-1) || (i == MAX-1)) { /* If cell that is set to
* 1 is on the outside edge
* of the array, */
exit_i = i; exit_j = j; /* set the exit position */
exit_set = 1;
}
}
visited[i][j] = 1; /* Set visited flag to 1 */
num_visit++;
}
if (sum >= 3) visited[i][j] = 1; /* If the cell already has at least
* 3 neigbors, keep it as 0 */
} /* End of if (visited) */
} /* End of for j loop */
} /* End of for i loop */
} /* End of while num_visit loop */
/* Prints out twoD_array of 1's and 0's */
/* printf("%i,i=%i, j=%i\n", dir, i, j);
for (i=0; i < MAX; i++) {
for (j=0; j < MAX; j++) {
printf("%i ", twoD_array[i][j]);
}
printf("\n");
}
*/
return(exit_set);
}
/* Controls navigation of user through maze.
* Avoids walking through walls. */
void Maze_Navigate(float speed) {
int maze_x, maze_z, i;
float x_pos, y_pos, z_pos;
float head_x, head_y, head_z;
float azimuth, elevation, roll;
float maze_pos[3], head_pos[3];
CAVEGetWandFront(x_pos, y_pos, z_pos);
CAVEGetHead(head_x, head_y, head_z);
head_pos[X] = head_x;
head_pos[Y] = head_y;
head_pos[Z] = head_z;
CAVENavConvertCAVEToWorld(head_pos, maze_pos);
maze_x = (int)((maze_pos[X]/HT)+DS);
maze_z = (int)((maze_pos[Z]/HT)+DS);
/* Check to see if wall is ahead.
* If so, do not allow movement in that direction.
* When the user changes the wand's orientation
* to move away from the wall, movement is allowed again */
if (maze_pos[X]/HT+DS - maze_x > 0.9) {
if (x_pos > 0.0) { /* Moving towards wall */
if (maze_x == MAX-1) /* Outer wall of maze */
x_pos = 0.0; /* Do not move in the x direction */
else if (twoD_array[maze_z][maze_x+1] == 0) {
for (i=0; i<100; i++)
x_pos = x_pos - i*0.1; /* Do not move in the x direction */
}
}
}
if (maze_pos[X]/HT+DS - maze_x < 0.1) {
if (x_pos < 0.0) { /* Moving towards wall */
if (maze_x == 0) /* Outer wall of maze */
x_pos = 0.0; /* Do not move in the x direction */
else if (twoD_array[maze_z][maze_x-1] == 0)
x_pos = 0.0; /* Do not move in the x direction */
}
}
if (maze_pos[Z]/HT+DS - maze_z > 0.9) {
if (z_pos > 0.0) { /* Moving towards wall */
if (maze_z == MAX-1) /* Outer wall of maze */
z_pos = 0.0; /* Do not move in the z direction */
else if (twoD_array[maze_z+1][maze_x] == 0)
z_pos = 0.0; /* Do not move in the z direction */
}
}
if (maze_pos[Z]/HT+DS - maze_z < 0.1) {
if (z_pos < 0.0) { /* Moving towards wall */
if (maze_z == 0) /* Outer wall of maze */
z_pos = 0.0; /* Do not move in the z direction */
else if (twoD_array[maze_z-1][maze_x] == 0)
z_pos = 0.0; /* Do not move in the z direction */
}
}
/* Move user's position according to wand orientation
* at a certain speed*/
CAVENavTranslate(x_pos/speed, 0.0, z_pos/speed);
/* If user has changed the wand's orientation,
* change the user's orientation accordingly.
* Otherwise, don't change orientation */
CAVEGetWandOrientation(azimuth, elevation, roll);
if (azimuth != old_azimuth) {
old_azimuth = azimuth;
CAVENavRot(azimuth/(speed*1.5), 'y');
}
} /* End of Maze_Navigate */
void main(int argc, char **argv) {
float speed;
char show;
/* Get user input for whether to draw ceiling polygon or not */
printf("Do you want to show the ceiling? (y/n)\n");
scanf("%c", &show);
if ((show == 'y') || (show == 'Y'))
ceiling = 1; /* Draw ceiling polygon */
else
ceiling = 0; /* Don't draw ceiling polygon */
/* Call to calculate array of 1's and 0's for maze paths and walls */
if (!Calculate_Maze_Array()) {
printf("No exit for maze, please choose another number.");
Calculate_Maze_Array(); /* If first maze has no exit,
* generate another array. */
}
/* Cave calls */
CAVEConfigure(NULL, NULL, NULL);
CAVEInit();
CAVEInitApplication(Make_Maze, 0);
CAVEDisplay(Display_Maze, 0);
/* Move to start position of maze */
CAVENavTranslate((start_j-DS+1.0)*HT, 0.0, (start_i-DS+1.0)*HT);
speed = 200.0; /* Speed of movement in maze */
old_azimuth = 0.0;
/* The event loop */
while (!CAVEgetbutton(CAVE_ESCKEY)) {
/* The user only moves while holding down wand button 1 */
if (CAVEBUTTON1) {
Maze_Navigate(speed); /* Navigate at given speed */
} /* End of if(CAVEBUTTON1) */
/* Up arrow increases speed of movement in CAVE */
/* Change KKEY to UPARROWKEY when not in simulator mode */
if (CAVEgetbutton(CAVE_KKEY)) {
if (speed > 20.0) /* Upper limit on speed */
speed = speed - 1.0;
}
/* Down arrow decreases speed of movement in CAVE */
/* Change JKEY to DOWNARROWKEY when not in simulator mode */
if (CAVEgetbutton(CAVE_JKEY)) {
if (speed < 2000.0) /* Lower limit on speed */
speed = speed + 1.0;
}
} /* End of while (!CAVEgetbutton) */
CAVEExit();
}