TerrainMeshDecal         Using a mesh decal to show a marker on the terrain for terrain selection

## Terrain Selection using a Mesh Decal

When reading Intermediate Tutorial 5, I wanted to use a decal to indicate where the user clicks on the terrain. With help from the forums, here's some simple code to achieve that goal with a mesh decal. The idea is simple - create a small square mesh that sits just above the terrain where the user clicked, and texture it.

First, creating a manual object for the mesh. "Crosshairs" is the name of the material I'm using. Since this attaches the mesh to the scene, it should only be called once.

// Initial creation of the mesh decal
void createMeshDecal()
{
mMeshDecal = new Ogre::ManualObject("MeshDecal");
mSceneMgr->getRootSceneNode()->attachObject(mMeshDecal);

int x_size = 4;  // number of polygons
int z_size = 4;

mMeshDecal->begin("Crosshairs", Ogre::RenderOperation::OT_TRIANGLE_LIST);
for (int i=0; i<=x_size; i++)
{
for (int j=0; j<=z_size; j++)
{
mMeshDecal->position(Ogre::Vector3(i, 0, j));
mMeshDecal->textureCoord((float)i / (float)x_size, (float)j / (float)z_size);
}
}

for (int i=0; i<x_size; i++)
{
for (int j=0; j<z_size; j++)
{
mMeshDecal->quad( i * (x_size+1) + j,
i * (x_size+1) + j + 1,
(i + 1) * (x_size+1) + j + 1,
(i + 1) * (x_size+1) + j);
}
}
mMeshDecal->end();
}

Next, the code I call when the user clicks on the terrain. "rad" is the "radius" of the decal - I had vague thoughts of making the decal larger if it's further in the distance.

// Update mesh decal when terrain selection happens
void setMeshDecal(Ogre::Real x, Ogre::Real z, Ogre::Real rad)
{
Ogre::Real x1 = x - rad;
Ogre::Real z1 = z - rad;

int x_size = 4;  // number of polygons
int z_size = 4;

Ogre::Real x_step = rad * 2/ x_size;
Ogre::Real z_step = rad * 2/ z_size;

mMeshDecal->beginUpdate(0);
// redefine vertices
for (int i=0; i<=x_size; i++)
{
for (int j=0; j<=z_size; j++)
{
mMeshDecal->position(Ogre::Vector3(x1, getTerrainHeight(x1, z1) + 1, z1));
mMeshDecal->textureCoord((float)i / (float)x_size, (float)j / (float)z_size);
z1 += z_step;
}
x1 += x_step;
z1 = z - rad;
}
for (int i=0; i<x_size; i++)
{
for (int j=0; j<z_size; j++)
{
mMeshDecal->quad( i * (x_size+1) + j,
i * (x_size+1) + j + 1,
(i + 1) * (x_size+1) + j + 1,
(i + 1) * (x_size+1) + j);
}
}
mMeshDecal->end();
}

Finally, figuring out the intersection with the ground:

Ogre::Real getTerrainHeight(Ogre::Real x, Ogre::Real z)
{
Ogre::Ray* verticalRay = new Ogre::Ray( Ogre::Vector3(x, 5000, z), Ogre::Vector3::NEGATIVE_UNIT_Y );
mRaySceneQuery->setRay( *verticalRay );

// Execute query
Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();
Ogre::RaySceneQueryResult::iterator itr = result.begin( );

if ( itr != result.end() && itr->worldFragment )
{
Ogre::Vector3 intersection = itr->worldFragment->singleIntersection;
return intersection.y;
}
else
{
return 0;
}
}

And last, the material I'm using. Crosshairs.png is the image file from Intermediate Tutorial 5.

material Crosshairs
{
technique
{
pass
{
scene_blend alpha_blend
depth_write off
texture_unit
{
texture Crosshairs.png
scale 1 1
}
}
}
}

5 online users