meta data for this page

Terrain system overview

The purpose of the terrain system code in the reference application is to provide client applications with a simple, efficient 3D terrain system that's useful for rapid prototyping.

terrain.jpg

How it works

The terrain system divides the world into a series of 2D square cells. As the camera roams the world, new cells are created and old ones are destroyed. Each time a new cell is created, the client application will be given the parameters necessary to populate it based on whatever data or algorithm is controlling the terrain geometry.

The core functionality of the terrain system is housed in the CMyTerrainEngine class, which contains the initialization, roaming, and culling code. The CMyTerrainRenderer class contains the code necessary to render it within the SpeedTree framework.

While the terrain vertex data is populated cell by cell, the system uses a series of index buffers. Level of detail (LOD) is handled by rendering a particular cell with a different index buffer, depending on its distance from the camera as well as its neighbors. This allows the system to drop triangles (by skipping over more and more vertices in success index buffers) and to ensure that adjacent cells match by using index buffers where lower LOD cells use higher tessellated edges to match their higher LOD neighbors. This also has the added advantage of only having to populate a given cell once, albeit at the highest LOD.

Note that the terrain engine does not support more than one height value per (x,y) point (meaning it doesn’t support overhangs).

Initialization

To get started, use the CMyTerrainEngine class, which can be initialized using the CMyTerrainEngine::Init() function:

st_bool CMyTerrainEngine::InitGeometry(st_int32   nNumLods, 
                                       st_int32   nMaxTileRes,
                                       st_float32 fCellSize);

The parameters are:

  • nNumLods: Determines the number of discrete LOD steps a tile will take from highest LOD to lowest. Each step cuts the number of triangles used by 75%.
  • nMaxTileRes: Sets the terrain tile resolution for the highest LOD. The tile is essentially a (nMaxTileRes X nMaxTileRes) mesh and the resolution drops in each successive LOD so that the next LOD would be (nMaxTileRes/2 X nMaxTileRes/2) and so forth. Note that because of this progression, the value must be a (power-of-two + 1) number like 17, 33, or 65. Other values will cause bad matches between LOD states.
  • fCellSize: Specifies the size of the cells used in rendering and culling. Picking a good size is important in striking a balance between efficient culling and quick rendering. Large cells result in faster culling times and fewer draw calls but also rendering a fair amount of offscreen geometry (consider a large cell whose corner is barely visible – the entire cell must be rendered). Small cells result in slower culling times and more draw calls, but far fewer wasted vertices. The reference application, whose world units are feet, uses a default cell size of 500.0.

Generally speaking, you should just need to have one terrain object in your world. Note that if you want to have the SpeedTree SDK handle rendering chores for your application, you can use the CMyTerrainRenderer class, which derives from the CMyTerrainEngine class. See the terrain rendering page for details.

Other initialization parameters

For the terrain culling system to work properly, it must be able to predict when a new terrain tile will be visible before it can know its complete extents. That is, if the cell's geometry is being provided by the application, but the application can't provide that geometry until it exists, some a priori knowledge is necessary. The terrain system can know the width and length of the tiles since they are easily computed and the same for each, but the height of the terrain is unknown. To assist with this, CMyTerrainEngine::SetHeightHints() is provided so that the client application can provide the expected low and high points for the terrain, allowing the culling/roaming algorithm to complete the 3D extents of the cells before they have been created.

The roaming system can use dynamic allocation while it's running. To prevent this, and account for all of its heap allocation up front, use CMyTerrainEngine::SetHeapReserves(). The only hint currently supported by the terrain system is SHeapReserves::m_nMaxVisibleTerrainCells. Once set properly, the terrain system will not make any additional heap allocation calls.