For the last week or so, I've been knuckling down on creating a fully streaming overworld in Daggerfall Tools for Unity. If you're not sure what I aim to accomplish, here's a brief overview of how I want this to work from the Unity Editor.
- Setup DaggerfallUnity singleton as normal.
- Add prefab StreamingWorld into scene hierarchy.
- Add prefab PlayerAdvanced into scene hierarchy.
- Set player virtual position in PlayerGPS and virtual time in WorldTime.
- Hit Play and explore entire Illiac Bay, entering any building, any dungeon, and experiencing full day/night cycle with climate and seasons.
In many ways, I'm already very close to this goal. The tools have procedural loading of cities and dungeons, virtual time and space, climates and seasons, day and night, interiors and exteriors, and more. What's missing is a full terrain setup and intelligent loading/unloading of blocks as player traverses the world.
You can probably imagine what a huge job this is. While I am working very quickly towards this goal, it will be another 3-4 weeks before it all comes together in a state that I'm happy with. To maintain regular updates over that time, I will split news on my progress over multiple articles.
In this first article, let's take a look at Daggerfall's height map and how it translates into useful terrain.
Similar to the 1000x500 maps found in POLITIC.PAK and CLIMATE.PAK, Daggerfall also stores a 1000x500 elevation map in WOODS.WLD. Here's a grayscale dump of that data (click for full size).
You can also see a nice false-colour version of this map here.
Like most height maps, dark areas are low elevations (values of 2 or less are water) and bright areas are high elevations. Daggerfall also stores a noise map in WOODS.WLD, which I'm not going to talk about as I plan to use a better method of noise generation.
To better understand the scale of this data, here is a zoomed-in 10x8 sample grabbed from along the northern coastline (I have brightened image so individual points are easier to make out). Each of the below squares represents a single "map pixel", equivalent to one full-sized city like Daggerfall or Wayrest. If you measure the time it takes to cross from one side of Wayrest to the other, it would take 10x that amount of time to cross the below sample west-to-east.
Once the scale is understood, it becomes apparent there's not much height data here considering the actual size of terrain represented. This is why Daggerfall's terrain is mostly flat. It's stretching a single height sample over a huge area then modulating that with a little noise. Sometimes you can fluke a nice bit of terrain, but it's very rare. On the whole, the overworld in Daggerfall is very flat and bland.
To improve this situation, there needs to exist more data in between height samples above. This new data must be quick to sample, use very little memory, and create a continuous grade between samples with plenty of interesting variety. To accomplish this, I am combining a few basic techniques.
The first problem to solve is the rate of elevation change between map pixels. It's incredibly boring to have a huge flat area abruptly stepping up or down into yet another huge flat area. Our baseline needs to be a nice continuous elevation change from sample-to-sample.
I have added a new API method called GetHeightBilinear(). This quickly samples any point in the 1000x500 map with any number of bilinear interpolations between samples. The result is a much smoother overworld full of curves as each height sample blends into the next.
Below is the same zoomed-in terrain sample using bilinear interpolation to create additional sample points across the surface.
If written out to a full-size image, this would be 128000x64000 pixels, enough to create a reasonably detailed overworld at the same grid resolution found in RMB blocks. New sample points are created on the fly from existing data, and it doesn't require any additional memory.
There's still a problem however. Despite having nice continuous samples, the terrain is still quite boring. The next technique is to add some noise and break things up a little. I'm using a fast simplex noise generator to create small variations at ground level. Below is an example of noise overlayed with interpolated heightmap.
You won't see it in the thumbnail, but if you click through to full-size image you will see small whorls of coherent noise added to the height data. Compare this with the blocky first image, and you can see just how much fine data has been mathematically inserted. This can be scaled to any grid resolution and tweaked as required.
Of course, this is just the beginning. The next step is to create real terrain chunks from this data and tune noise generation towards interesting-looking terrain based on climate data.
Before wrapping up, I will leave you with the following image of a small patch of terrain (equivalent to 2x2 RMB blocks, or 1/4 the size of a full city. I have noise turned up to show the kind of deformations possible.
Over the course of the next few weeks, I will continue building on this foundation, adding better texturing, cities, and climates into the mix.