Zylann / godot_heightmap_plugin

HeightMap terrain for Godot implemented in GDScript
Other
1.72k stars 159 forks source link

Is it possible to use this plugin for runtime procedural terrain generation #76

Open slapin opened 5 years ago

slapin commented 5 years ago

Is it possible to use this plugin for runtime procedural terrain generation?

Is there some eechanism for me to use runtime-generated height data and other data to build terrain using this plugin? I was implementing my own terrain building system for procedural world, but I thought I could ask if I could make my work simpler...

Zylann commented 5 years ago

It should be usable for that but I need more information about what you actually need to do.

mxnemu commented 5 years ago

I was also looking for infinite procedural terrain in godot. I want to use a seed and generate more terrain as the player moves further out.

  1. Can I pass the generator_dialog params to a script object and receive an hterrain node for it? I had a short look at it and the code in generator/ seemed very specifically coupled to also generate that preview image.

  2. My other clue would be to use godot simplex noise and pass it to hterrain_data._import_heightmap, but that only takes files. So maybe that could be adjusted to read from a in-memory texture to skip the unnecessary hard-drive-operation.

I haven't really tried anything yet. Thanks for the great plugin btw.

Zylann commented 5 years ago

It is possible to generate the terrain on the fly in game, by generating images of the correct format, and storing them in HTerrainData. It would be the same as what the plugin does in the editor (which appears more complex because the use case is different, but it boils down to generating textures). I need to take the time to make an example of this. Paging terrain is a bit more complex because of handling seams, it may require changes a bit deeper.

GammaGames commented 4 years ago

It would be nice if you could pass 2d arrays of floats to a helper and it would return an image. Would also be cool to pass in arrays for detail and splat maps too, though splats would be harder to generate

Zylann commented 4 years ago

It would be nice if you could pass 2d arrays of floats to a helper and it would return an image

I think you can do that with the Image class.

Would also be cool to pass in arrays for detail and splat maps too, though splats would be harder to generate

Just use images, arrays will allocate too much memory and Image is better fit to the task. It's probably less true in C# but that remains a topic about the Image class, not the plugin.

GammaGames commented 4 years ago

I am meaning helpers that would generate the images for you with the given data, which would be useful when you’re generating terrain completely in code

Zylann commented 4 years ago

@GammaGames what kind of helper are you thinking about in code terms?

GammaGames commented 4 years ago

Just a few helpers that would create the images for users, like:

I'm not really sure what the global_albedo texture is for, is that used for chunks that are far away from the camera?

Zylann commented 4 years ago

create_heightmap(data, min=0.0, max=1.0) -> Image

Apart from doing Image.new(), this can't do anything helpful because min and max can't even be stored or have any impact on the return value of this function. Better just do Image.new() then, with the RH format, and iterate through pixels to set them to the values you want. RH allows to directly set the height you want, without the need for quantization, so I don't see why you need data.

create_normalmap(data, min=0.0, max=1.0) -> Image

This one too, min and max make no sense either. You also don't need data since again, you can create an Image and use that as a grid in the first place. Image even has bump_to_normalmap if you want to generate normals on the CPU. Besides, the plugin is already able to bake normals, and if you use the GPU to generate your terrain you won't even need data because it's doable in such shader and you'll have an image already.

create_splatmap(texture1, texture2=[], texture3=[], min=0.0, max=1.0) -> Image

Still makes no sense. A splatmap doesn't need min and max, not even textures (or are these data arrays again?). It's really just an image in which you set r, g, b and a to the values you want, which again, you can do already by using Image in which you basically set pixels.

create_detailmap(data, min=0.0, max=1.0) -> Image

Same.

I'm not really sure what the global_albedo texture is for, is that used for chunks that are far away from the camera?

Global albedo is a baked texture used to tint grass and replace textures at very large distances or minimaps (to hide repeating patterns that are too far away anyways). It is computed from the result of the ground shader, so if you want to generate that one procedurally you either need to use the same shader, or somehow calculate an approximation on th CPU. It's not always needed though, so the plugin doesn't bake it by default.

Currently I think you don't even need Image.new(), you can obtain the image you want by using HTerrainData.get_image(channel, index), where channel would be CHANNEL_HEIGHTMAP (or other), and index be 0 in this case, or higher for multiple grass layers. Once you have that image you can modify it the way you want with set_pixel, blend_rect or set_data, and once you're done, call HTerrainData.notify_region_change(rect) to apply the changes to the graphics card and update height ranges (for culling). This is how the brushes modify the terrain, as well as the generator tool. I just need to find the time to write up an example.

GammaGames commented 4 years ago

this can't do anything helpful because min and max can't even be stored or have any impact on the return value of this function

So what would happen if, instead of passing in values from 0 to 1 , you passed in values from -100 to 100? Or 0 to 255?

The min and max are just helpers so that the function can inverse_lerp to get the correct colors.

Once you have that image you can modify it the way you want

I think you are missing the point. Using images as data structures when generating data is not user friendly. Yes, users can create their own functions and generate the images themselves, but having static helper functions built in would make the barrier for generating terrain through code that much simpler.
Using 2d arrays with numbers that you can easily generate and tweak and then convert into images for the terrain asset is much more user friendly.

Zylann commented 4 years ago

So what would happen if you, instead of passing in values from 0 to 1 , you passed in values from -100 to 100? Or 0 to 255?

They will be -100 to 100, or 0 to 255. The format of the heightmap is designed to allow these ranges, it's not an 8-bit image.

The min and max are just helpers so that the function can inverse_lerp to get the correct colors.

It doesn't need to because when you use the Image API, colors can already be specified within the 0..1 range if the image represents a color (or a ratio).

I think you are missing the point. Using images as data structures when generating data is not user friendly.

But then why would it need to be solved in this plugin then? Even if I provided such helpers they would be really slow, that's a GDScript plugin and I rely solely on Godot's API to run the fast parts. If Image isn't friendly, that's something to solve Godot side.

There is almost no difference between these two methods:

# With an array
array.resize(resolution * resolution)
for y in resolution:
    for x in resolution:
        var i = x + y * resolution
        array[i] = *generate height*
# Then later you have to convert the whole array into an image which doubles your overhead
# With an image
im.lock()
for y in resolution:
    for x in resolution:
        im.set_pixel(x, y, Color(*generate height*, 0, 0))
im.unlock()

The first one is just slower and uses more memory.

GammaGames commented 4 years ago

You still aren't understanding the point. This was never a question about which was the fastest, it is obvious directly setting pixels on an image would be the fastest. This is about making it easier to generate these images directly with code. Let's just take your own quote from above:

[...] it boils down to generating textures.

Instead of requiring the user to create images and modify them directly, they could generate their terrain data (add erosion, different passes for biomes and types of terrain, etc) and use the function to generate the texture for them.

Zylann commented 4 years ago

@GammaGames sure, but I don't believe that avoiding Image makes things easier as well. I wrote the code above to show you it makes no big difference. If you desperately need some helper just so you can use arrays, you could PR them to the util.gd class to convert to images since it has no real relation to terrain stuff (or... have your own, since there are plenty of ways to use arrays of different kinds).

Zylann commented 4 years ago

FYI I added an article about procedural generation using GDScript: https://github.com/Zylann/godot_heightmap_plugin/blob/master/addons/zylann.hterrain/doc/main.md#procedural-generation-from-a-script

eranimo commented 2 years ago

@Zylann is it possible to use the brushes from a script?

Zylann commented 2 years ago

Not directly at the moment. Maybe they can with some changes, since the plugin uses the same features available at runtime to paint, it is just only setup this way in editor.