www.pudn.com > Map.GL.FPS.zip > Terrain.cpp
//***********************************************************************//
// //
// - "Talk to me like a 3 year old!" Programming Lessons - //
// //
// $Author: DigiBen DigiBen@GameTutorials.com //
// //
// $Program: Height Map //
// //
// $Description: This shows how render a height map from a file. //
// //
// $Date: 7/12/01 //
// //
//***********************************************************************//
#include "main.h"
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// This file handles all of the terrain functions.
//
// * NOTE *
//
// The terrain might look small on the screen, and considering how fast we
// move about it, but it's actually quite huge and could be a whole world
// to walk around. So don't be fooled :)
//
//
//
///////////////////////////////// HEIGHT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns the height into the height map
/////
///////////////////////////////// HEIGHT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
int Height(BYTE *pHeightMap, int X, int Y)
{
// This is used to index into our height map array.
// When ever we are dealing with arrays, we want to make sure
// that we don't go outside of them, so we make sure that doesn't
// happen with a %. This way x and y will cap out at (MAX_SIZE - 1)
int x = X % MAP_SIZE; // Error check our x value
int y = Y % MAP_SIZE; // Error check our y value
if(!pHeightMap) return 0; // Make sure our data is valid
// Below, we need to treat the single array like a 2D array.
// We can use the equation: index = (x + (y * arrayWidth) ).
// This is assuming we are using this assumption array[x][y]
// otherwise it's the opposite. Now that we have the correct index,
// we will return the height in that index.
return pHeightMap[x + (y * MAP_SIZE)]; // Index into our height array and return the height
}
///////////////////////////////// SET VERTEX COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This sets the color value for a particular index, depending on the height index
/////
///////////////////////////////// SET VERTEX COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void SetVertexColor(BYTE *pHeightMap, int x, int y)
{
if(!pHeightMap) return; // Make sure our height data is valid
// Here we set the color for a vertex based on the height index.
// To make it darker, I start with -0.15f. We also get a ratio
// of the color from 0 to 1.0 by dividing the height by 256.0f;
float fColor = -0.15f + (Height(pHeightMap, x, y ) / 256.0f);
// Assign this green shade to the current vertex
glColor3f(0, fColor, 0 );
}
///////////////////////////////// RENDER HEIGHT MAP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This renders the height map as QUADS
/////
///////////////////////////////// RENDER HEIGHT MAP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void RenderHeightMap(BYTE pHeightMap[])
{
int X = 0, Y = 0; // Create some variables to walk the array with.
int x, y, z; // Create some variables for readability
float fColor = 0.0f; // Create a variable to hold our color of the polygon
if(!pHeightMap) return; // Make sure our height data is valid
glBegin( GL_QUADS ); // Render Quads
// Next we actually need to draw the terrain from the height map.
// To do that, we just walk the array of height data and pluck out
// some heights to plot our points. If we could see this happening,
// it would draw the columns first (Y), then draw the rows.
// Notice that we have a STEP_SIZE. This determines how defined our
// height map is. The higher the STEP_SIZE, the more blocky the terrain
// looks, while the lower it gets, the more rounded it becomes.
// If we set STEP_SIZE = 1 it would create a vertex for every pixel in the height map.
// I chose 16 as a decent size. Anything too much less gets to be insane and slow.
// Of course, you can increase the number when you get lighting in.
// Then vertex lighting would cover up the blocky shape. Instead of lighting,
// we just put a color value associated with every poly to simplify the tutorial.
// The higher the polygon, the brighter the color is.
for ( X = 0; X < MAP_SIZE; X += STEP_SIZE )
for ( Y = 0; Y < MAP_SIZE; Y += STEP_SIZE )
{
// Get the (X, Y, Z) value for the bottom left vertex
x = X;
y = Height(pHeightMap, X, Y );
z = Y;
// Set the color value of the current vertice
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Send this vertex to OpenGL to be rendered (integer points are faster)
// Get the (X, Y, Z) value for the top left vertex
x = X;
y = Height(pHeightMap, X, Y + STEP_SIZE );
z = Y + STEP_SIZE ;
// Set the color value of the current vertex
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Send this vertex to OpenGL to be rendered
// Get the (X, Y, Z) value for the top right vertex
x = X + STEP_SIZE;
y = Height(pHeightMap, X + STEP_SIZE, Y + STEP_SIZE );
z = Y + STEP_SIZE ;
// Set the color value of the current vertex
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Send this vertex to OpenGL to be rendered
// Get the (X, Y, Z) value for the bottom right vertex
x = X + STEP_SIZE;
y = Height(pHeightMap, X + STEP_SIZE, Y );
z = Y;
// Set the color value of the current vertice
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Send this vertex to OpenGL to be rendered
}
glEnd();
// Reset the color
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
///////////////////////////////// LOAD RAW FILE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This loads a .raw file into an array of bytes. Each value is a height value.
/////
///////////////////////////////// LOAD RAW FILE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void LoadRawFile(LPSTR strName, int nSize, BYTE *pHeightMap)
{
FILE *pFile = NULL;
// Let's open the file in Read/Binary mode.
pFile = fopen( strName, "rb" );
// Check to see if we found the file and could open it
if ( pFile == NULL )
{
// Display our error message and stop the function
MessageBox(NULL, "Can't find the height map!", "Error", MB_OK);
return;
}
// Here we load the .raw file into our pHeightMap data array.
// We are only reading in '1', and the size is the (width * height)
fread( pHeightMap, 1, nSize, pFile );
// After we read the data, it's a good idea to check if everything read fine.
int result = ferror( pFile );
// Check if we received an error.
if (result)
{
MessageBox(NULL, "Can't get data!", "Error", MB_OK);
}
// Close the file.
fclose(pFile);
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
/////////////////////////////////////////////////////////////////////////////////
//
// * QUICK NOTES *
//
// Though being one of the simplest ways to display terrain from a height map,
// it's a good start. The next step we would take would be to make a quad tree
// that only displayed the vertices that we could see from our view frustum.
// This would allow us to display large terrains, but not have to push so many polygons.
// This is a form of space partitioning. There isn't as many good places to learn
// about space partitioning as I would like, but the best place is www.GameInstitute.com.
// They have an excellent terrain rendering course that deals with all of these problems,
// including line of sight, terrain rendering techniques, adaptable meshes, geomorphing, etc..
//
// Let's go over the steps that we accomplished during this tutorial
//
// 1) First, we need to read the height map from the .raw file. This is simple because
// there is no header to a .raw file, it is just the image bits. This file format
// isn't what you generally want to use because you have to either know what the
// size and type are, or guess, but I thought it fitting for this tutorial.
//
// 2) After we read our height map data, we then needed to display it. This was
// also a simple function because we are just making QUADS with a set size.
// I chose to do 16 by 16 quads, but you can change this to what ever you want.
// With our height map array, we treated it as a 2D array and did 2 for loops
// to draw each quad for each row and column. Instead of doing lighting, I
// just gave each vertex a green intensity. depending on it's height. This makes
// the terrain look like there is lighting applied. This also makes it easier to
// see the definition in the terrain until lighting and texture maps are applied.
//
//
// That's pretty much it. As for doing texturing, this would be a good way to do
// texture tiling. You can go into a paint program and create a blank
// file the size of your height map. This will allow you to set up your textures
// to see how you want it to look. Then, make a 2D array that stores an index
// into your texture array, then just bind that texture to the current quad that matches.
// it's X and Y position. Maybe we will write a tutorial on this.
//
// The other option is to let OpenGL find the texture coordinates for you, or you can
// find them yourself by stretching a texture over the whole thing (which isn't always desirable),
// or wrap it. Remember, (0, 0) is the top left corner and (1,1) is the bottom right corner.
// You will have to find the ratio you want to wrap it with. Here is an example:
//
// glTexCoord2f( (float)x / (float)MAP_SIZE,
// - (float)z / (float)MAP_SIZE );
//
// Make sure you cast integers to floats when necessary otherwise it will truncate it.
// The z is actually the Y value of the height map, but the z value in world coordinates.
// Check out the next height map tutorial to see texturing a height map in action,
// along with rendering the terrain using triangle strips.
//
// Good luck!
//
//
// Ben Humphrey (DigiBen)
// Game Programmer
// DigiBen@GameTutorials.com
// Co-Web Host of www.GameTutorials.com
//
//