In the first part of this series, we looked at how the world in Daggerfall is constructed from multiple 1000x500 pixel maps, and how procedural techniques can be applied to add more fine detail to Daggerfall's height map.
This article shows how continuous height map data generated by world readers is injected into scene as continuous meshed terrain and how terrain is landscaped into flat areas for cities.
First up, I have chosen not to use Unity's native terrain system. There are several reasons for this, but chiefly is that Daggerfall terrain textures work very differently from Unity. In Unity (like most modern 3D engines) the terrain is textured using a splat map - a special texture which determines how detail textures are to be blended at every vertex. Daggerfall on the other hand uses a grid of quads textured from a selection of 56 tiles. These tiles are earth, sand, grass, stone, and road with hand-painted transitions between most states.
Above is the temperate terrain set as viewed from Daggerfall Imaging 2. There is one complete set of 56 tiles per climate base (desert, temperate, mountain, swamp) with one variant for snow and another for rain. To save texture space, these tiles also use UV modification to be rotated, flipped, or both rotated and flipped where required.
In order for the terrain to blend seamlessly from wilderness into city tiles, it becomes necessary to build a custom terrain system which understands how Daggerfall works. This unfortunately excludes the default Unity terrain. The code is modular however, so you can always replace part or all of my terrain system with something else.
With the decision made to create a Daggerfall-like terrain system, the next stage is to transform raw height data into streaming mesh samples and format ground for locations.
The first step of this journey was actually implemented a few weeks back in the 1.1 update. See article Time & Space and More for details about PlayerGPS and WorldTime components. Suffice to say, the toolset already has a good understanding of where the player is in the world and what locations are nearby. I will copy an image from the time and space article here as it pertains to world streaming.
Above is a snapshot of the world in pure numerical form. Each of these tiles represents a Map Pixel (discussed in part 1), which is the size of a full-sized city. The player is standing at the origin of Daggerfall city at map pixel 207, 213. In the immediate vicinity are Ripwych Commons to the north-west (206, 212) and Copperfield Manor to the south (207, 214). Keep an eye on them, as we'll be seeing more of them soon.
At a high level, the StreamingWorld component stores data much like the above. It keeps track of a small bubble of world space around player in a 2D array. As the player moves, tiles are shifted up, down, left, or right in the array (based on direction player is moving). New map pixels are loaded as required and everything is occasionally snapped back to near origin, which avoids precision errors at extreme limits of the map. From the player's perspective, they are walking endlessly into the distance. In reality, they are on a terrain treadmill.
Inside each of these map pixels a DaggerfallTerrain object is created for the entire area. This is generated procedurally from Daggerfall's height map combined with smooth noise to add small details. This terrain is then broken up into several DaggerfallTerrainChunk objects, which represent the actual mesh objects and colliders for the player to see and walk on.
The edge vertices and normals of each adjacent terrain are stitched together to create a nice continuous terrain. The next screenshot shows a 3x3 terrain area, once again centred on Daggerfall, with the tiling texture system discussed above. Right now it is just set to grass. I also have turned up the noise scale to make the deformations a little more interesting.
The locations are there in memory, but nothing is being drawn yet. Before we can insert locations, some foundations must be laid. First come the appropriate texture tiles. Unlike the earlier screenshot, the locations are centred inside their map pixel. Daggerfall city is in the middle, Copperfield Manor is to the south, and Ripwych Commons is just barely visible to the north-west.
Now location textures are in the right place, but there's a big problem - cities in Daggerfall need a flat plane to sit on. This raises two questions. What level should the terrain be flattened to and how can everything be smoothed out cleanly?
I evaluated several different methods for selecting city level. The below screenshot shows a few of them without any smoothing.
I didn't like the highest point as it made everything feel too raised. The average is pretty good as the city is leveled evenly in between all deformations, but I rejected it also as another pass was required to determine average elevation after applying noise. World height was very cheap to obtain but sometimes felt a little too high or too low. I ended up settling on median bilinear height (from the map reader), which is also very cheap and consistently landed somewhere between the world height and true average. So that's the level I went with.
The next stage was to blend the flat areas into the random terrain. Interestingly, even full-sized cities like Daggerfall do not really fill their entire map pixel. They have a band of 14 tiles around the outside to use as blend space. These even have special index (>55) to indicate not to use RMB-defined tiles and instead blend with terrain.
The blending process gave me serious headaches for a few days. I tried several different methods of blending heights but none of them gave me a perfect transition from the rectangular city area all the way to edge of terrain. In the end, I created a system of building scale maps with linear rolloff from edge of city to edge of terrain. This uses linear interpolation for the straight rects and bilinear in the corner rects to keep everything smooth.
What is important is the scale is 0 all the way around terrain bounds and smoothly reaches 1 at the location bounds. Drawing the scale map for Copperfield Manor looks like the following. Each of the points below represents the blend weight for random terrain vs level terrain. The further out from the centre rect, the more influence random terrain will have. It looks similar to a pure radial falloff, but if you look carefully you will notice everything converges on a rectangular area, not a single point as would be the case with radial.
Applying the scale map gives us a nice smooth transition from flat area into random terrain. The terrain features can still express themselves, but the transition feels natural and remains completely walkable even with lots of noise.
From here, it's a simple matter of plopping down locations where needed and let the player go exploring.
The next part of this series will be about enhancing details of terrain. I will discuss adding textures outside of cities, improving elevation noise, and adding trees and other wilderness flats.