Loading a heightmap

BlitzMax Forums/BlitzMax Beginners Area/Loading a heightmap

AltanilConard(Posted 2007) [#1]
I was trying to load in a height map, to generate a terrain with it using MiniB3D, but I'm having problems with getting the actualy height from a pixel value.
This is what I have now:
Global world:TPixmap	

Function LoadTerrainFromHeightMap(hmap:String)
	
	'load the heightmap image
	world = LoadPixmap(hmap)
	If Not world Then RuntimeError("can't load heightmap")
	
	'load pixmap dimensions
	ZoneX = world.width
	ZoneZ = world.height
	
	'create the array that will hold the height of the terrain
	Global ZN:Float[zonex, zonez]
	
	'loop trough the whole map, storing the height
	For x:Int = 0 To ZoneX - 1
		For z:Int = 0 To ZoneZ - 1
			
			'read the pixel and store in array
			Local h:Float = ReadPixel(world, x, (ZoneZ - 1) - z)
			h# = Abs(h#)
			ZN#(x, z) = (h#)
			
		Next
	Next

	RuntimeError(ZN#(1,1))
	
End Function

LoadTerrainFromHeightMap("bitmap.bmp")


For testing purpose I used a black bitmap, and it returned the value 16777260, wich is quite weird since I figured a black bitmap should return 0, and a white bitmap should return 16777216 (R*G*B=256^3).

And once I get the right rgb value from my pixelmap, I would have no idea how to get a good value from it that could serve as my Y variable when generating the terrain.

(I allready made the function that generates the terrain, the only problem I'm having is reading the height.)

Could anyone help me out please?

Thanks in advantage.

*edit*
I used a runtimeerror to return the value btw. :P


impixi(Posted 2007) [#2]
When saving a heightmap to an image file, I tend to use a single colour component (eg blue), providing for only 256 discreet levels, but that’s usually enough for my purposes. I then use bitwise operators when reading in the pixel value. Here’s some sample code (ignore the encoded media stuff):


EDIT: Actually, I think there's a bug in my code... I'll get back to you...

EDIT2: There we go. That's better...



Let me know if you want me to explain anything in that code.

Incidentally, there’s some stuff in the code archives related to heightmaps: Heightmap Toolbox V0.3.6


impixi(Posted 2007) [#3]
More specific to your question, if you could post some code that demonstrates how you are converting your height map data into image data, it might help.

A common technique is as follows:

Saving

* Generate the height map array (of floats, doubles or integers, etc).
* Create a blank image having the same dimensions as the array.
* For each coordinate:
# Derive a byte value from the height value.
# Use the byte value for the Red, Green and Blue component of the corresponding image pixel. (Displays as a ‘grey’ colour).
* Save the image.

Loading

* Load the height map image.
* Create a height map array (of floats, doubles or integers, etc) having the same dimensions as the image.
* For each pixel in the image:
# Read the pixel as an integer
# Use a bitwise operator to read a single colour component (byte value).
# Convert the byte value into a height value and place it in the corresponding array coordinate.


AltanilConard(Posted 2007) [#4]
Thank you very much for the great example. I had allready looked into your HeightMap Toolbox you posted in the Code Archives, but I didn't really understand the code I found there, fortunatelly I do now with your explanation.

In my code I didn't export my map data to image data yet, I made my heightmap (for testing purpose) in paint and saved it as a bitmap. I guess images created in paint won't serve as heightmaps, since they don't work with your code either.
Well I guess I'll just have to write my own terrain editor to export heightmaps (not that I mind).

Just one question, what excatly does this piece of code do:
c :| c Shr 24
Map[x, z] = $000000 | c



impixi(Posted 2007) [#5]
I had allready looked into your HeightMap Toolbox you posted in the Code Archives, but I didn't really understand the code I found there


Heh. The intention with that code was to create a ‘class’ that could just be plugged-in / imported into an existing project. I suspect I’ve failed. Possibly I need to provide a collection of simple, old-fashioned functions instead.

As for those two lines of code, that I ripped from my Heightmap Toolbox written over a year ago, on closer analysis I think I must have been drunk at the time!

Here's a simpler method:

   	For Local x:Int = 0 To (Width - 1)
   		For Local z:Int = 0 To (Height - 1)
      		      Local c:Int = ReadPixel(pm, x, z)
      		      Map[x, z] = c & 255
   		Next
   	Next 


Variable c is a 32 bit signed integer (or, more importantly, 4 bytes), used to store a pixel’s properties: Red, Green, Blue, Alpha. Each byte in the integer corresponds to one of those properties, meaning they each can range in value from 0 to 255.

In order to extract only a single pixel property we use Bitwise AND (&). To get the value of the last byte in the integer, we & c with decimal 255.

Example:

In the sample image, test.png, coordinate 90, 100 has a decimal pixel value of:
16645629.

Decimal:
16645629
AND 255
= 253 (the height value we’re after)

Binary:
00000000111111011111110111111101
00000000000000000000000111111111
-----------------------------------------------
00000000000000000000000111111101

If you need an explanation of bitwise operations this article might help.


EASIER STILL:

Since I’m only using the final byte to store the height value, I could exploit BlitzMax’s Int to Byte type casting behaviour. Change the import loop to:

   	For Local x:Int = 0 To (Width - 1)
   		For Local z:Int = 0 To (Height - 1)
      			Map[x, z] = ReadPixel(pm, x, z)
   		Next
   	Next


Since each Map element is of type Byte, any integer assigned there will have its first three bytes discarded during the cast.


impixi(Posted 2007) [#6]


I made my heightmap (for testing purpose) in paint and saved it as a bitmap




You can do that. You just need write import code that 'knows' how you're mapping colours to heights. My example uses the red colour component. 0 = low, 255 = high. So any colour you draw with should be chosen accordingly.


AltanilConard(Posted 2007) [#7]
Wo thanks, this cleared up alot :)
And don't get me wrong, you're Heightmap Toolbox works fine, it's just that I didn't want to blind copy your functions so I tried to actually 'understand' what they did (wich I do now).

I'm almost done making a terrain editor now, thanks for all the help.