Sunday, November 26, 2023


There's not been much activity here recently, but things have been moving behind the scenes. I've mostly been trying to work out how to improve the visuals for my planets. I was just using OpenGL to display them, and I wrote last time about wondering whether to change to a less low-level approach that might make it easier to get a few nice visual effects in. (I was particularly missing the fake 3D effect of my old maps.) After a lot of time alternating between digging deeper into OpenGL and trying alternatives, I decided to use raylib, which is not an engine so much as a library that keeps things pretty low-level, but without quite as much fuss as doing everything in OpenGL directly. Also it's easy to use ImGui with it, and I wasn't going to redo all the GUI at this stage.

There's still quite a lot of getting one's hands dirty with this. I've spent a lot of time tinkering with shaders, and have managed to generate some fairly simple effects, but I'm quite pleased with them:

Getting the normal map to work properly was very fiddly. For some reason I don't understand (a glitch in my cubesphere object, or a glitch in my shader?) the shadows on the top and bottom faces of the cubesphere vary incorrectly across the faces, even when the normal map is definitely correct. So I had to mess about with my normal map creating routine to vary them in the opposite way, to counteract this effect. It's still not perfect, but it looks mostly OK, so I'm sticking with it for now. It's certainly nice to see shading on my mountains again (even though, at this scale, it's probably too exaggerated).

Next I need to think about how to improve my shader - ideally by adding some basic atmospheric effects, as well as clouds, if possible. And then I'll need to think about how to integrate the regional map level into this system, which is where things will get tricky.

Tuesday, September 12, 2023

Another decision point

Time (at last) for a new update!

I have, more or less, managed to convert all of my existing terrain and climate routines to work with spherical planets. This has been quite fiddly to do. Some could be converted fairly straightforwardly, while others could not. In some cases I had to rewrite them from scratch, and in extreme cases I had to simply alter them to work with a giant 2D map which subsequently gets wrapped onto the sphere. In some cases I found that the rewritten versions were better than the original - e.g. my continent-generating routines now seem to throw up more varied terrain more often, including archipelagos, meaning that I don't need to use the Aegean-creating function.

I was particularly held up by a subtle but nasty bug in my routines for moving from one point on the sphere to the next, which caused inaccuracies when moving from one face to the next. Much of my time was spent writing workarounds to deal with the effects of this bug until I finally found it, after which I had to go through and remove some of those workarounds.

But it's done! Mostly. All four of the planetary types (small continents, large continents, oceanic, and non-tectonic) can now be in spherical form, and in all three planet sizes too. Most features such as mountains, dunes, lakes, wetlands, craters, etc. are present, as are the variable climates (including exotic climates). There are still some issues. Artefacts occasionally appear at the edges of faces, and occasionally the whole thing crashes. So there's still some work to be done smoothing it all.

Here's a typical continental world, showing some of these features:

Here are a couple of non-tectonic worlds that I thought looked interesting:

Some new terrain features are possible now. For example, rarely, ocean ridges on small worlds can create planet-wrapping archipelagos and islands:

This was unplanned, but I like the effect, so I've left it in as an occasional possibility.

And you can of course view things like rivers on the globe:

- and climate types:

So now there are two problems I need to consider.

First, what engine should I use to display all of this? At the moment I'm just using OpenGL directly. This is fine for displaying simple objects such as the facets of my globe, but doing more elaborate effects is probably beyond my abilities. So far I've not been bothering about this as I wanted to work out whether I could create the terrain etc. of the planets first, and worry about how to display them later. But obviously with this method I can't do things like apply shadows, which means that they don't look as nice as my 2D maps that have the faux shading effect. So I'm wondering whether to use an existing engine such as Irrlicht to render the globe, which might allow me to do more. But then of course I'd have to learn how to do that...

Second, what about the regional maps? As things stand, I haven't implemented them at all yet in this new version. The actual generation of them shouldn't be too tricky. (I know I will regret typing that! But at the moment I think it should be fairly straightforward to convert the existing regional map creation routines to work with the spheres.) The problem is how to display them. One option is to keep the system of a zoomed-in mode, where the region is displayed as a 2D map, just as it is now. That would be very straightforward. But consider again what each region represents:

Each tile here is a region (supposing this is an Earth-sized world). Now suppose we zoom in on one in the middle of the "face" that is facing us. All is fine - it can be displayed in the zoomed-in screen, and we can scroll around to north, south, east, and west. But suppose we keep going north and find ourselves on the "face" that's on the top of the cubesphere. Scrolling around here won't be taking us north, south, east, and west any more, at least not in any consistent way. Suppose we now scroll to the right and go down the "face" to the right of this image. When we started on the first face, the top of the map was pointing to the north. But now it's pointing east. Things get even worse if we try to navigate around one of the points where three "faces" meet.

All of this comes from the fact that the world isn't really a sphere at all, but a cube, and if we display the regional map in 2D form it's going to be very hard to conceal this fact.

So the alternative is not to do it that way at all. Instead, we could allow the user to zoom gradually in on the globe, generating the regional maps as they do so, and then display those maps directly on the globe itself. So as you zoom in on the map you would see the regional-level details popping in once it's close enough.

There are two problems with this. The first is that it's hard! We'd have to be able to generate regional terrain on the fly as the user is moving around the globe, and keep track of which regions have been generated, which ones need to be generated, and be able to paste them onto the right bit of the globe. All of this is theoretically possible but could be a nightmare to implement.

The second problem is that some tiles would get distorted. Look again at the image above, and in particular the regions at the corners. We get away with the distortion at these points at global scale, but I worry that at regional scale it would look pretty bad. Mountains and rivers, in particular, would look unnaturally stretched.

So I'm not sure what to do about this. The next job is therefore probably to convert the regional map generating parts of the program and try pasting the generated maps onto the globe to see how they look.

Wednesday, June 28, 2023

The blue marble

I've not had much of a chance to post any updates for a while. This is partly because I've been very preoccupied with real-life distractions (finishing the first draft of a book 😱) and partly because the process of translating everything into spherical form is pretty time-consuming.

Much of the difficulty comes from having to deal with multiple coordinate systems at once. The cube-sphere, as described in my last post, is really good for creating terrain that looks reasonably good from all angles, without the distortions you get from just wrapping a rectangle directly onto a sphere. From any given point on the cube-sphere you can easily move to the neighbouring points in a grid fashion, just like on a 2D plane (except at the corners of the faces, where it gets tricky). However, if you want to move due east or west, or north or south, it's much more complicated, because the grid doesn't line up with those directions unless you're right on the equator. To work those out you need to know the longitude and latitude of the points, and converting from cube-sphere coordinates to longitude and latitude (and back) is both fiddly and resource-intensive. And being able to specify directions of this kind is essential for the climate simulation.

Still, I've made some progress. I've converted most of the standard continental terrain generation to the spherical form, and made a good start on the climate model. Temperature and basic rainfall are in place, as well as wind directions and sea ice. Here are some pictures - they are all of the same world, which hopefully gives a sense of how the globe looks from different angles:

Here's the south pole, so you can see it's possible to have polar continents without distortion:

And here are the wind bands (red: westerly; cyan: easterly), which aren't very realistic, but I'm not going to try to change them:

There's a still a lot to do. I've been having a lot of difficulty getting the mountain generation methods to work well with the globe - in fact I've had to create an entirely new way of doing the mountains along continental edges, which is based on the functions I originally created to import user-generated mountain maps. But there's more work to be done with them (the world shown here is one of the most mountainous I've been able to generate, and it still looks very flat). Then I need to add the monsoon routines, before getting to grips with rivers and lakes, which is never much fun. But I think I've reached the point where it looks like converting this whole thing to spherical worlds can actually work, so I'm hoping the final results will be worth the effort...

Saturday, April 1, 2023

Cubular balls

After the last release version of Undiscovered Worlds I'm more or less at the point I was nearly four years ago, when I got as far as I could with the original version of this project in AGK. Back then I wondered which of these three things to do next:

(1) Create a new level of detail, so one could zoom in on the regional maps and expand them.
(2) Start populating worlds with procedurally generated life forms.
(3) Rewrite the entire thing in a more suitable language.

I went for option (3), and the current state of UW is the result. It's much better than what I had then - faster, fewer errors, more options and variety, and much more detail and realism (within reason). So what next?

Options (1) and (2) remain on the to-do list, and I was initially going to get cracking with (1). This would add "local" maps, which would have the same relation to the regional map that the regional map does to the world map. But before doing that I thought I'd try a bit of more fundamental experimenting with the program.

One obvious limitation of UW as it stands is that it deals only with flat maps. I wrote right back at the start that creating spherical worlds is theoretically possible, but I wasn't going to try it as it's too difficult. But I've always wished I could have done it, and this limitation is more apparent now that worlds with different climates are possible. That means that there's no guarantee that any given world will have ice caps at the northern and southern edges of the map, which in turn makes it harder to hand-wave away the terrible warping that happens if you try to wrap one of these rectangular world maps onto a sphere.

So I thought, having come this far, I might as well at least try to see whether I could rewrite the program to work with spherical worlds. If it doesn't work then I can just give it up and continue with the flat maps as before, but it might be interesting to see what can be done.

So after a fair bit of tinkering, and wrestling with the horrors of OpenGL textures, I have this:

This is my existing diamond-square algorithm, which forms the basis for a lot of the global generation, but rewritten to map onto a sphere. This wasn't easy, but it works!

The basic idea is to represent the world as a cube. Each of the six faces of the cube has its own terrain map. When the cube is displayed, we warp each face to form a sphere shape:

This works because each face of the cube is not a single plane object but a grid of 16*16 of them. We warp the cube by taking each vertex of each plane object and transforming them using the extremely useful formulae found here. Here's what it looks like if we show all the sub-faces:

So our sphere is actually a polyhedron made up of 1,536 squares, which are distorted to varying degrees. As you can see, this projection keeps all of the subfaces roughly the same size as each other, but distorts the shapes towards the edges of the faces and especially at the corners. An additional irregularity is that where most of the squares have eight neighbours (including the diagonals), the ones at the corners have only seven.

The visualisation is useful because each of those squares also corresponds to a single regional map. Exactly how the regional map will work with this remains somewhat unclear, particularly when it comes to navigating round the corners, but I'll cross that bridge if I come to it.

In any case, the distortion involving in warping the cube into a sphere is much less than you get if you try to wrap a rectangle onto a sphere. In the latter case there are two points of distortion - the north and south poles - whereas with the cube there are eight - the corners. That shares out the distortion more evenly so that it is much harder to see.

The next job is to see if I can adapt the rest of my global terrain (and climate!) generation algorithms to the spherical maps. I'm fairly sure it should be theoretically possible, but that doesn't mean it's practical to do, so we shall see... If not, back to the flat maps!

Friday, March 10, 2023

Update: version 1.0 now downloadable

The updated version of Undiscovered Worlds is now downloadable on the first page. I've also updated the instructions for use there, and the screenshots. Since the previous version was rather unpolished compared to this one, I'm retroactively numbering it 0.x and calling the new one 1.0!

Note that saved worlds and settings from previous versions aren't compatible with this one.

The new version has all the features I've written about since the original upload, plus a few that I haven't. Here's a summary:

UI and presentation

  • Proper installer application
  • New app icon (courtesy of u/Iron-Phoenix2307)
  • Nicer font (Roboto)
  • Clearer organisation of information
  • Automatic update notifications

World generation

  • Three sizes of worlds now possible
  • New terrain types: non-tectonic and ocean worlds
  • Greater variety of tectonic terrain
  • New features: craters, mud flats, beaches, mangroves
  • Much larger sandy deserts
  • Variable planetary information: orbital eccentricity and obliquity, gravity, etc
  • More robust, detailed, and flexible climate modelling, allowing for exotic climates

Custom worlds

  • In addition to importing maps, custom-generated ones are now possible with user-defined settings
  • Ability to specify all planetary variables, including extreme settings
And of course various bugs have been eliminated, and many exciting new ones added.

The source code has all been updated too, so everyone is welcome to poke around in there too.

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.)