March 19, 2019

Detail II: Fresh Blood

I had begun working on detailed hex maps here.

It gave me no end of trouble. I wanted to be able to generate a single hex at a time, not the whole world, but there were so many continuity issues. So I took a page out of this book and began a different approach. I would lay down the rivers first, and only then would I generate terrain around it.


To generate the river path, I just use a random walk with a probability based on proximity to the goal (the river egress in red). This works out nice. But this is a flat hex. I had talked previously about using IDW to make a smooth interpolation between neighboring hexes, and adding noise. But I also want to ensure that the rivers have a constant downward slope.


Uh, oops. The river runs along a little ridge! Easy to fix. We just add height randomly until the conditions are satisfied that the river hexes are in a valley or trench.


Doing a few at a time and they line up nice! But the central river system isn't the only drainage I want to worry about - and I can use a bit of erosion like I've done for the world map. That's a quick calculation to figure out where everything should go...


Make the rivers display based on drainage-area:


Then erode. I'm not doing the full erosion code, just an average between the source and target hexes:


That looks pretty decent. What about the hex it drains into?


Whoa. Not, uh, what I expected. The river channels are all wrong. It's possible that there's something odd going on when the code tries to reconcile three input rivers. In many places, the river actually flows uphill! Not ideal. The brute force approach for the 2-river case simply is inadequate for a more complicated system. So then, we have $n$ inputs, with defined heights, $n-1$ connections at defined points, and a single exit, and every hex along the line must be lower than all that precede it.

This, by the way, is where my code starts to balloon into a spaghetti mess, as I desperately bug fix, welding on patches here and there. But eventually I get to a place that isn't totally borked. I still need to fix the way the drainage displays, however. Right now it's not continuous. I'll handle that in another post.


I also see effects from the master hex popping up - there is a clear cliff between the two here. This could ideally be avoided (it can be avoided using some other methods) but in this case I may just "relax" the edges towards the ideal (determined by IDW). Specifically, I'll lower the IDW exponent, making the edges smoother. Without the rivers, the effect is immediate.


But adding them causes a disturbance in the force. It turns out that the code for ensuring the valleys will eventually raise all the hexes...with a hard border at the master hex edge. With a little bit of retooling I can set that to start in the middle first, thus making it more likely that the edges will be relatively unadulterated.


Better. Not perfect, yet. But on the right track.


Eh. Great is the enemy of good and I'm tired of making these images tonight.

2 comments:

  1. "This, by the way, is where my code starts to balloon into a spaghetti mess, as I desperately bug fix, welding on patches here and there."

    Ha, welcome to my world! Very interesting to see your adaptation of the idea, and I think it's looking good. I'm glad it's proving useful to someone. I still get some of those rivers-along-ridges too, though not as many as I did at the start. My solution was simply to increase the constant by which rivers are lowered compared to their host tiles, and to try to encourage the terrain in general to balloon higher. For some reason it never occurred to me to just go through the tiles after the terrain has been generated, check for rivers-on-ridges, and force the neighbouring terrain to be higher. I will have to think about adding a routine to do that, thereby causing further spaghettification.

    ReplyDelete
    Replies
    1. You might have an easier time with rivers-on-ridges than I do, because I just loop through, raising random hexes a random amount, until it satisfies the river-valley condition. Your code is probably better suited to this as it preserves the entire edge from the diamond-square seed, thus avoiding the problem in the last image where there's an obvious border between hexes.

      Delete