Terrain Intro

From BISim Wiki - Mirror I
Jump to: navigation, search

This page gives an overview of the concepts related to the definition of the terrain in VBS.

Contents

Land Grid

The Land Grid is the main structure on which the terrain and its properties are defined. Each land grid cell (which is simply a square in the XZ plane) can be assigned a texture, its four corners have heights defined, it contains path-planning and ambient sound information, etc. Each land grid cell also stores all objects which have their centers on it, which allows for fast collision detection -- only the given cell and its neighbors can be searched for objects colliding with some geometry (this however also leads to the well-known limitation on object size).

The recommended size of the land grid cell is around 40 meters. Because of the number of parts of the engine connected with the Land Grid other sizes make little sense and should be used very carefully. The engine variable which holds the land grid cell size is called _landGrid.

The total number of land grid cells determines the terrain size and is stored in the variable _landRange. Until recently this number had to be a power of two, to make bounds checking slightly faster. The product _landGrid * _landRange gives the total size of the map in meters. It is assumed by the engine that the map is always a square.

Note: even though the heightmap should define _landRange+1 vertices on the edge of the map if there are _landRange terrain cells, for simplicity it only defines _landRange vertices and the last vertex is duplicated by the engine (more precisely, the heights on the edges of the map are repeated infinitely, as you can see in the game). All 2D arrays in the Landscape class are simply of the size _landRange x _landRange or of the size _terrainRange x _terrainRange (see below).

Satellite Grid

Each land grid cell can have a different material assigned. By material we mean an RVMAT file which defines the satellite texture, the surface mask texture (containing surface layer weights) and up to four detail layers, each of which consists of three textures (detail color map, detail normal map and macro map that adds some large-scale noise to the surface texture). The terrain shader uses all of these textures stages to display the satellite image when the camera is far enough from the terrain and to display a blend of the detail layers when the camera is closer.

Since it would be inefficient to define one RVMAT and a small satellite image for each land grid cell, Visitor typically assigns the same material and a reasonably sized satellite texture to a number of adjacent cells in both the X and Z directions. In this way the satellite grid is defined, containing blocks of land grids cells with the same material, although this definition is rather implicit and not really strict -- the engine should handle small irregularities in the satellite grid (however Visitor does not take advantage of this feature).

A typical size of the satellite grid block is 12 land grid cells in VBS2. The engine expects the size to be divisible by four, but this is an artificial constraint (bug?) that could be removed from the engine.

Texture Overlap

When one tries to map a texture to a block of land grid cells, there are several problems related to the limitation that texture sizes must be powers of two (apart from hardware limitations, the reason for this is that mipmaps need to be generated for the textures).

The first problem is that the user is free to specify an almost arbitrary satellite imagery resolution (in meters per pixel). One then seldom gets a number of pixels per satellite block that is a power of two. This number has to be rounded to the nearest larger power of two and as a result only a portion of the texture contains useful pixels that are displayed on the terrain. The rest is a margin around the sides of the texture image containing wasted pixels. Another problem is that bilinear filtering is applied to the texture in which case ideal texture sizes tend to be N+1 instead of N. This is why there should always be a certain margin to allow all mip-levels (power-of-two fractions of N) to access the extra pixel and obtain a seamless mapping for the whole terrain at all zooms.

The result is that only the centers of the texture tiles are displayed. The margins contain copies of parts of the useful areas of the neighboring texture tiles. This also holds for the surface mask tiles, which use exactly the same mapping as the satellite tiles.

Example: the Land Grid on Sahrani has 512 cells, each cell is 40 meters wide. The satellite resolution is 1 meter per pixel and 512x512 texture tiles are used. From these user-specified parameters we calculate the texture overlap and satellite block size. One land grid cell should have 40 pixels mapped to it (we divide the cell size by the resolution). At most 12 whole land grid cells (12 x 40 = 480) will fit into the 512-pixel texture. The rest (2 x 16 pixels) will be the overlap. In fact, this overlap of 16 is the minimum Visitor will always choose. Just to check, 12 is divisible by 4, which is good, otherwise Visitor would round the block size down and increase the overlap.

VBS2 terrain grids.png

The above land segment is textured with the following satellite tile:

VBS2 terrain tile.png

Note: the decisions about the overlap and the exact mapping parameters (TexGens in the RVMATs) are entirely up to Visitor. The engine doesn't care about how the textures are mapped and how the result looks like, it just reads and uses the mapping values from the RVMATs. This is a very flexible system and we are free to change some things related to terrain texturing without modifying the engine.

There is one additional limitation in Visitor that the satellite resolution must be such that a whole number of pixels are mapped onto the satellite block. If the land grid cell size and/or the resolution is weird, this won't be the case and Visitor will show the segment size in red. The reason behind this is probably an effort to obtain a perfect alignment of the texture tiles on the terrain, with undetectable seams.

Terrain Grid

Since the Land Grid with its 40-meter spacing is usually too coarse to represent a good looking terrain, the idea of subdividing the Land Grid to enhance the heightmap was introduced in the engine at some point (probably even before Operation Flashpoint). Most data is still defined on the Land Grid but the heightmap (and a small number of other arrays) use the Terrain Grid. The map designer can choose to subdivide each land grid cell into 2x2, 4x4, 8x8, etc., terrain grid cells. The Terrain Grid has its own variables, _terrainGrid and _terrainRange, but in fact these are strongly tied to _landGrid and _landRange by the subdivision. The subdivision amount is always a power of two (it can be 1, in which case the two grids are the same).

Visitor pretends that the Terrain Grid is the primary grid, which may lead to some confusion. For example, the total terrain size must be a multiple of _landGrid, not _terrainGrid. This means that the user-specified terrain size may be rounded slightly up, to the nearest land grid cell.

Land Segments

The terrain is not rendered by drawing the terrain grid cells individually, one by one. That would be too slow. Instead in DirectX one should try to put all faces which share the same texture into a buffer and process the whole buffer with one DIP (DrawIndexedPrimitive) call. The fewer DIP calls per frame, the better. When a map is loaded, the engine checks how many adjacent land grid cells share the same material (to discover the satellite block size), and the map is divided into blocks called land segments which (loosely) represent the DirectX buffers. The terrain is then rendered in so many DIP calls as there are visible land segments in the scene.

It makes no sense to make the land segments larger than the satellite blocks. The faces in the buffers would have to be divided into sections by material, and each section would have to be drawn with a separate DIP call. However the engine is ready to do that if necessary, for example if the satellite blocks are really small or if the satellite grid is not regular.

Note: in multi-map, when several maps are glued together, the satellite grid should stay regular across the whole world. To design such a map, all it takes is to choose the size of the multi-map tile to be a multiple of the satellite block size. This is now easier to do with the non-power-of-two map sizes.

On the other hand, the land segments may end up smaller than the satellite blocks, because of the limitation that one buffer may not contain more than 32768 vertices. In that case the engine tries to make the land segment exactly one half, one third, etc. of the satellite block size, to get a good alignment with the materials. Also there is currently a limitation that the land segment will not be larger than 32 land grid cells.

Terrain LOD-ing

The engine implements a simple level-of-detail (LOD) algorithm in order to increase the rendering performance for higher view distances. Each land segment can be generated in up to 7 versions with decreasing level of detail. The most detailed LOD (numbered zero) contains all terrain grid vertices, the second most detailed LOD (number 1) contains only every other vertex, and so on. Higher LODs are used for distant segments. The distance where the first LOD switch occurs depends on the Terrain Detail setting in Video Options.

A clever technique is used to prevent visible LOD switching and also rendering gaps (cracks) between segments with different LODs. Each vertex of any LOD knows not only its own height, but also the height it would have in all higher LODs. Every land segment can be rendered at its own (correct) LOD, but can also be degenerated in the vertex shader to appear as any higher LOD. The LOD value is then in fact not a whole number (0, 1, 2, 3, 4, 5, 6), but a continuous (fractional) value. In the camera space, this value increases exponentially (but smoothly) with the distance from the camera. Each vertex uses a height that is a blend between the nearest LOD heights. As a result, the boundary between two segments is at the same LOD level and there are no gaps between non-matching triangles. Also the LOD switches are not abrupt but continuous.

VBS2 terrain lods.png

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox