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;
   }
   // 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
                      }
                  }
         }
  }