godot_heightmap_plugin icon indicating copy to clipboard operation
godot_heightmap_plugin copied to clipboard

Manual terrain stitching

Open Zylann opened this issue 5 years ago • 13 comments

This is a second proposal similar to #55. I'm not decided yet which way to go, as each approach takes significant work. This one focuses on a smaller scope. Manual terrain stitching would allow to:

  • Create larger worlds
  • Create worlds that are not necessarily square
  • Customize each sector differently, although that means more bookeeping work for you (note: having more textures is about to become possible without relying on stitching: #10)

The difference with #55 is that:

  • This focuses only on stitching, no streaming, therefore is less work to implement (at least it allows to script it)
  • It lets the designer edit and save sectors individually
  • More control means more responsibility: it's up to the user to organize files and terrains configurations within the scene, as less stuff is automated

Workflow

The HTerrain node would be given 4 new properties of type NodePath:

  • neighbor_negative_x
  • neighbor_positive_x
  • neighbor_negative_z
  • neighbor_positive_z

Those properties will be the path to another terrain node on each of the sides, to specify that they are neighbors. When assigned another terrain, if sizes match, the other terrain's neighbor property will also be set and they will be stitched together.

From there, it gives information for the plugin to do the following:

  • Properly generate normal maps, to make seams disappear
  • Make geometry clipmaps react across two terrains so vertices align (not sure yet if it will be necessary)
  • Paint across multiple terrains at once
  • Import or generate worlds as multiple terrains

Of course that also works by script, but erasing seams will only happen in editor. So if you generate terrain procedurally, it's on you to keep it seamless (which is relatively easy when noise is used).

Things to figure out

The fact that this approach allows different configurations per node, it raises situations to solve.

  • What if the user paints a ground texture across two neighbors that don't share such texture?

    • Don't paint on the terrain not having the texture (a seam will appear)
    • Don't paint at all if the brush intersects with a terrain not having the texture
    • Change configuration so that the texture becomes shared (not necessarily wanted)
    • Note: the same concern happens for detail layers
  • What if two neighbors contain the same ground texture but the user decides to change one?

    • Don't do anything special (a seam will appear until the user finishes to do what they have in mind)
    • Change all neighbors sharing that texture as well (not necessarily wanted)
  • What if the user scales one terrain having neighbors?

    • Scale neighbors too
    • Don't allow it (annoying)
  • What if the user changes the resolution of a terrain having neighbors?

    • Change resolution of neighbors too
    • Don't allow it
  • What if the user changes collision layers on a terrain having neighbors?

    • Change neighbors too
    • Arguably there are some properties to be kept in sync this way

In general, it's possible to overcome those things manually. This system is pretty simple but that simplicity allows for many cases that may or may not be OK, and it's not always trivial to solve them automatically. So it's more a matter of what should the plugin do to help the user.

Note: concerns about power of two textures still applies (#12), because terrain textures might need to be padded, making current resolution choices invalid.

Zylann avatar Apr 24 '20 20:04 Zylann

I really really want this. I actually thought I would be able to do this already out of the box Not the stitching part, but using multiple terrains and putting them side by side by translating the mesh. I was planning to destroy far away meshes as the character goes far. But, then i found out It's not possible to translate the mesh.

I'm doing a top down game so it's not necessary for me to see things far away.

Gyeff avatar May 20 '20 07:05 Gyeff

i found out It's not possible to translate the mesh.

It is possible to do, the gizmos are just broken https://github.com/Zylann/godot_heightmap_plugin/issues/41 and the heightmap collider is doing funny things in the editor https://github.com/Zylann/godot_heightmap_plugin/issues/129. And you can have multiple terrains and all, the problem is just that they don't stitch well (unless you import pre-generated terrains from a program that takes care of stitching), and the editing experience doesn't treat them as connected.

The main goal of this feature is mostly to improve the editing experience by making the terrain better aware of neighbors, which can serve as base for related functionality,

Zylann avatar May 20 '20 09:05 Zylann

As a user who will primarily import my own textures and heightmaps from an external program (World Machine) I believe that sector streaming would be the most cross-ideological way of approaching this.

godot_heightmap_plugin is a great tool, not only for terrain editing, but for rendering and implementing terrain in the Godot engine.

I feel like if Sector streaming was implemented, this plugin would already be very close to mergeable with the Godot main binary, as it already seems so complete.

After sector streaming difficulties were out of the way, it would make sense to allow users to tinker with their height and colormaps. There are many many tools out there which allow users to edit terrain, and opening the doors to them with open arms (full tile streaming support) will greatly improve your plugins audience.

tavurth avatar Jul 14 '20 17:07 tavurth

So you agree with https://github.com/Zylann/godot_heightmap_plugin/issues/55#issuecomment-657680547 ? Note that manual terrain stitching is another approach to do the same thing, having both sounds hard to keep up.

Zylann avatar Jul 14 '20 18:07 Zylann

Yep, sorry I wasn't clearer.

I think that more manual options and control are always good, as I prefer to build bottom up, but in this case I think 80% of your users are going to be looking at sector streaming first, and those users who need manual terrain stitching will be looking for escape hatches to do something themselves, rather than a built in method.

I believe even though it might be more work initially the user payout will be higher, and people will stick with the plugin for longer.

An example of my particular use case would be the following:

Currently I have some terrain split up into 48 sectors, which I'll load in series of 9

~ ~ ~ ~ ~
~ O O O ~
~ O X O ~
~ O O O ~
~ ~ ~ ~ ~ 

X: Player
O: High res terrain
~: Low res terrain (Second HTerrain)

Where the player is on the center square, and the other tiles are loaded in as the player moves. I'll currently probably apply two layers of godot_heightmap_plugin terrain, one of a high resolution (9 cells above), and one of a lower resolution to display the distant areas of the map.

It's highly likely that I'll bypass the predefined splatmap system built in this module, as I need more control over my ground textures for things like terrain deformation or snow tracking layers. The built in escape hatch for custom shader will be useful in this case.

Another use for the custom shader escape hatch in this use case will be that I can cull out areas of the low res terrain which are occluded by the higher detail terrain map of 9 layers, to prevent them from clipping with each other.

For this use case, I'd much rather have a system to manage streaming in the next of the 9 layers and manage myself the cross boundary texture and bumpmap differences, than have a system in place which works for the defined use cases (up to preset N texture maps) but does not work for my particular use case (N texture maps, plus special effect maps).

I'm not sure if I'm misunderstanding the issue, but from my perspective it seems like this, perhaps you can correct me if I'm mistaken?

tavurth avatar Jul 14 '20 18:07 tavurth

I think I may have an "alternative" solution, that may work in the grand scheme of things if we used a height map as the base. If we can zoom in on the heightmap source and adjust the edges of the "chunk", because we can know what will go next to it.

Example: We have a 10241024 heightmap image, then we take an area of 256256. Then we set the offset to 0/0, to get the first chunk. After this, we take the same image, add it to a new map, but now we set the offset to 0/256, then create the new one. because both maps knew what is next to them from the source image, the edges should line up perfectly.

If we want a map that required multiple height maps because of size, then we can play with the offsets, and use the edges as buffers to use the same technique to connect chunks of even larger maps.

It leaves manual editing at the edges problematic but would leave a use case when you can pre-generate a large terrain, then import it into Godot, without the need to stitch the maps manually.

At least this solution should not need major changes. We do some synchronization between the maps with the source image.

Frontrider avatar Sep 19 '20 20:09 Frontrider

We have a 10241024 heightmap image, then we take an area of 256256. Then we set the offset to 0/0, to get the first chunk. After this, we take the same image, add it to a new map, but now we set the offset to 0/256, then create the new one. because both maps knew what is next to them from the source image, the edges should line up perfectly.

This is what the plugin already does at the moment, only with smaller chunks.

It leaves manual editing at the edges problematic

Yeah, that's basically why the two issues are currently open. The problem is not how they are adressed, but how it's presented in the plugin and how they integrate with the rest of the features.

In any case the plugin will need important changes in future versions to support super-large worlds and gain rendering performance by switching more load to the GPU.

Zylann avatar Sep 19 '20 20:09 Zylann

I've been looking around, and I'll keep looking, but I'll ask anyways. Do you have any tool that you know that could convert a black and while image to the formats you use? At least a height map. Commenting it here, because if I can generate a large image to the sizes I need then split it up to smaller images.

That would provide a temporary solution, and let you be compatible with terrain generator tools. Tried importing spliced up heightmaps, but it's misaligned, and I assume it's like that because it would need the rest of the image as a reference.

Frontrider avatar Mar 06 '21 21:03 Frontrider

Do you have any tool that you know that could convert a black and while image to the formats you use?

There is this: https://hterrain-plugin.readthedocs.io/en/latest/#import-an-existing-terrain

The heightmap specifically is imported as an Image resource with the Image.FORMAT_RH format, saved as a .res among the other images referenced by data.hterrain. The others are PNGs but they have specific .import options.

Zylann avatar Mar 06 '21 21:03 Zylann

Hmm. Is it possible to access the generator gui's methods from code, without copying it?

Frontrider avatar Mar 07 '21 00:03 Frontrider

Depends what you want to do, but the generator is currently not written to be used from code. The GUI includes the setup, but the generation execution is controlled by a separate script. You'll have to read it to understand how that works. There is probably a way to extract it, but I havent done so because if a real generation engine was to be exposed it would rather be node-based I think, rather than endlessly adding options. The current one is quite limited and has no guarantee of API stability. Also it doesnt seem to be related to this issue.

Zylann avatar Mar 07 '21 00:03 Zylann

It is slightly related because doing that manually is extremely close to seamless (I overlapped 2 terrains by 1-2 units, and there is 1 spot where it will need some manual intervention), and in my specific case it is as seamless as it needs to be. Like "floating point error" close. If there is some rounding to the edges then it may be seamless without having to know about the rest.

Frontrider avatar Mar 07 '21 00:03 Frontrider

I think the reason you'd get seams with separate heightmaps is only because normal maps need to be calculated with knowledge of neighbors. The generator doesn't do that, because the source of height is procedural anyways. You have images, so that would need to work differently. Then for other maps, seams might also occur because of filtering, which means map resolutions need to include padding as I noted here https://github.com/Zylann/godot_heightmap_plugin/issues/12#issuecomment-606237713

Zylann avatar Mar 07 '21 00:03 Zylann