If you haven't done so already, be sure to visit the Wiki Portal to read about how the wiki works. Especially the Ogre Wiki Overview page.
Table of contents
Introduction
This is a quick implementation of Intermediate Tutorial 6. The class TerrainDecal represents a texture that is projected orthogonaly on top of the terrain from a given point and with a given size. This class will detect the terrain pages that are affected (limited to four, because only the four corners of the projection area are tested) and adds a pass to the materials of those pages.
If the Decal has to move, just call updatePosition(). I think you can guess the meaning of updateSize(). ;)
Using the class
on startup:
- init (doesn`t show the decal, just prepares)
- updatePosition
- show
after that, do whenever necessary:
- hide
- show
- updatePosition
- updateSize
Performance
One problem with this implementation is the performance: since every decal adds another pass to the underlaying terrain pages, the fps hit is quite hard as soon as multiple decals act at the same time. So I will be happy to accept optimizations! There are some promising new approaches in this forum thread
, which I will investigate further in the near future.
Code
class TerrainDecal { protected: Ogre::Vector3 mPos; // center Ogre::Vector2 mSize; // size of decal std::string mTexture; // texture to apply Ogre::SceneNode* mNode; // the projection node Ogre::Frustum* mFrustum; // the projection frustum Ogre::SceneManager* mSceneManager; // pointer to PLSM2 bool mVisible; // is the decal visible/active or not? // info about materials that are receiving the decal std::map<std::string,Ogre::Pass*> mTargets; // passes mapped by material names bool isPosOnTerrain(Ogre::Vector3 pos) { // get the terrain boundaries Ogre::AxisAlignedBox box; mSceneManager->getOption("MapBoundaries",&box); // check if pos is in box, ignore y pos.y = 0; return box.intersects(pos); } std::string getMaterialAtPosition(Ogre::Vector3 pos) { void* myOptionPtr = &pos; // check if position is on battlefield if( isPosOnTerrain(pos) ) { mSceneManager->getOption ("getMaterialPageName", myOptionPtr); std::string name = **static_cast<std::string**>(myOptionPtr); return name; } else return ""; } void addMaterial(std::string matName) { // check if material is already decalled if( mTargets.find(matName) != mTargets.end() ) { Ogre::LogManager::getSingleton().getDefaultLog()->logMessage("material should be added to decal but was already!"); return; } using namespace Ogre; // get the material ptr MaterialPtr mat = (MaterialPtr)MaterialManager::getSingleton().getByName(matName); // create a new pass in the material to render the decal Pass* pass = mat->getTechnique(0)->createPass(); // set up the decal's texture unit TextureUnitState *texState = pass->createTextureUnitState(mTexture); texState->setProjectiveTexturing(true, mFrustum); texState->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); texState->setTextureFiltering(FO_POINT, FO_LINEAR, FO_NONE); // set our pass to blend the decal over the model's regular texture pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); pass->setDepthBias(1); // set the decal to be self illuminated instead of lit by scene lighting pass->setLightingEnabled(false); // save pass in map mTargets[matName] = pass; Ogre::LogManager::getSingleton().getDefaultLog()->logMessage(std::string("added material to decal: ") + matName + "(" + Ogre::StringConverter::toString(mTargets.size()) + " materials loaded)"); } std::map<std::string,Ogre::Pass*>::iterator removeMaterial(std::string matName) { // remove our pass from the given material mTargets[matName]->getParent()->removePass(mTargets[matName]->getIndex()); Ogre::LogManager::getSingleton().getDefaultLog()->logMessage(std::string("removed material from decal: ") + matName + "(" + Ogre::StringConverter::toString(mTargets.size()-1) + " materials loaded)"); // remove from map return mTargets.erase(mTargets.find(matName)); } public: TerrainDecal() { mVisible = false; mNode = 0; mFrustum = 0; }; ~TerrainDecal() { hide(); // delete frustum mNode->detachAllObjects(); delete mFrustum; // destroy node mNode->getParentSceneNode()->removeAndDestroyChild(mNode->getName()); }; void init( Ogre::SceneManager* man, Ogre::Vector2 size, std::string tex ) { using namespace Ogre; // set SM mSceneManager = man; // init projective decal // set up the main decal projection frustum mFrustum = new Ogre::Frustum(); mNode = mSceneManager->getRootSceneNode()->createChildSceneNode(); mNode->attachObject(mFrustum); mFrustum->setProjectionType(Ogre::PT_ORTHOGRAPHIC); mNode->setOrientation(Ogre::Quaternion(Ogre::Degree(90),Ogre::Vector3::UNIT_X)); // set given values updateSize(size); mTexture = tex; // texture to apply // load the images for the decal and the filter TextureManager::getSingleton().load (mTexture, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, 1); mVisible = false; } void show() { if( !mVisible ) { mVisible = true; updatePosition(mPos); } } void hide() { if( mVisible ) { // remove all added passes while( !mTargets.empty() ) removeMaterial(mTargets.begin()->first); mVisible = false; } } void updatePosition( Ogre::Vector3 pos ) { // don`t do anything if pos didn`t change if( pos == mPos ) return; // save the new position mPos = pos; mNode->setPosition(pos.x,pos.y+1000,pos.z); // don`t show if invisible if( !isVisible() ) return; // check near pages (up to 4) std::list<std::string> neededMaterials; Ogre::Vector3 t; // x high z high t = Ogre::Vector3(mPos.x+mSize.x/2.0f,1000,mPos.z+mSize.y/2.0f); neededMaterials.push_back(getMaterialAtPosition(t)); // x high z low t = Ogre::Vector3(mPos.x+mSize.x/2.0f,1000,mPos.z-mSize.y/2.0f); neededMaterials.push_back(getMaterialAtPosition(t)); // x low z low t = Ogre::Vector3(mPos.x-mSize.x/2.0f,1000,mPos.z-mSize.y/2.0f); neededMaterials.push_back(getMaterialAtPosition(t)); // x low z high t = Ogre::Vector3(mPos.x-mSize.x/2.0f,1000,mPos.z+mSize.y/2.0f); neededMaterials.push_back(getMaterialAtPosition(t)); // remove empties neededMaterials.remove(""); // remove doubles neededMaterials.unique(); // compare needed materials with used // for every used material std::map<std::string,Ogre::Pass*>::iterator used = mTargets.begin(); while(true) { // stop if we are through if( used == mTargets.end() ) break; // find in needed std::list<std::string>::iterator needed = std::find(neededMaterials.begin(),neededMaterials.end(),used->first); if( needed == neededMaterials.end() ) { // material is not needed any longer, so remove it used = removeMaterial(used->first); } else { // remove it from needed list, bedause it is already loaded neededMaterials.remove(used->first); // go further used++; } } // add all remaining needed to used list while( !neededMaterials.empty() ) { addMaterial(neededMaterials.front()); neededMaterials.erase(neededMaterials.begin()); } } void updateSize(Ogre::Vector2 size) { if( mSize != size ) { // save param mSize = size; // update aspect ratio mFrustum->setAspectRatio(mSize.x/mSize.y); // update height mFrustum->setOrthoWindowHeight(mSize.y); } } bool isVisible() { return mVisible; } };
Alias: Projective_Decals
Contributors to this page: JustBoo
,
Soldans
and
jacmoe
.
Page last modified on Monday 28 of June, 2010 20:19:56 UTC by JustBoo.
The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.

