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