Sunday, February 26, 2023

Water and sand

In my last post I talked about (yet another) new way of making coastlines better: by creating a mask for them using a simple diamond-square algorithm, and then filling in the terrain afterwards. It occurred to me that something similar could work for lakes, too.

Lakes are currently created at regional level via a horrendously complex process. Originally, I treated lakes as simply part of the regional terrain, using the diamond-square algorithm to generate the lake beds using the actual elevation values taken from the global map. This didn't work well, so I changed this to a more complicated method that traces the edge of the tiles of the lake and then disrupts it, to create a mask, and then generates new lake bed to fill it. This works fairly well most of the time. However, sometimes it goes wrong, for reasons I can't diagnose, and produces lakes with square borders or leaves them out entirely.

But it occurred to me that I could just adapt my new coastlines approach. If I'm generating a mask for the shape of the lakes before calculating the elevation of their beds, I can make that mask using the diamond-square algorithm, just as with the coastlines. The key realisation is that this algorithm doesn't have to be seeded with elevation values from the global map. It can be anything. This allows me to give it values that tend to produce good lake shapes, and then fill in the depths afterwards.

This gives results like these:


This method has several advantages over the previous one. It's much simpler and requires far less code. It also seems not to go wrong, which also helps. Moreover, this method can render lakes of any size. With the old method, for each regional map, UW would create the entirety of any lakes that were only partially on the map, as in the last image above. This meant there was a limit to how big they could be (and even the larger ones within that limit tended not to work properly). This new method doesn't do that, however. It just creates the bit of lake being shown and ignores anything outside the borders of the current region. This means we can have very large lakes. It also means that we can have very large ergs (areas of sandy desert) and salt pans, because the program treats these as weird lakes. So where before these couldn't get very extensive, we can now have immense sandy deserts like this one:


Indeed there's nothing to stop us now from having Arrakis-style planets with ocean-sized deserts of sand. As with everything, we don't want that to happen too often, but anything that (believably) increases the range of possibilities is good. Some more adjustments to this new system are needed, but it seems to be fairly robust so far, so I'm quite pleased with it.

Tuesday, February 7, 2023

Sinking the extra islands

In my last post I mentioned an issue with excessive islands appearing around coastlines. Happily, it's proven to be a very easy problem to fix (at least I think so...), but I thought it worth explaining.

Here's an example of the problem:

In itself there's nothing wrong with this, but it's a problem when all coastlines look like this - and as you can see in the last post, they do. But why? If you look through earlier posts on this blog you'll see mostly much less busy coasts. Somewhere along the line, I've made some kind of change to tackle some other problem, and it's had the unexpected side effect of replicating the Hebrides everywhere. Which is fine on occasion, but we don't want it everywhere. It's another illustration of the difficulty of making changes to what is now such a complicated piece of code, as it has so many moving parts that a small tweak in one place can have unlooked-for ramifications elsewhere.

How are coastlines made, anyway? Well, the terrain at the regional level is basically done by seeding the corners of the tiles with values taken from the corresponding cells at the global level, and then applying the diamond-square algorithm to fill in all the rest. Where terrain higher than sea level meets terrain that's lower, we get coasts. There are then endless tweaks to make them look better, but the diamond-square is at the heart of the process.

I think that what's happened has something to do with the changes I made a while ago to the sea bed, especially the addition of continental shelves. This means shallower seas near the coasts on the global map. Shallower seas mean more islands on the regional map, because the diamond-algorithm basically makes the terrain bumpy, and if the terrain is near the surface of the water to start with, lots of those bumps will stick out of it. (This is especially so given that the algorithm is set to tend to bump land up, in order to emphasise river valleys.) The solution, then, is simple. I add to this diamond-square algorithm a check to see whether a point at the corners or on the side of the tile is below sea level. If it is, we make it a lot lower. As a result, the algorithm tends to produce lower land around the sea, and far fewer islands are produced.

But doesn't this result in undesirably deep seas around the coasts? No, because the undersea terrain that this algorithm produces isn't actually used for the final map. Instead, there's another diamond-square-based function that creates undersea terrain. UW calls the first function first, and works out what's land and what's sea, and the heightmap for the land. It then calls another function to create seabed, and then pastes that onto the sea regions of the original heightmap. This allows me to make changes to the land terrain generation without messing up the seabed, and vice versa. So in this case, we can artificially lower the seabed in the way described above during the initial terrain generation, and it won't affect the final seabed - but it will affect the coastlines.

Here's what the above map looks like after doing this:


This looks much more plausible. Some islands still remain, and of course we don't want to get rid of them entirely. I've set it so that the island-reducing tweak varies in strength. It's weaker in areas that have higher roughness, and much weaker in arctic-type areas where one might expect fjords. So crinkly little islands are more likely in such areas. As always, variety helps to make the maps more believable, at least in theory.

(Also, notice how in the first picture there is one river that doesn't reach the coast. I think this is because the land has been extended out into tiles that correspond to ocean cells on the global map, and the river simply doesn't reach that far. There are functions that are supposed to deal with such cases by extending the rivers out to the coast if need be, but they seem to have failed here. In any case, you can see in the second picture that the coast has retreated inland and the river now reaches the sea properly, so that's a bonus from this tweak.)

Sunday, February 5, 2023

Mud and sand

Having grown up by the coast, I'm very aware of the need for a terrain generator to be able to handle different kinds of coastlines. So I wanted to try to include some in Undiscovered Worlds.

Since I currently live near the Bristol Channel I'm particularly aware of the importance of mud flats, so I had a go at incorporating those first. The basic idea depends on a feature that has been present in UW for a long time: inlets. The program removes land around river mouths, depending on factors such as the size of the river and the tidal range at that point. It occurred to me that it's easy to keep track of where these inlets have been created, and we can simply lay down mud flats around them - again based on factors such as the size of the river and the tidal range. Mud flats, of course, are represented by an array of bools within the region object.

At this point things got very complicated as I decided this would only make sense if we also kept track of how much silt every river is transporting. I wrote a new system to simulate this and got it mostly working, but annoyingly it wasn't quite right. This was a pain as the maps looked quite good with river colours varying from bright blue to dull mud, depending on their silt content, but the fact that sometimes this didn't work right (with small stretches of river inexplicably showing too much or too little silt) made it not really worth the effort - especially as it was largely a visual effect that didn't really make much difference to the world generation.

So after scrapping all that, I went back to the inlets. In this map, inlets are marked in magenta:


These are all river mouths where the program has cut away land to make it look like the rivers have eroded the coastline. Most are for very small rivers, but some are for larger ones.

Now we create a temporary array to hold silt levels. Going through the map tile by tile, we lay down silt levels on coastal cells that are within inlets. The levels of silt are determined by the size of the river and the tidal strength of the area. Then, we go through the map again and spread the silt out from these points, both inland and into the sea. Any cells that have a certain level of silt or higher are then turned into mud flats, which also involves setting their height to 1m above sea level, and possibly creating some saltwater wetlands nearby too.

Only a few inlets actually have enough tidal strength to generate mud flats big enough to show on the map - bearing in mind that each pixel is a kilometre, so the mud flats have to be quite extensive to show up. Here is a region that has quite a lot of them:


And on the relief map, they blend in fairly subtly with the wetlands around them:



We can also add mud flats to river deltas, too:

Now we want nicer beaches too! Namely, sandy beaches and also shingle ones. We can make these in exactly the same way. With the beaches, though, the tidal range is more important than river size, so we tend to get mud flats around large rivers and sand or shingle around small ones. And where mud flats tend to spread inland along the rivers, beaches tend to spread along the coast. Finally, we set it so that mud, sand, and shingle can all mix with each other, making for varied coastlines.

The resulting effect is quite subtle, because again only really large beaches can be seen at this scale. Here, you can see a few spots of yellow or brown along the coast where there is enough sand or shingle to make a large beach:


There might not seem much point adding such minor features, but one of the new planetary variables is lunar gravity. Worlds with weak lunar gravity have small tidal variation, and relatively few mud flats and beaches. But worlds with strong lunar gravity can have extreme tides, with correspondingly extensive beaches and mud flats:

There are a couple more tweaks we can add. First, mud flats get a chance to generate barrier islands along the ocean-facing edge. This results in areas like the Wadden Sea, where a string of islands protects a large intertidal zone. Second, in tropical areas, mud flats (and salty/briny wetlands) get mangrove forests on them. You can see the darker green coastal areas here (the user can turn off this effect if desired):


So that's that for coastal zones, at least for now. The next job is to try to work out why all my coastlines have acquired far too many little craggy islands and get rid of most of them (they never used to look like that!). After that, I'll aim to release the updated version.