TerrainMeshDecal

From Ogre Wiki

Jump to: navigation, search

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;
  }
  // redefine quads
  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
                     }
                 }
        }
 }
Personal tools
administration