/* Greydon Foil 3.10.04 */ /* terrain.c */ #include "terrain.h" #include #include #include #include #ifdef __APPLE__ #include #else #include #endif /* These are the global grids that I can call within the program */ Grid* mainGrid; Grid* flow; Grid* acc; /* These are used so I can pass them into other functions, specifically the gotoMainInputLoop function */ int globalArgc; char** globalArgv; int fillToggle; int colorScheme; int resolution; int heightLevel; float pos[3] = {0, 0, 0}; float rot[3] = {0, 0, 0}; float eye[3] = {0, 0, 0}; float pitch; float yaw; float car[3] = {0, 0, 0}; /* x, y, z of car*/ float ratio; /* This method determines the orientation of the drawing to be done on the screen and then loops through the grid and draws triangles using heights in the grid as vertices and coloring those vertices according to heights. It also checks to see if any of the vertices are points with no height data and does not draw them if they do. */ void display3d(void) { if (fillToggle == 1) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } else { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } float startingPointX, startingPointY; if (mainGrid->numCols > mainGrid->numRows) { startingPointX = -1; startingPointY = 1 - ((mainGrid->numCols - mainGrid->numRows)/2)* ratio; } else { startingPointX = -1 + ((mainGrid->numRows - mainGrid->numCols)/2)* ratio; startingPointY = 1; } int modX = mainGrid->numCols % resolution; int modY = mainGrid->numRows % resolution; /* This determines the relative height to the rest of the grid. Using they keys you can change the relief of the map */ float heightRatio = mainGrid->largestHeight - mainGrid->smallestHeight; heightRatio = heightRatio * (float)heightLevel; float low = mainGrid->smallestHeight; /* This loops through all of the points on the grid and creates the grid triangles using their values and their heights. It is pretty messy because it has to check to make sure that an increase in resolution doesn't cause a segment fault and that each points is plotted to it's correct x- and y-position on the screen. */ int i, j; for (j = 0; j < mainGrid->numRows - resolution; j += resolution) { for (i = 0; i < mainGrid->numCols - resolution; i += resolution) { if (mainGrid->values[i][j] != mainGrid->nodataVal && mainGrid-> values[i+resolution][j] != mainGrid->nodataVal && mainGrid-> values[i][j+resolution] != mainGrid->nodataVal && mainGrid-> values[i+resolution][j+resolution] != mainGrid->nodataVal) { glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][j]); glVertex3f(startingPointX + i*ratio, startingPointY - j*ratio, (mainGrid->values[i][j] - low)/heightRatio); colorFunction(mainGrid->values[i+resolution][j]); glVertex3f(startingPointX + (i + resolution) * ratio, startingPointY - j*ratio, (mainGrid->values[i+resolution][j] - low)/heightRatio); colorFunction(mainGrid->values[i][j+resolution]); glVertex3f(startingPointX + i*ratio, startingPointY - (j+resolution) * ratio, (mainGrid->values[i][j+resolution] - low)/heightRatio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][j+resolution]); glVertex3f(startingPointX + i*ratio, startingPointY - (j+resolution)*ratio, (mainGrid->values[i][j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i+resolution][j]); glVertex3f(startingPointX + (i+resolution)* ratio, startingPointY - j*ratio, (mainGrid->values[i+resolution][j]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j+resolution]); glVertex3f(startingPointX + (i+resolution)*ratio, startingPointY - (j+resolution)*ratio, (mainGrid->values[i + resolution] [j + resolution]-low)/heightRatio); glEnd(); /* These take into account the special cases when the resolution is increased and you have to make sure the program doesn't call for elements outside of the array bounds. */ if ((j + resolution * 2 > mainGrid->numRows) && (i + resolution * 2 > mainGrid->numCols)) { /* bottom corner */ if (mainGrid->values[mainGrid->numCols - 1][j+resolution] != mainGrid->nodataVal && mainGrid->values[mainGrid->numCols-1] [mainGrid->numRows-1] != mainGrid->nodataVal && mainGrid-> values[i+resolution][mainGrid->numRows-1] != mainGrid-> nodataVal && mainGrid->values[i][mainGrid->numRows-1] != mainGrid->nodataVal && mainGrid->values[mainGrid->numCols-1] [j] != mainGrid->nodataVal) { glBegin(GL_POLYGON); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution)* ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution] - low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1] [j + resolution]); glVertex3f(startingPointX + (mainGrid->numCols - 1) * ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[mainGrid->numCols-1] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution] [mainGrid->numRows - 1]); glVertex3f(startingPointX + (i + resolution) * ratio, startingPointY - (mainGrid->numRows - 1) * ratio, (mainGrid->values[i+resolution] [mainGrid->numRows-1]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i + resolution] [mainGrid->numRows - 1]); glVertex3f(startingPointX + (i + resolution)*ratio, startingPointY - (mainGrid->numRows - 1)*ratio, (mainGrid->values[i+resolution] [mainGrid->numRows-1]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1] [j + resolution]); glVertex3f(startingPointX + (mainGrid->numCols - 1)* ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[mainGrid->numCols-1] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1] [mainGrid->numRows - 1]); glVertex3f(startingPointX + (mainGrid->numCols - 1)*ratio, startingPointY - (mainGrid->numRows - 1)*ratio, (mainGrid->values[mainGrid->numCols-1] [mainGrid->numRows-1]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); /* right bottom square */ colorFunction(mainGrid->values[i][j + resolution]); glVertex3f(startingPointX + (i)* ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i][j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution) * ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i][mainGrid->numRows - 1]); glVertex3f(startingPointX + (i) * ratio, startingPointY - (mainGrid->numRows - 1) * ratio, (mainGrid->values[i] [mainGrid->numRows-1]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][mainGrid->numRows - 1]); glVertex3f(startingPointX + (i)*ratio, startingPointY - (mainGrid->numRows - 1)*ratio, (mainGrid->values[i] [mainGrid->numRows-1]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution)* ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution] [mainGrid->numRows - 1]); glVertex3f(startingPointX + (i + resolution)*ratio, startingPointY - (mainGrid->numRows - 1)*ratio, (mainGrid->values[i+resolution] [mainGrid->numRows-1]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); /* bottom right square */ colorFunction(mainGrid->values[i + resolution][j]); glVertex3f(startingPointX + (i + resolution)* ratio, startingPointY - (j)*ratio, (mainGrid->values[i+resolution][j]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1][j]); glVertex3f(startingPointX + (mainGrid->numCols - 1) * ratio, startingPointY - (j)*ratio, (mainGrid->values[mainGrid->numCols-1] [j]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution) * ratio, startingPointY - (j + resolution) * ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution)*ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1][j]); glVertex3f(startingPointX + (mainGrid->numCols - 1)* ratio, startingPointY - (j)*ratio, (mainGrid->values[mainGrid->numCols-1] [j]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1] [j + resolution]); glVertex3f(startingPointX + (mainGrid->numCols - 1)*ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[mainGrid->numCols-1] [j+resolution]-low)/heightRatio); glEnd(); } } else if (j + resolution * 2 > mainGrid->numRows && i + resolution < mainGrid->numCols) { /* bottom */ if (mainGrid->values[i][j+resolution] != mainGrid->nodataVal && mainGrid->values[i+resolution][j+resolution] != mainGrid-> nodataVal && mainGrid->values[i][mainGrid->numRows-1] != mainGrid->nodataVal && mainGrid->values[i+resolution] [mainGrid->numRows-1] != mainGrid->nodataVal) { glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][j + resolution]); glVertex3f(startingPointX + (i)* ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i][j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution) * ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i][mainGrid->numRows - 1]); glVertex3f(startingPointX + (i) * ratio, startingPointY - (mainGrid->numRows - 1) * ratio, (mainGrid->values[i] [mainGrid->numRows-1]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][mainGrid->numRows - 1]); glVertex3f(startingPointX + (i)*ratio, startingPointY - (mainGrid->numRows - 1)*ratio, (mainGrid->values[i] [mainGrid->numRows-1]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution)* ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution] [mainGrid->numRows - 1]); glVertex3f(startingPointX + (i + resolution)*ratio, startingPointY - (mainGrid->numRows - 1)*ratio, (mainGrid->values[i+resolution] [mainGrid->numRows-1]-low)/heightRatio); glEnd(); } } else if ((j + resolution < mainGrid->numRows) && (i + resolution * 2 > mainGrid->numCols)) { /* right */ if(mainGrid->values[i+resolution][j] != mainGrid->nodataVal && mainGrid->values[i+resolution][j+resolution] != mainGrid->nodataVal && mainGrid->values[mainGrid->numCols-1] [j] != mainGrid->nodataVal && mainGrid->values [mainGrid->numCols-1][j+resolution] != mainGrid->nodataVal) { glBegin(GL_POLYGON); colorFunction(mainGrid->values[i + resolution][j]); glVertex3f(startingPointX + (i + resolution)* ratio, startingPointY - (j)*ratio, (mainGrid->values[i+resolution][j]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1][j]); glVertex3f(startingPointX + (mainGrid->numCols - 1) * ratio, startingPointY - (j)*ratio, (mainGrid->values[mainGrid->numCols-1] [j]-low)/heightRatio); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution) * ratio, startingPointY - (j + resolution) * ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i + resolution][j + resolution]); glVertex3f(startingPointX + (i + resolution)*ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[i+resolution] [j+resolution]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1][j]); glVertex3f(startingPointX + (mainGrid->numCols - 1)* ratio, startingPointY - (j)*ratio, (mainGrid->values[mainGrid->numCols-1] [j]-low)/heightRatio); colorFunction(mainGrid->values[mainGrid->numCols - 1] [j + resolution]); glVertex3f(startingPointX + (mainGrid->numCols - 1)*ratio, startingPointY - (j + resolution)*ratio, (mainGrid->values[mainGrid->numCols-1] [j+resolution]-low)/heightRatio); glEnd(); } } } } } } /* display3d */ /* This method determines the orientation of the drawing to be done on the screen and then loops through the grid and draws triangles using heights in the grid as vertices and coloring those vertices according to heights. */ void display2d(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (fillToggle == 1) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } else { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } float ratio; float startingPointX, startingPointY; if (mainGrid->numCols > mainGrid->numRows) { ratio = 2/((float)mainGrid->numCols); startingPointX = -1; startingPointY = 1 - ((float)(mainGrid->numCols - mainGrid->numRows)/2)*ratio; } else { ratio = 2/((float)mainGrid->numRows); startingPointX = -1 + ((float)(mainGrid->numRows - mainGrid->numCols)/2)*ratio; startingPointY = 1; } int i, j; for (j = 0; j < mainGrid->numRows -1; j++) { for (i = 0; i < mainGrid->numCols -1; i++) { glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][j]); glVertex2f(startingPointX + i*ratio, startingPointY - j*ratio); colorFunction(mainGrid->values[i+1][j]); glVertex2f(startingPointX + (i + 1)* ratio, startingPointY - j*ratio); colorFunction(mainGrid->values[i][j+1]); glVertex2f(startingPointX + i*ratio, startingPointY - (j+1) * ratio); glEnd(); glBegin(GL_POLYGON); colorFunction(mainGrid->values[i][j+1]); glVertex2f(startingPointX + i*ratio, startingPointY - (j+1)*ratio); colorFunction(mainGrid->values[i+1][j]); glVertex2f(startingPointX + (i+1)* ratio, startingPointY - j*ratio); colorFunction(mainGrid->values[i + 1][j+1]); glVertex2f(startingPointX + (i+1)*ratio, startingPointY - (j+1)*ratio); glEnd(); } } glFlush(); } /* display2d */ /* This function initializes all of the arrays at the beginning of the program and when a key is pressed to initialize the scene. It also randomly places the car at some point on the terrain. */ void initView() { pos[0] = 0; pos[1] = -0.4; pos[2] = 0.2; rot[0] = -55; rot[1] = 0; rot[2] = 45; eye[0] = 0; eye[1] = 0; eye[2] = -3; pitch = 0; yaw = 0; int randX, randY, pos; randX = rand()%mainGrid->numCols/2; pos = rand()%2; if (pos == 1) randX *= -1; randY = rand()%mainGrid->numRows/2; pos = rand()%2; if (pos == 1) randY *= -1; car[0] = randX; car[1] = randY; car[2] = 0; } /* initView */ /* This function controls the different colors of the heights. For some cases it changes the colors based on the current height in relation to the tallest and shortest heights on the grid while in other cases it just assigns groups of heights a color. */ void colorFunction(float height) { float difference = mainGrid->largestHeight - mainGrid->smallestHeight; float color = ((height - mainGrid->smallestHeight)/difference); if (colorScheme == 0) { if (height == mainGrid->nodataVal) glColor3f(0, 0.2, 0.4); else glColor3f(color, color, color); } else if (colorScheme == 1) { if (height == mainGrid->nodataVal) glColor3f(0, 0.2, 0.4); else if (color < 0.2) glColor3f(0, 0, color * 5); else if (color < 0.4) glColor3f(0, (color - .2) * 5, 1); else if (color < 0.6) glColor3f(0, 1, 1 - (color - .4)*5); else if (color < 0.8) glColor3f((color-.6) * 5, 1, 0); else glColor3f(1, 1- (color-.8)*5, 0); } else if (colorScheme == 2) { if (height == mainGrid->nodataVal) glColor3f(0, 0.2, 0.4); else if (color < 0.2) glColor3f(color * 5, 0, 0); else if (color < 0.4) glColor3f(1, (color - .2) * 5, 0); else if (color < 0.6) glColor3f(1 - (color - .4)*5, 1, 0); else if (color < 0.8) glColor3f(0, 1, (color-.6) * 5); else glColor3f(0, 1- (color-.8)*5, 1); } } /* colorFunction */ /* This method uses the existing pos, rot, eye, center, and up arrays to determine the rotation and translation of the camera and the scene itself. */ void renderScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, 1, 0, 10); glRotatef(pitch, 1, 0, 0); glRotatef(yaw, 0, 1, 0); glTranslatef(eye[0], eye[1], eye[2]); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(pos[0], pos[1], pos[2]); glRotatef(rot[0], 1, 0, 0); glRotatef(rot[1], 0, 1, 0); glRotatef(rot[2], 0, 0, 1); display3d(); glutSwapBuffers(); } /* renderScene */ /* This method uses the existing pos, rot, eye, center, and up arrays to determine the rotation and translation of the camera and the scene itself. It also draws in any animations that are present. */ void renderSceneAnim(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, 1, 0, 10); glRotatef(pitch, 1, 0, 0); glRotatef(yaw, 0, 1, 0); glTranslatef(eye[0], eye[1], eye[2]); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(pos[0], pos[1], pos[2]); glRotatef(rot[0], 1, 0, 0); glRotatef(rot[1], 0, 1, 0); glRotatef(rot[2], 0, 0, 1); display3d(); findCurrentCarZ(); drawCar(); glutSwapBuffers(); } /* renderSceneAnim */ /* This method checks to make sure that the car is at a point over the grid and then sets it's height equal to the displayed height of the grid at the car's x- and y-coordinates. */ void findCurrentCarZ(void) { if ((int)car[0] + mainGrid->numCols/2 > 0 && (int)car[0] + mainGrid->numCols/2 < mainGrid->numCols && -1*((int)car[1] - mainGrid->numRows/2) > 0 && -1*((int)car[1] - mainGrid->numRows/2) < mainGrid->numRows && mainGrid->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] != mainGrid->nodataVal) { car[2] = (mainGrid->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] - mainGrid->smallestHeight); } } /* findCurrentCarZ */ /* This method just draws the car using the global coordinates stored in the car[] array. Since the grid has to account for different ratios when dealing with heights and positions, so does this method in order to make the car look like it belongs in place on the grid. */ void drawCar(void) { float heightRatio = mainGrid->largestHeight - mainGrid->smallestHeight; heightRatio = heightRatio * (float)heightLevel; float carsize = ratio * 5; glColor3f(1, 0, 0); glBegin(GL_POLYGON); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio + carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio - carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio + carsize); glEnd(); glBegin(GL_POLYGON); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio + carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio - carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio - carsize); glEnd(); glBegin(GL_POLYGON); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio + carsize); glEnd(); glBegin(GL_POLYGON); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio + carsize); glEnd(); glBegin(GL_POLYGON); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio + carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio + carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio + carsize, car[2]/heightRatio + carsize); glEnd(); glBegin(GL_POLYGON); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio - carsize, car[2]/heightRatio - carsize); glVertex3f(car[0]*ratio - carsize, car[1]*ratio - carsize, car[2]/heightRatio + carsize); glVertex3f(car[0]*ratio + carsize, car[1]*ratio - carsize, car[2]/heightRatio + carsize); glEnd(); } /* drawCar */ /* This method checks to make sure that the car is on the grid. If it is not then it randomly places it somewhere on the grid. */ void idleAnim(void) { /* If the car is out of bounds randomly place it in bounds */ if (car[0] < -mainGrid->numCols/2 || car[0] > mainGrid->numCols/2 || car[1] < -mainGrid->numRows/2 || car[1] > mainGrid->numCols/2) { int randX, randY, pos; randX = rand()%mainGrid->numCols/2; pos = rand()%2; if (pos == 1) randX *= -1; randY = rand()%mainGrid->numRows/2; pos = rand()%2; if (pos == 1) randY *= -1; car[0] = randX; car[1] = randY; car[2] = 0; } /* otherwise move the car with the flow of the terrain */ car[2] = (mainGrid->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] - mainGrid->smallestHeight); if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 1) car[1] -= 0.1; else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 2) { car[1] -= 0.1; car[0] += 0.1; } else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 3) { car[0] += 0.1; } else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 4) { car[1] += 0.1; car[0] += 0.1; } else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 5) { car[1] += 0.1; } else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 6) { car[1] += 0.1; car[0] -= 0.1; } else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 7) { car[0] -= 0.1; } else if (flow->values[(int)car[0] + mainGrid->numCols/2] [-1*((int)car[1] - mainGrid->numRows/2)] == 8) { car[1] += 0.1; car[0] -= 0.1; } else { /* if there is no flow from the current point then randomly move it somewhere else on the terrain */ int randX, randY, pos; randX = rand()%mainGrid->numCols/2; pos = rand()%2; if (pos == 1) randX *= -1; randY = rand()%mainGrid->numRows/2; pos = rand()%2; if (pos == 1) randY *= -1; car[0] = randX; car[1] = randY; car[2] = 0; } renderSceneAnim(); } /* This method handles the display for the animation. */ void disAnim(void) { renderSceneAnim(); glutIdleFunc(idleAnim); } /* Reads in the specified file if the arguments are correct and the file exists and then calls a function to build the grid, initializes openGL, and calls another function to draw the grid. */ int main(int argc, char** argv) { globalArgc = argc; globalArgv = argv; car[0] = -1; car[1] = -1; car[2] = 0; printf("\nStarting Terrain program. Type 'help' for a list of commands or enter commands below.\n\n"); gotoMainInputLoop(); return 1; } /* main */ /* This function is called to bring up the command prompt for the program. It is written as a function so other methods can call it once they have finished. */ void gotoMainInputLoop(void) { char inFilename[80]; char outFilename[80]; char str[30]; while(1){ printf("Terrain>>: "); scanf("%30s", str); if (strcmp("3dterrain", str) == 0) { /* 3d terrain */ scanf("%80s", inFilename); printf("Reading in grid %s.\n", inFilename); mainGrid = readGrid(inFilename); fillToggle = 0; /* sets fill to the off setting */ colorScheme = 1; /* sets to the first color setting */ resolution = 10; /* sets the resolution to 1 to 1 */ heightLevel = 2; /* sets it so the heights are displayed half as high */ /* initialize openGL */ glutInit(&globalArgc, globalArgv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(globalArgv[0]); glClearColor(0, 0, 0, 0); glColor3f(1, 1, 1); glutDisplayFunc(renderScene); glutKeyboardFunc(keypress3d); glutSpecialFunc(specialKey); initView(); glutCreateMenu(main_menu); glutAddMenuEntry("Fill/Outline", 1); glutAddMenuEntry("Change color scheme", 2); glutAddMenuEntry("Quit", 3); glutAttachMenu(GLUT_LEFT_BUTTON); glutMainLoop(); } else if (strcmp("animate", str) == 0) { /* animate */ scanf("%80s", inFilename); printf("Reading in grid %s.\n", inFilename); mainGrid = readGrid(inFilename); flow = allocateMemory(flow); flowDir(); fillToggle = 0; /* sets fill to the off setting */ colorScheme = 1; /* sets to the first color setting */ resolution = 10; /* sets the resolution to 1 to 1 */ heightLevel = 2; /* sets it so the heights are displayed half as high */ /* initialize openGL */ glutInit(&globalArgc, globalArgv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(globalArgv[0]); glClearColor(0, 0, 0, 0); glColor3f(1, 1, 1); glutDisplayFunc(disAnim); glutKeyboardFunc(keypress3d); glutSpecialFunc(specialKey); glutIdleFunc(idleAnim); initView(); glutCreateMenu(main_menu); glutAddMenuEntry("Fill/Outline", 1); glutAddMenuEntry("Change color scheme", 2); glutAddMenuEntry("Quit", 3); glutAttachMenu(GLUT_LEFT_BUTTON); glutMainLoop(); } else if (strcmp("2dterrain", str) == 0) { /* 2dterrain */ scanf("%80s", inFilename); printf("Reading in grid %s.\n", inFilename); mainGrid = readGrid(inFilename); fillToggle = 0; /* sets fill to the off setting */ colorScheme = 1; /* sets to the first color setting */ /* initialize openGL */ glutInit(&globalArgc, globalArgv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(globalArgv[0]); glClearColor(0, 0, 0, 0); glColor3f(1, 1, 1); glutDisplayFunc(display2d); glutKeyboardFunc(keypress2d); glutCreateMenu(main_menu); glutAddMenuEntry("Fill/Outline", 1); glutAddMenuEntry("Change color scheme", 2); glutAddMenuEntry("Quit", 3); glutAttachMenu(GLUT_LEFT_BUTTON); glutMainLoop(); } else if (strcmp("flowDir", str) == 0) { scanf("%80s %80s", inFilename, outFilename); printf("\tReading in %s.\n", inFilename); mainGrid = readGrid(inFilename); flow = allocateMemory(flow); printf("\tCalculating flow direction grid.\n"); flowDir(); printf("\tWriting flow direction grid to %s.\n",outFilename); writeGrid(flow, outFilename); mainGrid = freeMem(mainGrid); flow = freeMem(flow); } else if (strcmp("flowAcc", str) == 0) { /* flow accumulation */ scanf("%80s %80s", inFilename, outFilename); printf("\tReading in %s.\n", inFilename); mainGrid = readGrid(inFilename); flow = allocateMemory(flow); printf("\tCalculating flow direction grid.\n"); flowDir(); printf("\tCalculating the flow accumulation.\n"); acc = allocateMemory(acc); initAccValues(); flowAcc(); printf("\tWriting the flow accumulation grid to %s.\n", outFilename); writeGrid(acc, outFilename); mainGrid = freeMem(mainGrid); flow = freeMem(flow); acc = freeMem(acc); } else if (strcmp("printInfo", str) == 0) { /* printInfo */ scanf("%80s", inFilename); mainGrid = readGrid(inFilename); printInfo(mainGrid); mainGrid = freeMem(mainGrid); } else if (strcmp("exit", str) == 0){ /* exit */ printf("Goodbye.\n"); exit(1); } else if (strcmp("help", str) == 0) { /* help */ printf("\tCommands::\n"); printf("\t\t3dterrain MAPNAME - renders file of name MAPNAME in 3d.\n\n"); printf("\t\t2dterrain MAPNAME - renders file of name MAPNAME in 2d.\n\n"); printf("\t\tanimate MAPNAME - renders file of name MAPNAME and animates blocks following the flow direction along the map.\n\n"); printf("\t\tflowDir MAPNAME OUTNAME - builds the flow direction file from the input MAPNAME and writes it to a file called OUTNAME.\n\n"); printf("\t\tflowAcc MAPNAME OUTNAME - builds the flow accumulation file from the input MAPNAME and writes it to a file called OUTNAME.\n\n"); printf("\t\tprintInfo MAPNAME - reads in the file of name MAPNAME and prints out relevant information about that file.\n\n"); printf("\t\texit - exits the program\n\n"); } else printf("Incorrect input. Type 'help' for a list of available inputs\n"); } } /* This method is used to quickly free memory from structures that are no longer in use by the program. In order to reduce memory costs for the program (especially since there is no simple way to determine if a user is finished with a specific graph or not, I free all the structures after flow accumulation or flow direction. */ Grid* freeMem(Grid* inGrid) { int i; for (i = 0; i < inGrid->numCols; i++) { free(inGrid->values[i]); } free(inGrid->values); return inGrid; } /* freeMem */ /* This method is used to quickly allocate memory using the mainGrid structure that has already been populated and allocated. This method is solely used for the allocation of memory for the flow direction and flow accumulation grids and thus it can take advantage of the fact that we already know the size of the grid and we can just allocate memory of that size for each grid. */ Grid* allocateMemory(Grid* inGrid) { inGrid = (Grid*)malloc(sizeof(Grid)); if (inGrid == NULL) { printf("Memory allocation problems"); gotoMainInputLoop(); } inGrid->numCols = mainGrid->numCols; inGrid->numRows = mainGrid->numRows; inGrid->xllcorner = mainGrid->xllcorner; inGrid->yllcorner = mainGrid->yllcorner; inGrid->cellsize = mainGrid->cellsize; inGrid->nodataVal = mainGrid->nodataVal; inGrid->name = mainGrid->name; inGrid->values = (float**)malloc(sizeof(float*)*(inGrid->numCols)); if (inGrid->values == NULL){ printf("Memory allocation problems.\n"); gotoMainInputLoop(); } int i; for (i = 0;i < inGrid->numCols; i++) { inGrid->values[i] = (float*)malloc(sizeof(float)*(inGrid->numRows)); if (inGrid->values[i] == NULL) { printf("Memory allocation problems. \n"); gotoMainInputLoop(); } } return inGrid; } /* allocateMemory */ /* This method cycles through the graph and, for each point, looks at all its neighbors and determines which, if any, of them is below the current point. If the current point contains no data then it is discarded and it's direction is just kept as no data. Once this method is complete, the flow direction grid is also complete. *NOTE:* I have checked this file on small grids of my own testing and have found that this method works correctly. The problem was in my flow accumulation methods. */ void flowDir(void) { int i, j; for (j = 0; j < mainGrid->numRows; j++) { for (i = 0; i < mainGrid->numCols; i++) { if (mainGrid->values[i][j] != mainGrid->nodataVal) { float currentLowest = mainGrid->values[i][j]; int x, y; int lowestX, lowestY = 0; for (y = -1; y <= 1; y++) { for (x = -1; x <= 1; x++) { if (i + x >= 0 && i + x <= mainGrid->numCols - 1) { if (j + y >= 0 && j + y <= mainGrid->numRows - 1) { if (mainGrid->values[i+x][j+y] != mainGrid->nodataVal && mainGrid->values[i+x][j+y] < currentLowest) { lowestX = x; lowestY = y; currentLowest = mainGrid->values[i+x][j+y]; } } } } } if (currentLowest != mainGrid->values[i][j]) { if (lowestX == 0 && lowestY == -1) flow->values[i][j] = 1; else if (lowestX == 1 && lowestY == -1) flow->values[i][j] = 2; else if (lowestX == 1 && lowestY == 0) flow->values[i][j] = 3; else if (lowestX == 1 && lowestY == 1) flow->values[i][j] = 4; else if (lowestX == 0 && lowestY == 1) flow->values[i][j] = 5; else if (lowestX == -1 && lowestY == 1) flow->values[i][j] = 6; else if (lowestX == -1 && lowestY == 0) flow->values[i][j] = 7; else /* if (lowestX == -1 && lowestY == -1) */ flow->values[i][j] = 8; } else { flow->values[i][j] = 0; } } else flow->values[i][j] = flow->nodataVal; } } } /* flowDir */ /* This method just cycles through the input grid and makes sure that all values in the array are set to 0. This sets up the grid so we can cycle through it and recursively calculate its values. */ void initAccValues(void) { int i, j; for(j = 0; j < acc->numRows; j++) { for(i = 0; i < acc->numCols; i++) { if (flow->values[i][j] != flow->nodataVal) acc->values[i][j] = 0; else acc->values[i][j] = acc->nodataVal; } } } /* initAccValues */ /* This method just takes a flow direction grid and a flow accumulation grid and loops through the flow accumulation grid calculating each of its values. */ Grid* flowAcc(void) { int i, j; for(j = 0; j < acc->numRows; j++) { for(i = 0; i < acc->numCols; i++) { acc->values[i][j] = getAccValue(i, j); } } return acc; } /* flowAcc */ /* This method takes input flow direction and flow accumulation grids and a point (i, j) and returns the flow accumulation at that point if it has been calculated. If it hasnt it calls a method to compute the flow accumulation at that point and then returning that value. */ float getAccValue(int i, int j) { if (acc->values[i][j] != 0 && acc->values[i][j] != acc->nodataVal) return acc->values[i][j]; else if (acc->values[i][j] == 0) { return (acc->values[i][j] = 1 + sumAccValues(i, j)); } } /* getAccValue */ /* flow directions: i n = 1; -1 0 1 ne = 2; -1 8 1 2 e = 3; j 0 7 3 se = 4; 1 6 5 4 s = 5; sw = 6; w = 7; nw = 8; */ /* This method looks at all values around an input point (i, j) and determines what their flow accumulation is and adding it to a variable. This method enables us to input a flow direction and flow accumulation grid and a point and find out what the flow accumulation is at that point using the flow accumulation values at all the points which flow into it. *NOTE* The problem I had was caused by having if else statements instead of if statements. Dumb mistake :(. */ float sumAccValues(int i, int j) { float sum = 0; if (j > 0 && flow->values[i][j-1] == 5) sum += getAccValue(i, j-1); if (j > 0 && i < acc->numCols -1 && flow->values[i+1][j-1] == 6) sum += getAccValue(i+1, j-1); if (i < acc->numCols - 1 && flow->values[i+1][j] == 7) sum += getAccValue(i+1, j); if (i < acc->numCols -1 && j < acc->numRows - 1 && flow->values[i+1][j+1] == 8) sum += getAccValue(i+1, j+1); if (j < acc->numRows - 1 && flow->values[i][j+1] == 1) sum += getAccValue(i, j+1); if (i > 0 && j < acc->numRows - 1 && flow->values[i-1][j+1] == 2) sum += getAccValue(i-1, j+1); if (i > 0 && flow->values[i-1][j] == 3) sum += getAccValue(i-1, j); if (i > 0 && j > 0 && flow->values[i-1][j-1] == 4) sum += getAccValue(i-1, j-1); return sum; } /* sumAccValues */ /* This function handles all of the special key bindings. */ void specialKey(int key, int x, int y) { switch (key) { case GLUT_KEY_DOWN: pitch -= 1; glutPostRedisplay(); break; case GLUT_KEY_UP: pitch += 1; glutPostRedisplay(); break; case GLUT_KEY_RIGHT: yaw += 1; glutPostRedisplay(); break; case GLUT_KEY_LEFT: yaw -= 1; glutPostRedisplay(); break; } } /* specialKey */ /* This method handles the key binding for the 2d viewing */ void keypress2d(unsigned char key, int x, int y) { switch(key) { case 'q': exit(0); break; case 'w': if (fillToggle == 0) fillToggle = 1; else fillToggle = 0; glutPostRedisplay(); break; case 'e': if (colorScheme < 2) colorScheme ++; else colorScheme = 0; glutPostRedisplay(); break; } } /* keypress2d */ /* This method handles all the key binding for the 3d programs. */ void keypress3d(unsigned char key, int x, int y) { switch(key) { case 'q': /* glutDestroyWindow(glutGetWindow()); I attempted to use this line of code to just close the currently opened window and not quit the program but it kept on giving me segmentation faults and I was unsure as to why. */ exit(0); break; case 'w': /* this toggles fill on and off */ if (fillToggle == 0) fillToggle = 1; else fillToggle = 0; glutPostRedisplay(); break; case '>': /* this increases and decreases the resolution */ resolution ++; glutPostRedisplay(); break; case '<': if (resolution > 1) { resolution --; glutPostRedisplay(); } break; case 'e': /* this cycles through the color schemes for the terrain */ if (colorScheme < 2) colorScheme ++; else colorScheme = 0; glutPostRedisplay(); break; case 'r': /* this increases and decreases the relief of the terrain */ heightLevel++; glutPostRedisplay(); break; case 'R': if (heightLevel > 1) { heightLevel--; glutPostRedisplay(); } break; case 'y': /* this resets the position of the car */ { int randX, randY, pos; randX = rand()%mainGrid->numCols/2; pos = rand()%2; if (pos == 1) randX *= -1; randY = rand()%mainGrid->numRows/2; pos = rand()%2; if (pos == 1) randY *= -1; car[0] = randX; car[1] = randY; car[2] = 0; } break; /* these keys handle the movement and rotation of the landscape */ case 'x': rot[0] += 5; glutPostRedisplay(); break; case 'X': rot[0] -= 5; glutPostRedisplay(); break; case 'c': rot[1] += 5; glutPostRedisplay(); break; case 'C': rot[1] -= 5; glutPostRedisplay(); break; case 'z': rot[2] += 5; glutPostRedisplay(); break; case 'Z': rot[2] -= 5; glutPostRedisplay(); break; case 'a': pos[2] -= 0.2; glutPostRedisplay(); break; case 'A': pos[2] += 0.2; glutPostRedisplay(); break; case 's': pos[0] -= 0.2; glutPostRedisplay(); break; case 'S': pos[0] += 0.2; glutPostRedisplay(); break; case 'd': pos[1] -= 0.2; glutPostRedisplay(); break; case 'D': pos[1] += 0.2; glutPostRedisplay(); break; /* this resets the veiw to the intial position */ case 't': initView(); glutPostRedisplay(); break; /* keys that handle the movement of the camera */ case 'b': eye[2] += 0.05; glutPostRedisplay(); break; case 'B': eye[2] -= 0.05; glutPostRedisplay(); break; } } /* keypress3d */ /* This method just initializes the values within the menu when you right-click the mouse. */ void main_menu(int value) { switch (value) { case 1: if (fillToggle == 0) fillToggle = 1; else fillToggle = 0; glutPostRedisplay(); break; case 2: if (colorScheme < 2) colorScheme++; else colorScheme = 0; break; case 3: exit(0); } glutPostRedisplay(); } /* main_menu */ /* This just prints out information about the grid. */ void printInfo(Grid* inGrid) { printf("\nThe name of the file is: %s\n", inGrid->name); printf("The number of columns in the file is: %d\n", inGrid->numCols); printf("The number of rows in the file is: %d\n", inGrid->numRows); printf("The xll corner is: %f\n", inGrid->xllcorner); printf("The yll corner is: %f\n", inGrid->yllcorner); printf("The cell size is: %f\n", inGrid->cellsize); printf("The NODATA value is: %d\n\n", inGrid->nodataVal); } /* This method opens a specified file for writing (overwriting data if the file already exists) and writes a file similar to the original input file but with the adjusted data values. */ void writeGrid(Grid* inGrid, char* filename) { /* Opens the specified file for writing */ FILE* fp; fp = fopen(filename, "w+"); if (fp == NULL) { printf("Error opening write file: %s \n", filename); gotoMainInputLoop(); } /* Begins writing the header information to the file */ if (fprintf(fp, "ncols \t\t %d\n", inGrid->numCols) < 0) { printf("Error writing file.\n"); gotoMainInputLoop(); } if (fprintf(fp, "nrows \t\t %d\n", inGrid->numRows) < 0) { printf("Error writing file. \n"); gotoMainInputLoop(); } if (fprintf(fp, "xllcorner \t %f\n", inGrid->xllcorner) < 0) { printf("Error writing file. \n"); gotoMainInputLoop(); } if (fprintf(fp, "yllcorner \t %f\n", inGrid->yllcorner) < 0) { printf("Error writing file. \n"); gotoMainInputLoop(); } if (fprintf(fp, "cellsize \t %f\n", inGrid->cellsize) < 0) { printf("Error writing file. \n"); gotoMainInputLoop(); } if (fprintf(fp, "NODATA_VALUE \t %f\n", inGrid->nodataVal) < 0) { printf("Error writing file. \n"); gotoMainInputLoop(); } /* loops through the array of information and writes it to the specified file, checking for errors throughout the write process. */ int i, j; for (i = 0; i < inGrid->numRows; i++) { for (j = 0; j < inGrid->numCols; j++) { if (fprintf(fp, " %f", inGrid->values[j][i]) < 0) { printf("Error writing file.\n"); gotoMainInputLoop(); } } fprintf(fp,"\n"); } fclose(fp); } /* writeGrid */ /* Opens a file for reading and then parses it correctly, filling in the values of the grid structure accordingly */ Grid* readGrid(char* filename){ Grid* inGrid = (Grid*)malloc(sizeof(Grid)); if (inGrid == NULL) { printf("Memory allocation problems"); gotoMainInputLoop(); } /* attemps to open the file and returns an error if there are problems */ FILE* fp; fp = fopen(filename, "r"); if (fp == NULL) { printf("Invalid file name\n"); gotoMainInputLoop(); } inGrid->name = filename; char name[10]; float numF = 0; int numI = 0; /* ncols */ if (fscanf(fp, "%s", name) == EOF || fscanf(fp, "%d",&numI) == EOF) { printf("Error reading file.\n"); gotoMainInputLoop(); } inGrid->numCols = numI; /* nrows */ if (fscanf(fp, "%s", name) == EOF || fscanf(fp, "%d", &numI) == EOF) { printf("Error reading file.\n"); gotoMainInputLoop(); } inGrid->numRows = numI; /* x11corner */ if (fscanf(fp, "%s", name) == EOF || fscanf(fp, "%f", &numF) == EOF) { printf("Error reading file.\n"); gotoMainInputLoop(); } inGrid->xllcorner = numF; /* y11corner */ if (fscanf(fp, "%s", name) == EOF || fscanf(fp, "%f", &numF) == EOF) { printf("Error reading file.\n"); gotoMainInputLoop(); } inGrid->yllcorner = numF; /* cellsize */ if (fscanf(fp, "%s", name) == EOF || fscanf(fp, "%f", &numF) == EOF) { printf("Error reading file.\n"); gotoMainInputLoop(); } inGrid->cellsize = numF; /* NODATA value */ if (fscanf(fp, "%s", name) == EOF || fscanf(fp, "%f", &numF) == EOF) { printf("Error reading file.\n"); gotoMainInputLoop(); } inGrid->nodataVal = numF; inGrid->values = (float**)malloc(sizeof(float*)*(inGrid->numCols)); if (inGrid->values == NULL){ printf("Memory allocation problems.\n"); gotoMainInputLoop(); } int i; for (i = 0;i < inGrid->numCols; i++) { inGrid->values[i] = (float*)malloc(sizeof(float)*(inGrid->numRows)); if (inGrid->values[i] == NULL) { printf("Memory allocation problems. \n"); gotoMainInputLoop(); } } inGrid->smallestHeight = 999.0; inGrid->largestHeight = -999.0; int currentRow = 0; int currentCol = 0; while(fscanf(fp, "%f", &numF) != EOF) { inGrid->values[currentCol][currentRow] = numF; if (inGrid->values[currentCol][currentRow] > inGrid->largestHeight) inGrid->largestHeight = inGrid->values[currentCol][currentRow]; else if (inGrid->values[currentCol][currentRow] < inGrid->smallestHeight && inGrid->values[currentCol][currentRow] - inGrid->nodataVal) inGrid->smallestHeight = inGrid->values[currentCol][currentRow]; currentCol++; if (currentCol == inGrid->numCols) { currentCol = 0; currentRow++; } } fclose(fp); if (inGrid->numCols > inGrid->numRows) ratio = 2/((float)inGrid->numCols); else ratio = 2/((float)inGrid->numRows); return inGrid; } /* readGrid */