|
|
|
 |
Reply From: |
Zylann |
You don’t have to set them all “by hand”. A heightmap can come from various sources, but the most common one is an image, and there is a way to pass image data to the shape.
To work with this shape, your image must be in the FORMAT_RF
format (32-bit float precision, single channel). If your image is 8-bit, this won’t work well, because colors can only have 255 values are are quantified between 0 and 1, while heights can be somewhere between -500 to 500 for example.
I think there isn’t a built-in tool yet to set it up in the editor (only plugins), but here is a script example:
var heightmap = Image.new()
# Load EXR file, one of the formats Godot can handle.
heightmap.load("file.exr")
# Godot's EXR loader still doesn't load properly single-channel images,
# so you should make sure to convert it
heightmap.convert(Image.FORMAT_RF)
# Create the shape (if you don't have one already)
var shape = HeightMapShape.new()
# Assign size first, otherwise it won't work
shape.map_width = heightmap.get_width()
shape.map_height = heightmap.get_height()
# Assign the heights using the image's raw data.
# Because the format matches, this is straightforward
shape.map_data = heightmap.get_data()
# Now all is left to do is to assign the shape to your collision node.
This still needs to be improved, both in usability and efficiency (setting width and height allocates a whole heightmap for no reason since it gets set from the image anyways).
Thanks for your quick answer!
It had just a little typo where map_height should be map_depth. I also think that you shouldn’t declare shape as a variable because it’s already a property of the CollisionShape.
My code looks like this:
extends CollisionShape
var heightmap = Image.new()
func _ready():
# Load EXR file, one of the formats Godot can handle.
heightmap.load("res://assets/sprites/hm2.exr")
# Godot's EXR loader still doesn't load properly single-channel images,
# so you should make sure to convert it
heightmap.convert(Image.FORMAT_RF)
# Create the shape (if you don't have one already)
shape = HeightMapShape.new()
# Assign size first, otherwise it won't work
shape.map_width = heightmap.get_width()
shape.map_depth = heightmap.get_height()
# Assign the heights using the image's raw data.
# Because the format matches, this is straightforward
shape.map_data = heightmap.get_data()
# Now all is left to do is to assign the shape to your collision node.
But when I try running the code it throws this error message:

I noticed that Godot docs says that map_data data type should be PoolRealArray not PoolByteArray. Could this be the problem?
Kimmo_12345 | 2020-03-19 20:16
Yeah I’ve thrown out this code as an untested example, feel free to adapt 
I noticed that Godot docs says that map_data data type should be PoolRealArray not PoolByteArray. Could this be the problem?
Ugh. That’s annoying.
Weeeeeell
var float_array = PoolRealArray()
heightmap.lock()
var i = 0
for y in heightmap.get_height():
for x in heightmap.get_width():
float_array[i] = heightmap.get_pixel(x, y).r
i += 1
heightmap.unlock()
Then use float_array
instead.
Zylann | 2020-03-19 20:22
Invalid set index '0' (on base: 'PoolRealArray') with value of type 'float'
Just use float_array.append(heightmap.get_pixel(x, y).r)
instead and get rid of the counter.
Tooniis | 2020-06-30 21:16
Just give it proper size heightmap.get_width() * heightmap.get_height()
beforehand. The reason I didnt use append()
is because at the time, Godot was reallocating the entire array everytime a single value was added, which adds up to horrible performance loss on large maps.
Zylann | 2020-06-30 22:37