User Tools

Site Tools


The primary purpose of the built-in terrain system in the SpeedTree SDK is not necessarily to be a state-of-the-art/cutting-edge terrain engine, rather it is to provide client applications with a simple, efficient 3D terrain system that's useful for rapid prototyping.

Note that the terrain engine does not support more than one height value point (i.e. no overhangs).

How It Works

The terrain system divides the world into a series of 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 Forest library, which contains the initialization, roaming, and culling code. The Render Interface library 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 composited into one large index buffer. LOD is handled by rendering a particular cell with 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 tesselated 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. Watch the reference application in wireframe mode to observe the effect.


To get started, simply #include “Forest/Terrain.h” to get the core, non-graphical terrain system functionality. Under the namespace SpeedTree, it defines a class called CTerrain which can be initialized using the CTerrain::Init() function:

st_bool CTerrain::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 there is far less 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 CTerrain object in your world. Note that if you want to have the SpeedTree SDK handle rendering chores for your application, you can use the CTerrainRI, which derives from the CTerrain 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 it 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, CTerrain::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 CTerrain::SetHint(). The only hint currently supported is CTerrain::HINT_MAX_NUM_VISIBLE_CELLS. Once set properly, the terrain system will not make any additional heap allocation calls.