I've found this a surprisingly difficult challenge to obtain a proper answer to: It seems there are many ways of doing it, yet none feel like they'd give a clean reliable result. I'd really like to know what solutions others found and recommend going for.
I start with a heightmap terrain: As usual I'll be storing height data in a Dictionary of Floats indexed by Vector2 positions, generated from random noise but possible to terraform later thus I save it... since this is Godot 4 I won't be using Zylann's terrain plugin which was never ported from GD3, I prefer generating the mesh from GDScript anyway. Now I want to add structures and roads; Buildings should be easy, just set their elevation to the average height of the points they touch if they're within tolerable spikiness and that's it... roads are much more tricky.
The base rules: Roads would be defined as Vector2 points in 2D space es X and Z coordinates, Y is always the height of the terrain at that point. While I don't intend to make their structure overly complex, roads must contain at least sidewalks textured with a separate material... I can manage having them defined as either a fixed mesh, or generated from a 2D curve describing an upward facing structure in 2D. Roads must be able to branch and merge: There will be dead ends and various intersection types, the goal (if possible) was for them to appear automatically if a bunch of road points are located close enough to each other. Right now there are three main issues I have no idea how to handle:
- Skinning to terrain: The mesh needs to be projected on the terrain surface and oriented to its various bumps. Easiest idea I could come up with is first generating the roads in 2D space at height 0, then offset each individual vertice on the Y axis to match the position of the terrain there. While I have an example for generating a terrain model from scratch, I don't presently have one for offsetting the vertices of an existing mesh; Could anyone share an example of looping through all vertices and giving them an offset on the Y axis, relative to their position from the center of the mesh so the mesh isn't flattened to the ground instead? Obviously collisions and pathfinding need to be updated as well.
- Bending between points: Roads need to curve between different spots and must seamlessly touch the neighboring segment. Even if calculated in 2D space with terrain projection done afterward, this is a very tricky one as it involves using some kind of curve deformation. If I have a rectangular road mesh, where say -X and +X are the ends meant to connect to identical instances, what would be the best way to bend the whole segment smoothly so each edge touches and faces toward the next segment's? This would be much easier if we had support for automatically repeating and deforming a mesh across a curve, unfortunately that would need to be implemented separately and only make things even more complicated.
- Generating intersections. This is also a complicated blocker, especially if I were to hope to get any creativity with the shapes; The logical path would be to make a mesh segment for the various types of intersections, but what if you want the different ends to touch from different angles? Like say I don't want a perfect + shaped intersection, but have the arms of that cross bend a little... or my 3 point intersection to have the shape of an Y instead of a T... not to mention having more than 4 connection points as some roundabouts do: It's not possible to achieve this using fixed intersection models! Of course I can't just start drawing a new road from every point, that would cause the sidewalks to intersect and produce a messy star pattern radiating from the center of the intersection... there would have to be some way for the sidewalks to warp around the center point but I can't imagine how this would be doable.
What is the best approach you recommend and are there any examples? #1 and #2 could for instance be tackled together, if generating a curve across the terrain then mapping the road to it in the first place. An option with simpler results but a saner implementation would be 3D tiles of the same horizontal size with a few fixed intersections projected to the terrain... this would get rid of worrying about #2 and #3 and I already have code to work with, but would be uglier as stuff like diagonal roads won't be possible except maybe a few 45* angle cases.