OGRE Wiki
Support and community documentation for Ogre3D
Ogre Forums
ogre3d.org
Log in
Username:
Password:
CapsLock is on.
Remember me (for 1 year)
Log in
Home
Tutorials
Tutorials Home
Basic Tutorials
Intermediate Tutorials
Mad Marx Tutorials
In Depth Tutorials
Older Tutorials
External Tutorials
Cookbook
Cookbook Home
CodeBank
Snippets
Experiences
Ogre Articles
Libraries
Libraries Home
Alternative Languages
Assembling A Toolset
Development Tools
OGRE Libraries
List of Libraries
Tools
Tools Home
DCC Tools
DCC Tutorials
DCC Articles
DCC Resources
Assembling a production pipeline
Development
Development Home
Roadmap
Building Ogre
Installing the Ogre SDK
Setting Up An Application
Ogre Wiki Tutorial Framework
Frequently Asked Questions
Google Summer Of Code
Help Requested
Ogre Core Articles
Community
Community Home
Projects Using Ogre
Recommended Reading
Contractors
Wiki
Immediate Wiki Tasklist
Wiki Ideas
Wiki Guidelines
Article Writing Guidelines
Wiki Styles
Wiki Page Tracker
Ogre Wiki Help
Ogre Wiki Help Overview
Help - Basic Syntax
Help - Images
Help - Pages and Structures
Help - Wiki Plugins
Toolbox
Freetags
Categories
List Pages
Structures
Trackers
Statistics
Rankings
List Galleries
Ogre Lexicon
Comments
History: MovableTextOverlay
View page
Source of version: 8
(current)
{maketoc} !!Introduction I started from ((ObjectTextDisplay|Xavier's ObjectTextDisplay)) example and eventually came up with these three classes, that do all the Text {LEX()}Overlay{LEX} positioning for you. The MovableTextOverlay class takes an Ogre {LEX()}MovableObject{LEX}, an Ogre String and a MovableTextOverlayAttributes pointer and makes your text moves with the Object on the screen, displaying it centered above the object ({LEX()}HUD{LEX} / heads up display). The MovableTextOverlayAttributes contain all the {LEX()}Font{LEX} / {LEX()}Material{LEX} / Colour {LEX()}Overlay{LEX} attributes. I put them into a separate class with the intention of creating "groups" of MovableTextOverlay objects that share same "attributes". The RectLayoutManager class is generic. It manages a list of non-overlapping rectangles within certain bounds. To find a non-overlapping position for a certain rectangle, it translates the rectangle on the Y axis until it finds a suitable position. I tried to optimise this class for speed as much as I could, any ideas are welcome. !!The Effect {IMG(src="img/wiki_up/Robots.jpg")}{IMG} !!The Code !!!MovableTextOverlay.h {CODE(wrap="1", colors="c++")}#ifndef __MovableTextOverlay_H__ #define __MovableTextOverlay_H__ #include "Ogre.h" #include "OgreFont.h" #include "OgreFontManager.h" using namespace Ogre; class MovableTextOverlayAttributes { public: MovableTextOverlayAttributes(const Ogre::String & name, const Ogre::Camera *cam, const Ogre::String & fontName = "BlueHighway", int charHeight = 16, const Ogre::ColourValue & color = Ogre::ColourValue::White, const Ogre::String & materialName = ""); ~MovableTextOverlayAttributes(); void setFontName(const Ogre::String & fontName); void setMaterialName(const Ogre::String & materialName); void setColor(const Ogre::ColourValue & color); void setCharacterHeight(unsigned int height); const Ogre::String& getName() const {return mName;} const Ogre::Camera* getCamera() const {return mpCam;} const Ogre::Font* getFont() const {return mpFont;} const Ogre::String& getFontName() const {return mFontName;} const Ogre::String& getMaterialName() const {return mMaterialName;} const Ogre::ColourValue& getColor() const {return mColor;} const Ogre::Real getCharacterHeight() const {return mCharHeight;} const Ogre::String mName; const Ogre::Camera *mpCam; Ogre::Font* mpFont; Ogre::String mFontName; Ogre::String mMaterialName; Ogre::ColourValue mColor; Ogre::Real mCharHeight; }; class MovableTextOverlay { public: MovableTextOverlay(const Ogre::String & name, const Ogre::String & caption, const Ogre::MovableObject *mov, MovableTextOverlayAttributes *attrs); virtual ~MovableTextOverlay(); void setCaption(const Ogre::String & caption); void setUpdateFrequency(Ogre::Real updateFrequency) {mUpdateFrequency = updateFrequency;} void setAttributes(MovableTextOverlayAttributes *attrs) { mAttrs = attrs; _updateOverlayAttrs(); } const Ogre::String& getName() const {return mName;} const Ogre::String& getCaption() const {return mCaption;} const Ogre::Real getUpdateFrequency() const {return mUpdateFrequency;} const bool isOnScreen() const {return mOnScreen;} const bool isEnabled() const {return mEnabled;} const MovableTextOverlayAttributes* getAttributes() const {return mAttrs;} void enable(bool enable); void update(Ogre::Real timeSincelastFrame); // Needed for RectLayoutManager. int getPixelsTop() {return Ogre::OverlayManager::getSingleton().getViewportHeight() * (mpOvContainer->getTop());} int getPixelsBottom() {return Ogre::OverlayManager::getSingleton().getViewportHeight() * (mpOvContainer->getTop() + mpOvContainer->getHeight());} int getPixelsLeft() {return Ogre::OverlayManager::getSingleton().getViewportWidth() * mpOvContainer->getLeft();} int getPixelsRight() {return Ogre::OverlayManager::getSingleton().getViewportWidth() * (mpOvContainer->getLeft() + mpOvContainer->getWidth());} void setPixelsTop(int px) {mpOvContainer->setTop((Ogre::Real)px / Ogre::OverlayManager::getSingleton().getViewportHeight());} // end protected: void _computeTextWidth(); void _updateOverlayAttrs(); void _getMinMaxEdgesOfTopAABBIn2D(Ogre::Real& MinX, Ogre::Real& MinY, Ogre::Real& MaxX, Ogre::Real& MaxY); void _getScreenCoordinates(const Ogre::Vector3& position, Ogre::Real& x, Ogre::Real& y); const Ogre::String mName; const Ogre::MovableObject* mpMov; Ogre::Overlay* mpOv; Ogre::OverlayContainer* mpOvContainer; Ogre::OverlayElement* mpOvText; // true if mpOvContainer is visible, false otherwise bool mEnabled; // true if mTextWidth needs to be recalculated bool mNeedUpdate; // Text width in pixels Ogre::Real mTextWidth; // the Text Ogre::String mCaption; // true if the upper vertices projections of the -MovableObject are on screen bool mOnScreen; // the update frequency in seconds // mpOvContainer coordinates get updated each mUpdateFrequency seconds. Ogre::Real mUpdateFrequency; // the Font/Material/Color text attributes MovableTextOverlayAttributes *mAttrs; }; #endif /* __MovableTextOverlay_H__ */{CODE} !!!MovableTextOverlay.cpp {CODE(wrap="1", colors="c++")}#include <OgreFontManager.h> #include <OgrePrerequisites.h> #include "MovableTextOverlay.h" MovableTextOverlay::MovableTextOverlay(const Ogre::String & name, const Ogre::String & caption, const Ogre::MovableObject *mov, MovableTextOverlayAttributes *attrs) : mpMov(mov) , mpOv(NULL) , mpOvContainer(NULL) , mpOvText(NULL) , mAttrs(attrs) , mName(name) , mCaption("") , mUpdateFrequency(0.01) , mNeedUpdate(TRUE) , mOnScreen(FALSE) , mEnabled(FALSE) { if (name == "") Ogre::Exception(Ogre::Exception::ERR_INVALIDPARAMS, "Trying to create MovableTextOverlay without name", "MovableTextOverlay::MovableTextOverlay"); if (caption == "") Ogre::Exception(Ogre::Exception::ERR_INVALIDPARAMS, "Trying to create MovableTextOverlay without caption", "MovableTextOverlay::MovableTextOverlay"); if (mAttrs == NULL) Ogre::Exception(Ogre::Exception::ERR_INVALIDPARAMS, "Trying to create MovableTextOverlay without Attributes", "MovableTextOverlay::MovableTextOverlay"); /* if(Ogre::OverlayManager::getSingleton().getByName(name + "_Ov")) { Ogre::Exception(Ogre::Exception::ERR_INVALIDPARAMS, "Trying to create MovableTextOverlay with a duplicate name", "MovableTextOverlay::MovableTextOverlay"); } */ // create an overlay that we can use for later mpOv = Ogre::OverlayManager::getSingleton().create(name+"_Ov"); mpOv->hide(); mpOvContainer = static_cast<Ogre::OverlayContainer*>(Ogre::OverlayManager::getSingleton().createOverlayElement( "Panel", name+"_OvC")); mpOvContainer->setDimensions(0.0, 0.0); mpOv->add2D(mpOvContainer); mpOvText = Ogre::OverlayManager::getSingleton().createOverlayElement("TextArea", name+"_OvTxt"); mpOvContainer->addChild(mpOvText); mpOvText->setMetricsMode(Ogre::GMM_RELATIVE); mpOvText->setDimensions(1.0, 1.0); mpOvText->setMetricsMode(Ogre::GMM_PIXELS); mpOvText->setPosition(0, 0); _updateOverlayAttrs(); setCaption(caption); } MovableTextOverlay::~MovableTextOverlay() { // overlay cleanup -- Ogre would clean this up at app exit but if your app // tends to create and delete these objects often it's a good idea to do it here. mpOv->hide(); Ogre::OverlayManager *overlayManager = Ogre::OverlayManager::getSingletonPtr(); mpOvContainer->removeChild(mName+"_OvTxt"); mpOv->remove2D(mpOvContainer); overlayManager->destroyOverlayElement(mpOvText); overlayManager->destroyOverlayElement(mpOvContainer); overlayManager->destroy(mpOv); } void MovableTextOverlay::setCaption(const Ogre::String & caption) { if (caption != mCaption) { mCaption = caption; mpOvText->setCaption(mCaption); mNeedUpdate = true; } } void MovableTextOverlay::_computeTextWidth() { const Font *pFont = mAttrs->getFont(); mTextWidth = 0; for(Ogre::String::iterator i = mCaption.begin(); i < mCaption.end();i++) { if (*i == 0x0020) mTextWidth += pFont->getGlyphAspectRatio(0x0030); else { mTextWidth += pFont->getGlyphAspectRatio(*i); } } mTextWidth *= mAttrs->getCharacterHeight(); } void MovableTextOverlay::_getMinMaxEdgesOfTopAABBIn2D(Ogre::Real& MinX, Ogre::Real& MinY, Ogre::Real& MaxX, Ogre::Real& MaxY) { const Ogre::Camera* mpCam = mAttrs->getCamera(); MinX = 0; MinY = 0; MaxX = 0; MaxY = 0; Ogre::Real X[4];// the 2D dots of the AABB in screencoordinates Ogre::Real Y[4]; if (!mpMov->isInScene()) return; const Ogre::AxisAlignedBox &AABB = mpMov->getWorldBoundingBox(true);// the AABB of the target const Ogre::Vector3 CornersOfTopAABB[4] = { AABB.getCorner(AxisAlignedBox::FAR_LEFT_TOP), AABB.getCorner(AxisAlignedBox::FAR_RIGHT_TOP), AABB.getCorner(AxisAlignedBox::NEAR_LEFT_TOP), AABB.getCorner(AxisAlignedBox::NEAR_RIGHT_TOP)}; Ogre::Vector3 CameraPlainNormal = mpCam->getDerivedOrientation().zAxis();//The normal vector of the plaine.this points directly infront of the cam Ogre::Plane CameraPlain = Plane(CameraPlainNormal,mpCam->getDerivedPosition());//the plaine that devides the space bevor and behin the cam for (int i = 0; i < 4; i++) { X[i] = 0; Y[i] = 0; _getScreenCoordinates(CornersOfTopAABB[i],X[i],Y[i]);// transfor into 2d dots if (CameraPlain.getSide(CornersOfTopAABB[i]) == Plane::NEGATIVE_SIDE) { if (i == 0)// accept the first set of values, no matter how bad it might be. { MinX = X[i]; MinY = Y[i]; MaxX = X[i]; MaxY = Y[i]; } else// now compare if you get "better" values { if (MinX > X[i])// get the x minimum { MinX = X[i]; } if (MinY > Y[i])// get the y minimum { MinY = Y[i]; } if (MaxX < X[i])// get the x maximum { MaxX = X[i]; } if (MaxY < Y[i])// get the y maximum { MaxY = Y[i]; } } } else { MinX = 0; MinY = 0; MaxX = 0; MaxY = 0; break; } } } void MovableTextOverlay::_getScreenCoordinates(const Ogre::Vector3& position, Ogre::Real& x, Ogre::Real& y) { const Ogre::Camera* mpCam = mAttrs->getCamera(); Vector3 hcsPosition = mpCam->getProjectionMatrix() * (mpCam->getViewMatrix() * position); x = 1.0f - ((hcsPosition.x * 0.5f) + 0.5f);// 0 <= x <= 1 // left := 0,right := 1 y = ((hcsPosition.y * 0.5f) + 0.5f);// 0 <= y <= 1 // bottom := 0,top := 1 } void MovableTextOverlay::enable(bool enable) { if (mEnabled == enable) return; mEnabled = enable; if (mEnabled) mpOv->show(); else mpOv->hide(); } void MovableTextOverlay::update(Real timeSincelastFrame) { static Real timeTillUpdate = 0; timeTillUpdate -= timeSincelastFrame; if (timeTillUpdate > 0) return; timeTillUpdate = mUpdateFrequency; Ogre::Real min_x, max_x, min_y, max_y; _getMinMaxEdgesOfTopAABBIn2D(min_x, min_y, max_x, max_y); if ((min_x>0.0) && (max_x<1.0) && (min_y>0.0) && (max_y<1.0)) mOnScreen = true; else mOnScreen = false; if (mNeedUpdate) { _computeTextWidth(); mNeedUpdate = false; } Real relTextWidth = mTextWidth / Ogre::OverlayManager::getSingleton().getViewportWidth(); Real relTextHeight = mAttrs->getCharacterHeight() / Ogre::OverlayManager::getSingleton().getViewportHeight(); mpOvContainer->setPosition(1-(min_x + max_x + relTextWidth)/2, 1-max_y); mpOvContainer->setDimensions(relTextWidth, relTextHeight); } void MovableTextOverlay::_updateOverlayAttrs() { const String &newMatName = mAttrs->getMaterialName(); const String &oldMatName = mpOvContainer->getMaterialName(); if (oldMatName != newMatName) { if (oldMatName.length()) mpOvContainer->getMaterial()->unload(); if (newMatName.length()) mpOvContainer->setMaterialName(newMatName); } mpOvText->setColour(mAttrs->getColor()); mpOvText->setParameter("font_name", mAttrs->getFontName()); mpOvText->setParameter("char_height", Ogre::StringConverter::toString(mAttrs->getCharacterHeight())); mpOvText->setParameter("horz_align", "left"); mpOvText->setParameter("vert_align", "top"); } MovableTextOverlayAttributes::MovableTextOverlayAttributes(const Ogre::String & name, const Ogre::Camera *cam, const Ogre::String & fontName, int charHeight, const Ogre::ColourValue & color, const Ogre::String & materialName) : mpCam(cam) , mpFont(NULL) , mName(name) , mFontName("") , mMaterialName("") , mCharHeight(charHeight) , mColor(ColourValue::ZERO) { if (fontName.length() == 0) Ogre::Exception(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid font name", "MovableTextOverlayAttributes::MovableTextOverlayAttributes"); setFontName(fontName); setMaterialName(materialName); setColor(color); } MovableTextOverlayAttributes::~MovableTextOverlayAttributes() { setFontName(""); setMaterialName(""); } void MovableTextOverlayAttributes::setFontName(const Ogre::String & fontName) { if (mFontName != fontName || !mpFont) { if (mpFont) { mpFont->unload(); mpFont = NULL; } mFontName = fontName; if (mFontName.length()) { mpFont = dynamic_cast<Ogre::Font*>(Ogre::FontManager::getSingleton().getByName(mFontName).getPointer()); if (!mpFont) Ogre::Exception(Ogre::Exception::ERR_ITEM_NOT_FOUND, "Could not find font " + fontName, "MovableTextOverlay::setFontName"); mpFont->load(); } } } void MovableTextOverlayAttributes::setMaterialName(const Ogre::String & materialName) { if (mMaterialName != materialName) { if (mMaterialName.length()) Ogre::MaterialManager::getSingletonPtr()->getByName(mMaterialName).getPointer()->unload(); mMaterialName = materialName; if (mMaterialName.length()) { Ogre::Material *mpMaterial = dynamic_cast<Ogre::Material*>(Ogre::MaterialManager::getSingletonPtr()->getByName(mMaterialName).getPointer()); if (!mpMaterial) Ogre::Exception(Ogre::Exception::ERR_ITEM_NOT_FOUND, "Could not find font " + materialName, "MovableTextOverlay::setMaterialName"); mpMaterial->load(); } } } void MovableTextOverlayAttributes::setColor(const Ogre::ColourValue & color) { mColor = color; } void MovableTextOverlayAttributes::setCharacterHeight(unsigned int height) { mCharHeight = height; }{CODE} !!!RectLayoutManager.h {CODE(wrap="1", colors="c++")}#ifndef __RectLayoutManager_H__ #define __RectLayoutManager_H__ #include <list> #include <algorithm> using namespace std; // It moves the rectangles on the Y axis so they won't overlap. class RectLayoutManager { public: enum MethodType { PLACE_ABOVE = 0, // the overlapping rectangles are placed above the non-overlapping ones PLACE_BELOW // the overlapping rectangles are placed below the non-overlapping ones }; class Rect { public: Rect(short left, short top, short right, short bottom) : mLeft(left) , mTop(top) , mRight(right) , mBottom(bottom) , dy(0) { if (mBottom <= mTop) throw std::exception("Condition Failure (top < bottom) in RectLayoutManager::Rect::Rect"); if (mRight <= mLeft) throw std::exception("Condition Failure (left < right) in RectLayoutManager::Rect::Rect"); } inline const short getTop() const {return mTop + dy;} inline const short getBottom() const {return mBottom + dy;} inline const short getLeft() const {return mLeft;} inline const short getRight() const {return mRight;} // STL needs this inline bool operator <(const RectLayoutManager::Rect &R) const {return getBottom() < R.getBottom();} // displacement on Y axis short dy; // original rectangle coordinates short mBottom; short mTop; short mLeft; short mRight; }; typedef std::list<RectLayoutManager::Rect> RectList; RectLayoutManager( unsigned short leftBound, unsigned short topBound, unsigned short rightBound, unsigned short bottomBound, MethodType method = PLACE_ABOVE) : mMethod(method) , mBoundTop(topBound) , mBoundLeft(leftBound) , mBoundBottom(bottomBound) , mBoundRight(rightBound) , mMinDistance(1) , mMaxRectHeight(0) , mDepth(0) { if (mBoundBottom <= mBoundTop) throw std::exception("Condition Failure (mBoundTop < mBoundBottom) in RectLayoutManager::RectLayoutManager"); if (mBoundRight <= mBoundLeft) throw std::exception("Condition Failure (mBoundLeft < mBoundRight) in RectLayoutManager::RectLayoutManager"); } ~RectLayoutManager(){clear();} const unsigned short getMinDistance() const {return mMinDistance;} const unsigned short getMaxRectHeight() const {return mMaxRectHeight;} const unsigned short getDepth() const {return mDepth;} void getBounds( unsigned short &left, unsigned short &top, unsigned short &right, unsigned short &bottom) { left = mBoundLeft; top = mBoundTop; right = mBoundRight; bottom = mBoundBottom; } void setMinDistance(unsigned short minDistance){mMinDistance = minDistance;} void setDepth(unsigned short depth){mDepth = depth;} bool isOutOfBounds(RectLayoutManager::Rect &r) { if (r.getTop() < mBoundTop || r.getBottom() > mBoundBottom || r.getLeft() < mBoundLeft || r.getRight() > mBoundRight) return true; else return false; } RectList::iterator getListBegin(){return mList.begin();} RectList::iterator getListEnd(){return mList.end();} void clear(){mList.clear();} RectList::iterator addData(Rect &Data) { if (isOutOfBounds(Data)) return mList.end(); // out of bounds, error switch (mMethod) { case PLACE_ABOVE: return addDataAbove(Data); case PLACE_BELOW: return addDataBelow(Data); default: return mList.end(); // incorrect method type, error } } protected: // List of orderedd rectangles // All items in list are assumed ordered and within established Bounds RectList mList; // The overlapping rectangles are placed at a mMinDistance from the other ones unsigned short mMinDistance; // This gets calculated "on the fly" unsigned short mMaxRectHeight; // An overlapping rectangle is moved on Y axis (Above or Below) untill // a place is found where it doesn't overlap any other rectangle. // mDepth is the number of times an overlapping rectangle will be moved // in order to find a non-overlapping place. // // (mDepth = 0) - the search will go on untill a place is found. // (mDepth > 0) - the search will go on <mDepth> times unsigned short mDepth; // Don't use these directly, use addData instead RectList::iterator addDataAbove(Rect &Data); RectList::iterator addDataBelow(Rect &Data); // Const variables can only be set in the constructor and certify the RectList integrity. // Method Type const MethodType mMethod; // Bounds const unsigned short mBoundTop; const unsigned short mBoundLeft; const unsigned short mBoundBottom; const unsigned short mBoundRight; }; static bool _fLessBottom(const RectLayoutManager::Rect &L, const RectLayoutManager::Rect &R) {return L.getBottom() < R.getBottom();} RectLayoutManager::RectList::iterator RectLayoutManager::addDataBelow(RectLayoutManager::Rect &Data) { bool MoveIt = false; bool FoundIt = false; RectList::iterator itStart; RectList::iterator itLastChecked; RectList::iterator itCurrent; RectList::iterator itInsert; unsigned short depth = 0; RectLayoutManager::Rect &r = Data; short height = r.getBottom() - r.getTop(); if (height > mMaxRectHeight) mMaxRectHeight = height; // find the first RECT that has .bottom > r.top // first possible intersect r.dy -= height; itStart = lower_bound(mList.begin(), mList.end(), r, &_fLessBottom); r.dy += height; // it's safe to add it at the back of the list if (itStart == mList.end()) { mList.push_back(r); return --(mList.end()); } // insert it temporarily (we will move it at the right place, afterwords) itInsert = mList.insert(itStart,r); for (itCurrent = itStart, itLastChecked = itInsert;itCurrent != mList.end();itCurrent++) { // Can't intersect r so i skip it if ((*itCurrent).getRight() < r.getLeft()) continue; // Can't intersect r so i skip it if (r.getRight() < (*itCurrent).getLeft()) continue; // Can't intersect r so i skip it if (r.getTop() > (*itCurrent).getBottom()) continue; short diff = (*itCurrent).getTop() - (*itLastChecked).getBottom(); short diff2 = mMaxRectHeight - ((*itCurrent).getBottom() - (*itCurrent).getTop()); if (diff > 0) // above the last checked { // If no rect overlapped r, then there is no need to move it if (!MoveIt && (diff > diff2)) { FoundIt = true; itLastChecked = itStart; break; } else MoveIt = true; if (mDepth && (depth >= mDepth)) break; // This is above r, so i check if its enought space to move r if (diff > height + diff2 + 2*mMinDistance) { r.dy = ((*itLastChecked).getBottom() + mMinDistance + 1) - r.getTop(); FoundIt = true; break; } depth++; } else // it overlaps MoveIt = true; itLastChecked = itCurrent; } if (itCurrent == mList.end()) { if (MoveIt) r.dy = ((*itLastChecked).getBottom() + mMinDistance + 1) - r.getTop(); else itLastChecked = itStart; FoundIt = true; } mList.erase(itInsert); if (FoundIt) { if (r.getBottom() > mBoundBottom) return mList.end(); // out of bounds itInsert = lower_bound(itLastChecked, itCurrent, r); itInsert = mList.insert(itInsert,r); return itInsert; }; return mList.end(); } static bool _fGreaterTop(const RectLayoutManager::Rect &L, const RectLayoutManager::Rect &R) {return L.getTop() > R.getTop();} RectLayoutManager::RectList::iterator RectLayoutManager::addDataAbove(RectLayoutManager::Rect &Data) { bool MoveIt = false; bool FoundIt = false; RectList::iterator itStart; RectList::iterator itLastChecked; RectList::iterator itCurrent; RectList::iterator itInsert; unsigned short depth = 0; RectLayoutManager::Rect &r = Data; short height = r.getBottom() - r.getTop(); if (height > mMaxRectHeight) mMaxRectHeight = height; // find the first RECT that has .bottom > r.top // first possible intersect r.dy += height; itStart = lower_bound(mList.begin(), mList.end(), r, &_fGreaterTop); r.dy -= height; // it's safe to add it at the back of the list if (itStart == mList.end()) { mList.push_back(r); return --(mList.end()); } // insert it temporarily (we will move it at the right place, afterwords) itInsert = mList.insert(itStart,r); for (itCurrent = itStart, itLastChecked = itInsert;itCurrent != mList.end();itCurrent++) { // Can't intersect r so i skip it if ((*itCurrent).getRight() < r.getLeft()) continue; // Can't intersect r so i skip it if (r.getRight() < (*itCurrent).getLeft()) continue; // Can't intersect r so i skip it if (r.getBottom() < (*itCurrent).getTop()) continue; short diff = (*itLastChecked).getTop() - (*itCurrent).getBottom(); short diff2 = mMaxRectHeight - ((*itCurrent).getBottom() - (*itCurrent).getTop()); if (diff > 0) // above the last checked { // If no rect overlapped r, then there is no need to move it if (!MoveIt && (diff > diff2)) { FoundIt = true; itLastChecked = itStart; break; } else MoveIt = true; if (mDepth && (depth >= mDepth)) break; // This is above r, so i check if its enought space to move r if (diff > height + diff2 + 2*mMinDistance) { r.dy = -(r.getBottom() - ((*itLastChecked).getTop() - mMinDistance - 1)); FoundIt = true; break; } depth++; } else // it overlaps MoveIt = true; itLastChecked = itCurrent; } if (itCurrent == mList.end()) { if (MoveIt) r.dy = -(r.getBottom() - ((*itLastChecked).getTop() - mMinDistance - 1)); else itLastChecked = itStart; FoundIt = true; } mList.erase(itInsert); if (FoundIt) { if (r.getTop() < mBoundTop) return mList.end(); // out of bounds itInsert = lower_bound(itLastChecked, itCurrent, r, _fGreaterTop); itInsert = mList.insert(itInsert,r); return itInsert; }; return mList.end(); } #endif /* __RectLayoutManager_H__ */{CODE} !!How to use it Add a global vector to hold your MovableTextOverlay pointers {CODE(wrap="1", colors="c++")}std::vector<MovableTextOverlay*> myVect;{CODE} __In your createScene__ {CODE(wrap="1", colors="c++")}Entity *ent = mSceneMgr->createEntity( "Robot", "robot.mesh" ); // get terrain height under the camera RaySceneQuery* raySceneQuery2 = mSceneMgr->createRayQuery(Ray(mCamera->getPosition(), Vector3::NEGATIVE_UNIT_Y)); RaySceneQueryResult& qryResult = raySceneQuery2->execute(); RaySceneQueryResult::iterator i = qryResult.begin(); if (i != qryResult.end() && i->worldFragment) { // create the attributes used by MovableTextOverlay MovableTextOverlayAttributes *attrs = new MovableTextOverlayAttributes("Attrs1",mCamera,"BlueHighway",16,ColourValue::White,"RedTransparent"); // add 10 MovableTextOverlay pointers to myVect for (int j=0; j<10;j++) { String k = StringConverter::toString(j); Entity *ent2 = ent->clone("Ent"+k); SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode( "Node"+k); node->setPosition(i->worldFragment->singleIntersection + Ogre::Vector3(j*10,0,0)); node->scale(Ogre::Vector3(0.1,0.1,0.1)); node->attachObject( ent2 ); MovableTextOverlay*p = new MovableTextOverlay("Text"+k," Robot"+k+" ", ent2, attrs); p->enable(false); // make it invisible for now p->setUpdateFrequency(0.01);// set update frequency to 0.01 seconds myVect.push_back(p); } }{CODE} __In your frameStarted__ {CODE(wrap="1", colors="c++")}RectLayoutManager m(0,0,mCamera->getViewport()->getActualWidth(), mCamera->getViewport()->getActualHeight()); m.setDepth(0); int visible=0; MovableTextOverlay *p=0; for (std::vector<MovableTextOverlay*>::iterator i = myVect.begin();i<myVect.end();i++) { p = *i; p->update(evt.timeSinceLastFrame); if (p->isOnScreen()) { visible++; RectLayoutManager::Rect r( p->getPixelsLeft(), p->getPixelsTop(), p->getPixelsRight(), p->getPixelsBottom()); RectLayoutManager::RectList::iterator it = m.addData(r); if (it != m.getListEnd()) { p->setPixelsTop((*it).getTop()); p->enable(true); } else p->enable(false); } else p->enable(false); }{CODE} __Media__ {CODE(wrap="1", colors="c++")}material RedTransparent { technique { pass { scene_blend alpha_blend lighting off depth_write off texture_unit { colour_op_ex source1 src_manual src_current 1 0 0 alpha_op_ex source1 src_manual src_current 0.4 } } } }{CODE}
Search by Tags
Search Wiki by Freetags
Latest Changes
IDE Eclipse
FMOD SoundManager
HDRlib
Building Ogre V2 with CMake
Ogre 2.1 FAQ
Minimal Ogre Collision
Artifex Terra
OpenMB
Advanced Mogre Framework
MogreSocks
...more
Search
Find
Advanced
Search Help
Online Users
102 online users