/* 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(); }