Sign in to follow this  
bellicose

Problem - 2D Random Map in OpenGL

Recommended Posts

I have been trying to generate a tile map off of a heightmap but have been running into issues. I am doing it using OpenGL in glOrtho projection so I have been mainly using NeHe Tutorial #34 as reference. The heightmap is being generated by LibNoise (which I will later use to generate random maps each time). It has worked when I made the array by hand myself, but obviously that is tedious and I want the maps to be random. This is what I get when I run my program: http://www.boringbox.com/images/example1.jpg That is zoomed out so that the entire map is visible. A closer shot: http://www.boringbox.com/images/example2.jpg When zoomed in to the normal level, each tile is 32x32. The heightmap used for that was http://www.boringbox.com/images/heightmap.bmp I tried a simpler heightmap (http://www.boringbox.com/images/heightmap2.bmp) to see if it would help me make any sense out of it. The result for the new map was: http://www.boringbox.com/images/example3.jpg As you can see, there seems to be either strips or spaces in between rows, and the map is being generated completely wrong. Here is the relevant code: MAP_SIZE and the array
#define MAP_SIZE	256		

...

BYTE g_HeightMap[MAP_SIZE*MAP_SIZE];  //Array to hold the heightmap


BMP load function and part of InitGL
void LoadBMPFile(LPSTR strName, int nSize, BYTE *pHeightMap)
{
	FILE *pFile = NULL;

	// 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 Error Message And Stop The Function
		MessageBox(NULL, "Can't Find The Height Map!", "Error", MB_OK);
		return;
	}

	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, "Failed To Get Data!", "Error", MB_OK);
	}

	// Close The File.
	fclose(pFile);
}


int InitGL(GLvoid)										// All Setup For OpenGL Goes Here
{
	...
			
	LoadBMPFile("heightmap.bmp", MAP_SIZE * MAP_SIZE, g_HeightMap);

        ...
	
	return TRUE;										// Initialization Went OK
}


The Height() function
int Height(BYTE *pHeightMap, int X, int Y)				// This Returns The Height From A Height Map Index
{
	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

	return pHeightMap[x + (y * MAP_SIZE)];				// Index Into Our Height Array And Return The Height
}


The Draw function
void RenderWorld(BYTE pHeightMap[])
{
	int tile;
	int X = 0, Y = 0;

	//Loop through the array. 

	for (Y = 0; Y < MAP_SIZE; Y++)
	{
		for (X = 0; X < MAP_SIZE; X++)
		{
			glLoadIdentity();

			//tile is equal to the height given by the Height function

			tile = Height(pHeightMap, X, Y );

			//Basic tiling for test purposes

			if (tile < 50)
			{
				tile = 0;					//Deep water
			}
			if (tile > 49 && tile < 120)
			{
				tile = 1;					//Shallow water
			}
			if (tile > 119 && tile < 140)
			{
				tile = 2;					//Sand
			}
			if (tile > 139 && tile < 190)
			{
				tile = 3;					//Grass
			}
			if (tile > 189 && tile < 200)
			{
				tile = 6;					//Light Brush
			}
			if (tile > 199 && tile < 245)
			{
				tile = 8;					//Forest
			}
			if (tile > 244 && tile < 256)
			{
				tile = 5;					//Rock
			}
			if (tile > 256 || tile < 0)
			{
				MessageBox(NULL, "Invalid Height Value", "Error", MB_OK);	//Check if an invalid number was given
			}

			//Draw the tiles

			glEnable(GL_TEXTURE_2D);

			glColor3f(1.0f,1.0f,1.0f);
			glBindTexture(GL_TEXTURE_2D, texture[tile].texID);		//The texture used is determined by 'tile' above
			glBegin(GL_QUADS);

			//offsetX/Y currently used for basic camera movement
			//Quads are textured backwards due to glOrtho 

			glTexCoord2f(0.0f, 1.0f); glVertex2d(X + offsetX, (Y + 1) + offsetY);		//Top left
			glTexCoord2f(1.0f, 1.0f); glVertex2d((X + 1) + offsetX, (Y + 1)+ offsetY);	//Top right
			glTexCoord2f(1.0f, 0.0f); glVertex2d((X + 1) + offsetX, Y + offsetY);		//Bottom right
			glTexCoord2f(0.0f, 0.0f); glVertex2d(X + offsetX, Y + offsetY);				//Bottom left
			glEnd();

			glDisable(GL_TEXTURE_2D);
		}
	 }
}


Essentially I am trying to modify NeHe Tutorial #34 for 2d use. Any glaring errors? It is probably something basic that I keep overlooking. But I really don't know why I am getting these results. Is there any better ways to go about doing this? I understand that since I am using LibNoise I don't need to read the heightmap from a file, but honestly I am not sure how to get the array that LibNoise creates. Thanks for your time, and I appreciate any help that can be given.

Share this post


Link to post
Share on other sites
pHeightMap points to bytes, yes but BMP data is typically stored as RGB data.

You can fix this by either of:
1. make sure you save your bitmap with one channel only (8bits instead of 24bits).
2. When loading the bitmap, read 1 of every 3 bytes from the bmp.
3. Fix your indexing into the height sampling function like so:


int Height(BYTE *pHeightMap, int X, int Y) // This Returns The Height From A Height Map Index
{
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

return pHeightMap[3*x + (y * 3 * MAP_SIZE)]; // Index Into Our Height Array And Return The Height
}


Hope that helps.

[Edited by - rethan on July 24, 2009 4:16:59 PM]

Share this post


Link to post
Share on other sites
I looked at the heightmap2.bmp file properties and I get this:

Width 256
Height 256
Bit Depth 24

Bit depth 24 means you have 3 bytes per component (8 bits each) aka RGB data which confirms what I mentioned above. :)

[Edited by - rethan on July 24, 2009 6:54:34 PM]

Share this post


Link to post
Share on other sites
Thanks for the information! When I applied your fix to the Height function I got a Access violation (0xC0000005). But now that I know the problem I will look into how to either save the BMP in one channel or an alternative way to read only every third bytes.

Thanks again.

edit: Got it working!

http://www.boringbox.com/images/gotit.jpg

Thanks again for your help. There are issues with the map (bottom side, and the evident straight line down the left) but at least now I got it actually showing up!

Share this post


Link to post
Share on other sites
The access violation is because you don't have room in your height array for all the bmp data. (Sorry I forgot to mention that) You have:


BYTE g_HeightMap[MAP_SIZE*MAP_SIZE]; //Array to hold the heightmap

this should be:

BYTE g_HeightMap[3*MAP_SIZE*MAP_SIZE]; //Array to hold the heightmap


Also, when you load the bmp, it should be via:

LoadBMPFile("heightmap.bmp", 3 * MAP_SIZE * MAP_SIZE, g_HeightMap);


But I agree with you that you should look into saving only 1 channel for your bitmap. You should replace the 3's I mentioned by a #define BITMAP_BPP 3,
or better yet have your load bmp file be able to determine the BPP from the file header, which brings me to my last and important point:

The NeHe tutorial loads .raw files not .bmp. BMP files have header data in the file that you would need to skip over/read before you get to the actual RGB data. If you are using the windows API, there are functions you can use that will give you the BMP's RGB data in a byte buffer. Or, you'll have to look at bmp headers to know how many bytes to skip, or you could save your file in the .RAW format.

Hope that helps!

[Edited by - rethan on July 24, 2009 4:24:28 PM]

Share this post


Link to post
Share on other sites
Ill make those changes. The main reason I am using BMP is because I am using LibNoise to generate the maps and save them. Haven't had the time to mess around with anything aside from the tutorials for LibNoise.

But thanks for all the information! I'll make sure to check it all out.

http://www.boringbox.com/images/thanksRethan.jpg

Share this post


Link to post
Share on other sites
The header info your looking for is (in pascal):

tagBITMAPFILEHEADER = packed record
bfType: Word;
bfSize: DWORD;
bfReserved1: Word;
bfReserved2: Word;
bfOffBits: DWORD;
end;


Other useful links http://msdn.microsoft.com/en-us/library/aa930979.aspx (from MS about the file format includes C header), and http://en.wikipedia.org/wiki/BMP_file_format.

Once you have it all working, a good thing to do is use the actual sizes from the bitmap file to set your array size instead of using outside values.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this