//Well this is gonna be quite the little tutorial //-------------------------------------------------- //First section: Variables //In this application, we use the variables: // int mapX; // int mapY; // int mapZ; //To hold our X,Y and Z coordinates for our heightmap //during the application. //We use: // const MapWidth = 384; // const MapHeight = 384; //To hold the width and height(length) for our heightmap //these have to be 3 times the size of the width and height //of our RAW file as our RAW file is parsed in intervals //of 3. So our 128 X 128 heightmap is really 384 X 384. //We then set up our texture storage space with: // GLuint texture[10]; //I have set up enough storage for 11 textures //for the next installment of terrain generation. //Now the next variable holds all our heights with: // BYTE HeightMap[MapWidth][MapHeight]; //I have set it to BYTE as all our data from our RAW //file is read in BYTES as Binary. //--------------------------------------------------- //Section 2: Loading the HeightMap Heights //Our first function is called LoadHeightMap //with arguments: (char* Filename, int Width, int Height) //this will be called on startup to read in our HeightMap //and allocate the data accordingly in our HeightMap array. //Within this I have the variable int smoothen, this will be //used later on in a loop to parse the heights and average //them amongst there surrounding heights for smoother //looking terrain. //We also have the variable FILE *File which will be the //file we are opening (heightmap.raw). //Next we set our File to our currently selected filename //and read it with the properties "rb" meaning read-binary. //Then we acctually read the file into our Heightmap array //in intervals of 1 byte per array for as many times as //our files width X height X 3 (remembering our RAW file //is parsed in intervals of 3). //Now that we have all the data within our array, we close //the file. //Next comes the smoothing, to smooth, I have gone through //every height within our array and averaged it out amongst //its surrounding heights. Simple addition and division to //get the average. I have parsed this through 3 times to get //an alright looking terrain. //--------------------------------------------------- //Section 3: Displaying the HeightMap //To get the optimum look out of the terrain, I am loading every //fourth height and setting them 4 units apart. If I set them //1 unit apart and call every 1 then the terrain looks jagged. //And if I set them further apart and call less, then it looks //too smooth for me. So you can change these as you see fit. //Now acctually displaying them is simple enough. I am using triangle //strips and am calling a triangle strip for every width and height. //Within this I am also setting our texture coordinates. Self explanatory //if you know about triangle strips. //--------------------------------------------------- //Section 4: Optimization //To optimise our terrain to get the best amount of frames //per second, I have added some Culling in the enabling //code to discard faces we dont need (remember to set the front face to CCW //- counter clock wise -as triangle strips set there faces opposite to most //shapes). //Also by calling only every fourth height, I am saving the processing //times and thus also increasing the frames per second. #include #include #include #include #include #include int mapX; int mapY; int mapZ; const MapWidth = 384; const MapHeight = 384; GLuint texture[10]; BYTE HeightMap[MapWidth][MapHeight]; GLuint LoadTexture( const char * filename, int width, int height ) { GLuint texture; unsigned char * data; FILE * file; //The following code will read in our RAW file file = fopen( filename, "rb" ); if ( file == NULL ) return 0; data = (unsigned char *)malloc( width * height * 3 ); fread( data, width * height * 3, 1, file ); fclose( file ); glGenTextures( 1, &texture ); //generate the texture with the loaded data glBindTexture( GL_TEXTURE_2D, texture ); //bind the texture to it's array glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); //set texture environment parameters //here we are setting what textures to use and when. The MIN filter is which quality to show //when the texture is near the view, and the MAG filter is which quality to show when the texture //is far from the view. //The qualities are (in order from worst to best) //GL_NEAREST //GL_LINEAR //GL_LINEAR_MIPMAP_NEAREST //GL_LINEAR_MIPMAP_LINEAR //And if you go and use extensions, you can use Anisotropic filtering textures which are of an //even better quality, but this will do for now. glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR ); //Here we are setting the parameter to repeat the texture instead of clamping the texture //to the edge of our shape. glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); //Generate the texture with mipmaps gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data ); free( data ); //free the texture return texture; //return whether it was successfull } void FreeTexture( GLuint texture ) { glDeleteTextures( 1, &texture ); } void LoadHeightMap (char* Filename, int Width, int Height) { int smoothen; FILE * File; File = fopen( Filename, "rb" ); fread( HeightMap, 1, Width * Height * 3, File ); fclose( File ); for (smoothen = 0; smoothen < 3; smoothen++){ for (mapX = 1; mapX < Width; mapX ++){ for (mapZ = 1; mapZ < Height * 3; mapZ++){ mapY = HeightMap[mapX][mapZ]; mapY += HeightMap[mapX-1][mapZ]; mapY += HeightMap[mapX+1][mapZ]; mapY += HeightMap[mapX][mapZ-1]; mapY += HeightMap[mapX][mapZ+1]; mapY += HeightMap[mapX-1][mapZ-1]; mapY += HeightMap[mapX-1][mapZ+1]; mapY += HeightMap[mapX+1][mapZ-1]; mapY += HeightMap[mapX+1][mapZ+1]; mapY = mapY/9; HeightMap[mapX][mapZ] = mapY ; } } } } void DisplayHeightMap (void) { int Height; for (mapX = 1; mapX < MapWidth; mapX +=4){ for (mapZ = 1; mapZ < MapHeight * 3; mapZ+=4){ glBegin(GL_TRIANGLE_STRIP); Height = HeightMap[mapX][mapZ]; glTexCoord2f(0,0); glVertex3f(float(mapX),Height,float(mapZ)); Height = HeightMap[mapX][mapZ+4]; glTexCoord2f(0,1); glVertex3f(float(mapX),Height,float(mapZ+4)); Height = HeightMap[mapX+4][mapZ]; glTexCoord2f(1,0); glVertex3f(float(mapX+4),Height,float(mapZ)); Height = HeightMap[mapX+4][mapZ+4]; glTexCoord2f(1,1); glVertex3f(float(mapX+4),Height,float(mapZ+4)); glEnd(); } } } void enable (void) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glFrontFace(GL_CCW); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glShadeModel (GL_SMOOTH); glEnable(GL_TEXTURE_2D); } void display (void) { glClearColor (0.0,0.0,0.0,1.0); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); enable(); glTranslatef(30, 0, 30); glRotatef(150, 0,1,0); glColor3f(1,1,1); glBindTexture(GL_TEXTURE_2D, texture[0]); DisplayHeightMap(); glutSwapBuffers(); } void init (void) { texture[0] = LoadTexture("texture.raw", 256, 256); LoadHeightMap ("height.raw", 128, 128); } void reshape (int w, int h) { glViewport (0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective (100, (GLfloat)w / (GLfloat)h, 0.01, 500.0); glMatrixMode (GL_MODELVIEW); } int main (int argc, char **argv) { glutInit (&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow ("A basic OpenGL Window"); init(); glutDisplayFunc (display); glutIdleFunc (display); glutReshapeFunc (reshape); glutMainLoop (); return 0; }