Demeter Terrain Engine >> Documentation >> Common Tasks

Home

News

Forums

Help
General
Mailing List
Screenshots
Documentation
Dependencies
Project Architecture
Core Library
Loaders
Building Demeter
Common Tasks
API Reference

Downloads

Consulting

Contribute

Report a Bug
Make a Request

CVS Access

Browse CVS tree

License

Common Tasks

Painting Detail Textures

One of Demeter's most exciting features is detail texture painting. You can paint "splats" of any texture at any point on the terrain, in whatever shapes and levels of opacity you like.  The uses for detail painting of this kind are endless. You can use it to procedurally paint grass and dirt textures across your entire terrain based on slope and elevation, to create "scorch marks", to draw tracks where vehicles pass, etc.

Painting is quite trivial following these steps:

  1. Create a Texture object for the texture image that you want to "splat" onto the terrain.
  2. Add the Texture object to the Terrain object's pool of shared textures by calling AddTexture() on the Terrain object's TextureSet.
  3. Call the Terrain object's Paint() method any number of times. Each time you call it, a "splat" of the texture will appear on the terrain surface.

Here is some sample code that demonstrates these steps:

Demeter::Texture* pDetailTexture =
    Demeter::Loader::GetInstance()->LoadTexture(
    "SDLTextureLoader",
    "mud.png,false,false,true");
int textureId = pTerrain->GetTextureSet()->AddTexture
    (pDetailTexture);
// Paint a "splat" at the location 1000.0f,1000.0f in world units
// The "splat" is 20 texels in size and it's alpha is 0.5f
// (half-transparent)

pTerrain->Paint(textureId,20,0.5f,1.0f,false,1000.0f,1000.0f);

Refer to the API Reference for details on all of the parameters of the Paint() method.

When you paint a "splat" on the terrain, the splat is mixed with any detail textures that may previously been painted in that area. The "intensity" and "maxIntensity" parameters of the Paint() method control how this mixing occurs. The "intensity" specifies how opaque the current splat will be. The "maxIntensity" param specifies that if other splats of that texture are already present, such that the current splat will be adding to the intensity of those splats already present, what the maximum amount of this particular texture is allowed to be. For example, if you are painting mostly transparent splats, but are painting many times to build up the intensity of the texture, but you want to specify that no matter how many times you splat the texture it never has more than 0.75 intensity, then you can pass 0.75 as the "maxIntensity" value and you are now free to safely splat knowing that even if you splat many times in the same area, the combined intensity of all of the splats will be clamped down to 0.75.

In addition to painting splats with the Paint() method, you can also erase them. Simply pass true instead of false for the bErase parameter, and be sure to set maxIntensity to 0.0 (or whatever minimum value you want to erase to.) The meaning of the "maxIntensity" parameter flips when erasing (it now acts as a minimum instead of a maximum.)

Mouse Picking

Demeter provides two ways to handle mouse picking: raytracing and depth buffer testing. Raytracing is extremely accurate but expensive, both in terms of speed and memory usage. Depth buffer testing is fast but its accuracy varies depending on the depth of the z-buffer and it is never as accurate as raytracing. Since most applications will only perform work when the user actually clicks, the relative expense of raytracing is usually negligible (as long as you can swallow its steep memory requirements.) Each technique is described below.

Raytracing

Raytracing is projecting a geometric ray from some starting point in 3D space, in some direction, and determining where that ray intersects the terrain's surface (if it intersects at all.) So to use raytracing for the purposes of mouse picking, we cast the ray from the point on the screen where the user clicked out into the 3D world behind the monitor. If this ray intersects the terrain surface, then we can say the user has clicked that point on the terrain.

To use raytracing features, you must compile Demeter with the preprocessor symbol _USE_RAYTRACING_SUPPORT_ #defined. Activating this feature causes all Terrains that are created to maintain a list of triangle objects for the purposes of raytracing, so enabling this feature will immediately cause all of your Terrain objects to consume significantly more memory.

Once you have compiled with _USE_RAYTRACING_SUPPORT_ #defined, you may call one of the IntersectRay() methods on Terrain objects in your application. See the API reference for a description of all of this method's parameters. Basically, you pass it the ray's starting point, the ray's direction as a normalized vector, and it returns to you the 3D point in world coordinates where the ray intersected the terrain. It also tells you how far away that point is from the ray's origin.

To construct a ray to be used for mouse picking, we must cast the ray from the user's "eye" position behind the viewplane, through the point on the viewplane where the mouse click occured, and out into the world. The following sample code shows one way to accomplish this.

#ifdef _USE_RAYTRACING_SUPPORT_
// If the user has clicked a mouse button...
if (event.type == SDL_MOUSEBUTTONDOWN)
{
    // If it's the left mouse button...
    if (event.button.button == 1)
    {
        Vector direction;
        direction.x = ((float)event.button.x /
                      (float)ScreenWidth) - 0.5f;
        direction.z = ((float)event.button.y /
                      (float)ScreenHeight) - 0.5f;
        direction.y = 1.0f;
        direction.z *= -1.0f;
       
direction.Normalize();
        // cameraTransform = simply the 4x4 matrix for the 
        //                   viewing xform

        direction = cameraTransform * direction;
        Vector intersect;
        float distance = pTerrain->IntersectRay(cameraPosition.x,
                         cameraPosition.y, currentCameraElevation,
                         direction.x, direction.y, direction.z,
                         intersect.x, intersect.y, intersect.z);
        if (0.0f <= distance)
        {
            cout << "The user clicked the point " << intersect.x
                 << "," << intersect.y << "," << intersect.z
                 << endl;
        }
    }
}

#endif

Depth Testing

Using the Depth buffer for mouse picking is far simpler and has no overhead cost. Simply call the Pick() method on the terrain object that you want to test against. See the API reference for all of this method's parameters. They basically consist of the 2D screen point that was clicked and it returns to you a 3D point where the click intersected the terrain surface. Using this technique, you do not have to figure out any viewing parameters, etc. as you do with the raytracing method. Also, this method does not require any changes to the compile of Demeter and does not use any extra memory.

Line-of-Sight / Collision Tests

The best way to do line-of-sight tests on a terrain is to use raytracing. You must follow the instructions in the section above on using raytracing for mouse picking. The only difference here is that instead of casting the ray from the view plane you are casting from an arbitrary point in 3D space. If the elements in your application (such as vehicles, etc.) have bounding spheres, then it is quite trivial to use Demeter's IntersectRay() method for the terrain and then also raytrace against your own bounding spheres. Whichever distance is less is the intersected object. This allows you to determine, for example, if a vehicle is visible from a point on the terrain. If you want accurate raytracing against your application elements, you can use hierarchies of spheres, where the bounding sphere is the root of the tree and there are some number of more detailed spheres that wrap the element accurately. If your raytrace is successful against the bounding sphere, you can traverse the nodes to more detailed spheres until the ray misses or you reach the lowest level nodes (which means the ray DOES intersect that element.) There is a good introduction to doing raytracing against spheres at http://www.2tothex.com/raytracing/basic.html.

 

 

SourceForge.net Logo Copyright ©2002 Clay Fowler