The dtTerrain Tutorial, Part 2 - Create Your Terrain

After reading part 1 you should now be versed in the mechanics of dtTerrain. Understanding the logic behind the terrain will help for the following part of our tutorial. We will demonstrate how to create a reader, renderer and decoration layer in code. I would like to point out the fact that only the most important parts of the reader, renderer and decorator will be shown here. If you would like to see complete example code then grab your copy of Delta3D and check out the dtTerrain project. You can also download the Reader, Renderer and Decorator here.

1. Terrain Reader

Letís start off by examining the DTED terrain reader included with dtTerrain. All terrain readers must extend the base abstract TerrainDataReader class. Make sure that you override the virtual functions for OnLoadTerrainTile, GetDataType, and GenerateTerrainTileCachePath.

The following is our constructor for the DTED terrain reader. Since DTED can be multiple levels we have defined an enum to assign an appropriate level.

DTEDTerrainReader(const DTEDLevelEnum &level = DTEDLevelEnum::ONE,
            const std::string &name="DTEDReader");

The following method must be included; it returns the type of terrain that is supported by this reader.

const TerrainDataType &GetDataType() const { return TerrainDataType::DTED; }

The OnLoadTerrainTile is also required. This method is called by the terrain when a tile needs to be loaded. This method will first check the tile's cache for the heightfield, if it is not found the method will then load the DTED data for the specified tile. If any errors occur during the load process, the appropriate exception is thrown.

virtual void OnLoadTerrainTile(PagedTerrainTile &tile); 

The GenerateTerrainTileCachePath generates the cache path for the specified tile. The cache path is equal to: $Latitude_$Longitude_$DTEDLevel.

const std::string GenerateTerrainTileCachePath(const PagedTerrainTile &tile);       

For our DTED reader the following is used to set the level of DTED supported.

void SetDTEDLevel(const DTEDLevelEnum &level);

This is our get method for the DTED level.

const DTEDLevelEnum &GetDTEDLevel() const
    return *mDTEDLevel;

This concludes the creation of a very simple DTED Reader. For the full code please take a look at the dtTerrain project file included in the Delta3D code release. This brief introduction should help get you started creating your own readers for different terrain types.

2. Terrain Renderer

This next area describes the included SOARX Renderer that comes with dtTerrain. A renderer describes how our data will be interpreted and rendered in our scene. The renderer takes the terrain tile information, namely the heightmap, and interprets the data to create a realistic landscape. If you run the testTerrain demo you will see that the terrain has an increasing level of detail the closer you get to the ground, a property of the SOARX algorithm.

In order to create a renderer you will need to override the virtual functions of OnLoadTerrainTile(), GetHeight(), GetNormal(), and GetRootDrawable(). We will briefly walk through the sample SOARX renderer code.

First we create our SOARX constructor.

SoarXTerrainRenderer(const std::string &name="SoarXRenderer");

Then it is required that we implement the OnLoadTerrainTile function. This method constructs a SoarXDrawable for the new terrain tile that needs to be loaded.

void OnLoadTerrainTile(PagedTerrainTile &tile);

We also have created an unload terrain tile function. This is to update the internal map of tiles and drawables based on what tiles were unloaded from the terrain.

void OnUnloadTerrainTile(PagedTerrainTile &tile);

Here we have implemented the GetHeight function, which gets the height of the terrain at a specified x,y coordinate.

float GetHeight(float x, float y);

We also need to implement the GetNormal function in order to return a vector at a specified point x,y.

osg::Vec3 GetNormal(float x, float y);

Lastly we need to implement the GetRootDrawable() method in order to return a scene node that encapsulates the renderable terrain. This will return an open scene graph group node with all the information required to render the terrain.

osg::Group *GetRootDrawable() { return mRootGroupNode.get(); }

3. Decoration Layer

Now that we have our terrain renderer created we should probably go over our GENETICS decorator a.k.a the vegetation decorator. The decorator creates a vegetation layer of objects that are returned as an OSG group node. All objects have their own positions related to geo-specific land cover classification (LCC) data.

The methods of OnLoadTerrainTile and GetOSGNode are required in order to create a valid decoration layer.

First we define our constructor for the vegetation decorator.

VegetationDecorator(const std::string &name="VegetationDecoratorLayer");

The following calculates various land classification images and procedurally builds a list of trees and other vegetation models which are then placed about the specified terrain tile.

virtual void OnLoadTerrainTile(PagedTerrainTile &tile);

When called, the OnUnloadTerrainTile method will remove the vegetation models from the map of currently visible tiles/vegetation.

virtual void OnUnloadTerrainTile(PagedTerrainTile &tile);

The OnTerrainTileResident places the actual vegetation geometry on the terrain. The vegetation placement occurs here since it is dependent on querying for the height of the terrain in order to place the vegetation. The terrain height can only be queried once it has been loaded by the renderer. Performing this ensures that the renderer has properly loaded this tile's data.

virtual void OnTerrainTileResident(PagedTerrainTile &tile);

This function will return the root scenegraph node for the vegetation. This is a required function. This contains all the transformed geometry that is placed at the correct height, as defined by the terrain tiles heightmap.

virtual osg::Node *GetOSGNode() { return mVegetationNode.get(); }

The AddVegetation function takes a terrain tile as a parameter and creates the group scenegraph node for the vegetation. This is where all the magic happens. The AddVegetation uses the heightmap of the terrain tile and a geotif to calculate a base land classification image, a slope map, and a relative elevation map. The slope map and relative elevation maps are used to create a probability of the placement of vegetation at different areas of a tile. The base land classification image defines the regions of where specific types of vegetation objects are placed. These different techniques combine to form an intelligent layer of vegetation that are ready to be added to the scene.

osg::Group *AddVegetation(PagedTerrainTile &tile);

Although we briefly covered the vegetation decorator, we have defined the key elements of a decorator in enough detail that you should be able to create your own. If you would like to see the full code, check it out in the dtTerrain project included with Delta3D. For a straight forward sample decorator check out the geotiffdecorator included in the sample zip here.

Head over to the third part of our tutorial by clicking here


Trackback URL for this entry:

No trackback comments for this entry.

About delta3d

delta3d is a game and simulation engine appropriate for a wide variety of simulation and entertainment applications. delta3d uses best-of-breed open source technologies to create a fully integrated game engine and with content creation tools.MORE


User Functions

Don't have an account yet? Sign up as a New User!

Lost your password?