Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
slapin opened this issue Apr 15, 2019 · 16 comments
Open
Labels
question Further information is requested

Comments

@slapin
Copy link

slapin commented Apr 15, 2019

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
Copy link
Owner

Zylann commented Jun 11, 2019

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

@mxnemu
Copy link

mxnemu commented Jul 29, 2019

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
Copy link
Owner

Zylann commented Jul 29, 2019

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
Copy link
Contributor

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
Copy link
Owner

Zylann commented Oct 3, 2019

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
Copy link
Contributor

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
Copy link
Owner

Zylann commented Oct 3, 2019

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

@GammaGames
Copy link
Contributor

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

  • create_heightmap(data, min=0.0, max=1.0) -> Image
    Pass in a 2d array of floats with an optional min and max, get an image back with the heightmap texture
  • create_normalmap(data, min=0.0, max=1.0) -> Image
    Same as above, but it generates the normal map texture
  • create_splatmap(texture1, texture2=[], texture3=[], min=0.0, max=1.0) -> Image
    Creates a blended splatmap texture with each set of data set to a color channel
  • create_detailmap(data, min=0.0, max=1.0) -> Image
    Would probably just call the create_heightmap function

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
Copy link
Owner

Zylann commented Oct 4, 2019

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
Copy link
Contributor

GammaGames commented Oct 4, 2019

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
Copy link
Owner

Zylann commented Oct 4, 2019

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
Copy link
Contributor

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
Copy link
Owner

Zylann commented Oct 4, 2019

@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
Copy link
Owner

Zylann commented Oct 4, 2019

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

@Zylann Zylann added the question Further information is requested label Mar 1, 2020
@eranimo
Copy link

eranimo commented May 2, 2022

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

@Zylann
Copy link
Owner

Zylann commented May 2, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants