History: MovableText
Preview of version: 8
Table of contents
What is it?
This class will allow you to write some text in the 3D world, the features are:
- The text is attached to a -SceneNode and therefore follows its movement and get smaller when further away
- The text is always facing the camera (like a -Billboard)
- The text can be positioned horizontally and vertically (center, left, etc...)
- The text can be translated on the UNIT_Y vector
How to use it
This class is very easy to use:
Copy to clipboard
MovableText* msg = new MovableText("TXT_001", "this is the caption"); msg->setTextAlignment(MovableText::H_CENTER, MovableText::V_ABOVE); // Center horizontally and display above the node /* msg->setAdditionalHeight( 2.0f ); //msg->setAdditionalHeight( ei.getRadius() ) // apparently not needed from 1.7*/ mNode->attachObject(msg);
OGRE v1.7 and Above Notes
To use this with OGRE 1.7 or newer versions, you need this resource package below:
No such attachment on this page
(A silly, easy to make mistake: don't forget to add the directory in the above archive to your resources.)
OGRE v1.6 and Below Notes
- Because of the latest modifications to the source to work with 1.7 and above, you may need to change BlueHighway-8 to BlueHighway if you're using 1.6 or below. (Or maybe you should consider updating your OGRE version 😊)
- To use MovableText on Ogre 1.6.x (Shoggoth), just add this:
Copy to clipboard
virtual void visitRenderables(Ogre::Renderable::Visitor* visitor, bool debugRenderables = false) {};
in the public method section.
- Default font: See note on the discussion page. (TODO: integrate content to this main page)
- Text is displayed from the center of the -SceneNode, setAdditionalHeight() allows you to translate the text on the UNIT_Y vector.
- For Mogre, you can use MOGRE MovableText
Source code
[+] MovableText.h
Copy to clipboard
/** * File: MovableText.h * * description: This create create a billboarding object that display a text. * * @author 2003 by cTh see gavocanov@rambler.ru * @update 2006 by barraq see nospam@barraquand.com * @update 2012 to work with newer versions of OGRE by MindCalamity <mindcalamity@gmail.com> * - See "Notes" on: http://www.ogre3d.org/tikiwiki/tiki-editpage.php?page=MovableText */ #ifndef __include_MovableText_H__ #define __include_MovableText_H__ namespace Ogre { class MovableText : public MovableObject, public Renderable { /******************************** MovableText data ****************************/ public: enum HorizontalAlignment {H_LEFT, H_CENTER}; enum VerticalAlignment {V_BELOW, V_ABOVE, V_CENTER}; protected: String mFontName; String mType; String mName; String mCaption; HorizontalAlignment mHorizontalAlignment; VerticalAlignment mVerticalAlignment; ColourValue mColor; RenderOperation mRenderOp; AxisAlignedBox mAABB; LightList mLList; Real mCharHeight; Real mSpaceWidth; bool mNeedUpdate; bool mUpdateColors; bool mOnTop; Real mTimeUntilNextToggle; Real mRadius; Vector3 mGlobalTranslation; Vector3 mLocalTranslation; Camera *mpCam; RenderWindow *mpWin; Font *mpFont; MaterialPtr mpMaterial; MaterialPtr mpBackgroundMaterial; /******************************** public methods ******************************/ public: MovableText(const String &name, const String &caption, const String &fontName = "BlueHighway-8", Real charHeight = 1.0, const ColourValue &color = ColourValue::White); virtual ~MovableText(); // Add to build on Shoggoth: virtual void visitRenderables(Ogre::Renderable::Visitor* visitor, bool debugRenderables = false); // Set settings void setFontName(const String &fontName); void setCaption(const String &caption); void setColor(const ColourValue &color); void setCharacterHeight(Real height); void setSpaceWidth(Real width); void setTextAlignment(const HorizontalAlignment& horizontalAlignment, const VerticalAlignment& verticalAlignment); void setGlobalTranslation( Vector3 trans ); void setLocalTranslation( Vector3 trans ); void showOnTop(bool show=true); // Get settings const String &getFontName() const {return mFontName;} const String &getCaption() const {return mCaption;} const ColourValue &getColor() const {return mColor;} Real getCharacterHeight() const {return mCharHeight;} Real getSpaceWidth() const {return mSpaceWidth;} Vector3 getGlobalTranslation() const {return mGlobalTranslation;} Vector3 getLocalTranslation() const {return mLocalTranslation;} bool getShowOnTop() const {return mOnTop;} AxisAlignedBox GetAABB(void) { return mAABB; } /******************************** protected methods and overload **************/ protected: // from MovableText, create the object void _setupGeometry(); void _updateColors(); // from MovableObject void getWorldTransforms(Matrix4 *xform) const; Real getBoundingRadius(void) const {return mRadius;}; Real getSquaredViewDepth(const Camera *cam) const {return 0;}; const Quaternion &getWorldOrientation(void) const; const Vector3 &getWorldPosition(void) const; const AxisAlignedBox &getBoundingBox(void) const {return mAABB;}; const String &getName(void) const {return mName;}; const String &getMovableType(void) const {static Ogre::String movType = "MovableText"; return movType;}; void _notifyCurrentCamera(Camera *cam); void _updateRenderQueue(RenderQueue* queue); // from renderable void getRenderOperation(RenderOperation &op); const MaterialPtr &getMaterial(void) const {assert(!mpMaterial.isNull());return mpMaterial;}; const LightList &getLights(void) const {return mLList;}; }; } //end namespace Ogre #endif
[+] MovableText.cpp
Copy to clipboard
/** * File: MovableText.cpp * * description: This create create a billboarding object that display a text. * * @author 2003 by cTh see gavocanov@rambler.ru * @update 2006 by barraq see nospam@barraquand.com * @update 2012 to work with newer versions of OGRE by MindCalamity <mindcalamity@gmail.com> * - See "Notes" on: http://www.ogre3d.org/tikiwiki/tiki-editpage.php?page=MovableText */ #include <stdafx.h> #include "OGRE/Ogre.h" #include "OGRE/OgreFontManager.h" #include "movableText.h" using namespace Ogre; #define POS_TEX_BINDING 0 #define COLOUR_BINDING 1 MovableText::MovableText(const String &name, const String &caption, const String &fontName, Real charHeight, const ColourValue &color) : mpCam(NULL) , mpWin(NULL) , mpFont(NULL) , mName(name) , mCaption(caption) , mFontName(fontName) , mCharHeight(charHeight) , mColor(color) , mType("MovableText") , mTimeUntilNextToggle(0) , mSpaceWidth(0) , mUpdateColors(true) , mOnTop(false) , mHorizontalAlignment(H_LEFT) , mVerticalAlignment(V_BELOW) , mGlobalTranslation(0.0) , mLocalTranslation(0.0) { assert(name != "" && "Trying to create MovableText without name"); assert(caption != "" && "Trying to create MovableText without caption"); mRenderOp.vertexData = NULL; this->setFontName(mFontName); this->_setupGeometry(); } MovableText::~MovableText() { if (mRenderOp.vertexData) delete mRenderOp.vertexData; // May cause crashing... check this and comment if it does if (!mpMaterial.isNull()) MaterialManager::getSingletonPtr()->remove(mpMaterial->getName()); } void MovableText::setFontName(const String &fontName) { if((Ogre::MaterialManager::getSingletonPtr()->resourceExists(mName + "Material"))) { Ogre::MaterialManager::getSingleton().remove(mName + "Material"); } if (mFontName != fontName || mpMaterial.isNull() || !mpFont) { mFontName = fontName; mpFont = (Font *)FontManager::getSingleton().getByName(mFontName).getPointer(); if (!mpFont) throw Exception(Exception::ERR_ITEM_NOT_FOUND, "Could not find font " + fontName, "MovableText::setFontName"); mpFont->load(); if (!mpMaterial.isNull()) { MaterialManager::getSingletonPtr()->remove(mpMaterial->getName()); mpMaterial.setNull(); } mpMaterial = mpFont->getMaterial()->clone(mName + "Material"); if (!mpMaterial->isLoaded()) mpMaterial->load(); mpMaterial->setDepthCheckEnabled(!mOnTop); mpMaterial->setDepthBias(1.0,1.0); mpMaterial->setDepthWriteEnabled(mOnTop); mpMaterial->setLightingEnabled(false); mNeedUpdate = true; } } void MovableText::setCaption(const String &caption) { if (caption != mCaption) { mCaption = caption; mNeedUpdate = true; } } void MovableText::setColor(const ColourValue &color) { if (color != mColor) { mColor = color; mUpdateColors = true; } } void MovableText::setCharacterHeight(Real height) { if (height != mCharHeight) { mCharHeight = height; mNeedUpdate = true; } } void MovableText::setSpaceWidth(Real width) { if (width != mSpaceWidth) { mSpaceWidth = width; mNeedUpdate = true; } } void MovableText::setTextAlignment(const HorizontalAlignment& horizontalAlignment, const VerticalAlignment& verticalAlignment) { if(mHorizontalAlignment != horizontalAlignment) { mHorizontalAlignment = horizontalAlignment; mNeedUpdate = true; } if(mVerticalAlignment != verticalAlignment) { mVerticalAlignment = verticalAlignment; mNeedUpdate = true; } } void MovableText::setGlobalTranslation( Vector3 trans ) { mGlobalTranslation = trans; } void MovableText::setLocalTranslation( Vector3 trans ) { mLocalTranslation = trans; } void MovableText::showOnTop(bool show) { if( mOnTop != show && !mpMaterial.isNull() ) { mOnTop = show; mpMaterial->setDepthBias(1.0,1.0); mpMaterial->setDepthCheckEnabled(!mOnTop); mpMaterial->setDepthWriteEnabled(mOnTop); } } void MovableText::_setupGeometry() { assert(mpFont); assert(!mpMaterial.isNull()); unsigned int vertexCount = static_cast<unsigned int>(mCaption.size() * 6); if (mRenderOp.vertexData) { // Removed this test as it causes problems when replacing a caption // of the same size: replacing "Hello" with "hello" // as well as when changing the text alignment //if (mRenderOp.vertexData->vertexCount != vertexCount) { delete mRenderOp.vertexData; mRenderOp.vertexData = NULL; mUpdateColors = true; } } if (!mRenderOp.vertexData) mRenderOp.vertexData = new VertexData(); mRenderOp.indexData = 0; mRenderOp.vertexData->vertexStart = 0; mRenderOp.vertexData->vertexCount = vertexCount; mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST; mRenderOp.useIndexes = false; VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration; VertexBufferBinding *bind = mRenderOp.vertexData->vertexBufferBinding; size_t offset = 0; // create/bind positions/tex.ccord. buffer if (!decl->findElementBySemantic(VES_POSITION)) decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT3, VES_POSITION); offset += VertexElement::getTypeSize(VET_FLOAT3); if (!decl->findElementBySemantic(VES_TEXTURE_COORDINATES)) decl->addElement(POS_TEX_BINDING, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0); HardwareVertexBufferSharedPtr ptbuf = HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(POS_TEX_BINDING), mRenderOp.vertexData->vertexCount, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); bind->setBinding(POS_TEX_BINDING, ptbuf); // Colours - store these in a separate buffer because they change less often if (!decl->findElementBySemantic(VES_DIFFUSE)) decl->addElement(COLOUR_BINDING, 0, VET_COLOUR, VES_DIFFUSE); HardwareVertexBufferSharedPtr cbuf = HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(COLOUR_BINDING), mRenderOp.vertexData->vertexCount, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); bind->setBinding(COLOUR_BINDING, cbuf); size_t charlen = mCaption.size(); float *pPCBuff = static_cast<float*>(ptbuf->lock(HardwareBuffer::HBL_DISCARD)); float largestWidth = 0; float left = 0 * 2.0 - 1.0; float top = -((0 * 2.0) - 1.0); Real spaceWidth = mSpaceWidth; // Derive space width from a capital A if (spaceWidth == 0) spaceWidth = mpFont->getGlyphAspectRatio('A') * mCharHeight * 2.0; // for calculation of AABB Ogre::Vector3 min, max, currPos; Ogre::Real maxSquaredRadius; bool first = true; // Use iterator String::iterator i, iend; iend = mCaption.end(); bool newLine = true; Real len = 0.0f; Real verticalOffset = 0; switch (mVerticalAlignment) { case MovableText::V_ABOVE: verticalOffset = mCharHeight; break; case MovableText::V_CENTER: verticalOffset = 0.5*mCharHeight; break; case MovableText::V_BELOW: verticalOffset = 0; break; } // Raise the first line of the caption top += verticalOffset; for (i = mCaption.begin(); i != iend; ++i) { if (*i == '\n') top += verticalOffset * 2.0; } for (i = mCaption.begin(); i != iend; ++i) { if (newLine) { len = 0.0f; for (String::iterator j = i; j != iend && *j != '\n'; j++) { if (*j == ' ') len += spaceWidth; else len += mpFont->getGlyphAspectRatio((unsigned char)*j) * mCharHeight * 2.0; } newLine = false; } if (*i == '\n') { left = 0 * 2.0 - 1.0; top -= mCharHeight * 2.0; newLine = true; continue; } if (*i == ' ') { // Just leave a gap, no tris left += spaceWidth; // Also reduce tri count mRenderOp.vertexData->vertexCount -= 6; continue; } Real horiz_height = mpFont->getGlyphAspectRatio((unsigned char)*i); Real u1, u2, v1, v2; Ogre::Font::UVRect utmp; utmp = mpFont->getGlyphTexCoords((unsigned char)*i); u1 = utmp.left; u2 = utmp.right; v1 = utmp.top; v2 = utmp.bottom; // each vert is (x, y, z, u, v) //------------------------------------------------------------------------------------- // First tri // // Upper left if(mHorizontalAlignment == MovableText::H_LEFT) *pPCBuff++ = left; else *pPCBuff++ = left - (len / 2); *pPCBuff++ = top; *pPCBuff++ = -1.0; *pPCBuff++ = u1; *pPCBuff++ = v1; // Deal with bounds if(mHorizontalAlignment == MovableText::H_LEFT) currPos = Ogre::Vector3(left, top, -1.0); else currPos = Ogre::Vector3(left - (len / 2), top, -1.0); if (first) { min = max = currPos; maxSquaredRadius = currPos.squaredLength(); first = false; } else { min.makeFloor(currPos); max.makeCeil(currPos); maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength()); } top -= mCharHeight * 2.0; // Bottom left if(mHorizontalAlignment == MovableText::H_LEFT) *pPCBuff++ = left; else *pPCBuff++ = left - (len / 2); *pPCBuff++ = top; *pPCBuff++ = -1.0; *pPCBuff++ = u1; *pPCBuff++ = v2; // Deal with bounds if(mHorizontalAlignment == MovableText::H_LEFT) currPos = Ogre::Vector3(left, top, -1.0); else currPos = Ogre::Vector3(left - (len / 2), top, -1.0); min.makeFloor(currPos); max.makeCeil(currPos); maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength()); top += mCharHeight * 2.0; left += horiz_height * mCharHeight * 2.0; // Top right if(mHorizontalAlignment == MovableText::H_LEFT) *pPCBuff++ = left; else *pPCBuff++ = left - (len / 2); *pPCBuff++ = top; *pPCBuff++ = -1.0; *pPCBuff++ = u2; *pPCBuff++ = v1; //------------------------------------------------------------------------------------- // Deal with bounds if(mHorizontalAlignment == MovableText::H_LEFT) currPos = Ogre::Vector3(left, top, -1.0); else currPos = Ogre::Vector3(left - (len / 2), top, -1.0); min.makeFloor(currPos); max.makeCeil(currPos); maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength()); //------------------------------------------------------------------------------------- // Second tri // // Top right (again) if(mHorizontalAlignment == MovableText::H_LEFT) *pPCBuff++ = left; else *pPCBuff++ = left - (len / 2); *pPCBuff++ = top; *pPCBuff++ = -1.0; *pPCBuff++ = u2; *pPCBuff++ = v1; currPos = Ogre::Vector3(left, top, -1.0); min.makeFloor(currPos); max.makeCeil(currPos); maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength()); top -= mCharHeight * 2.0; left -= horiz_height * mCharHeight * 2.0; // Bottom left (again) if(mHorizontalAlignment == MovableText::H_LEFT) *pPCBuff++ = left; else *pPCBuff++ = left - (len / 2); *pPCBuff++ = top; *pPCBuff++ = -1.0; *pPCBuff++ = u1; *pPCBuff++ = v2; currPos = Ogre::Vector3(left, top, -1.0); min.makeFloor(currPos); max.makeCeil(currPos); maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength()); left += horiz_height * mCharHeight * 2.0; // Bottom right if(mHorizontalAlignment == MovableText::H_LEFT) *pPCBuff++ = left; else *pPCBuff++ = left - (len / 2); *pPCBuff++ = top; *pPCBuff++ = -1.0; *pPCBuff++ = u2; *pPCBuff++ = v2; //------------------------------------------------------------------------------------- currPos = Ogre::Vector3(left, top, -1.0); min.makeFloor(currPos); max.makeCeil(currPos); maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength()); // Go back up with top top += mCharHeight * 2.0; float currentWidth = (left + 1)/2 - 0; if (currentWidth > largestWidth) largestWidth = currentWidth; } // Unlock vertex buffer ptbuf->unlock(); // update AABB/Sphere radius mAABB = Ogre::AxisAlignedBox(min, max); mRadius = Ogre::Math::Sqrt(maxSquaredRadius); if (mUpdateColors) this->_updateColors(); mNeedUpdate = false; } void MovableText::_updateColors(void) { assert(mpFont); assert(!mpMaterial.isNull()); // Convert to system-specific RGBA color; Root::getSingleton().convertColourValue(mColor, &color); HardwareVertexBufferSharedPtr vbuf = mRenderOp.vertexData->vertexBufferBinding->getBuffer(COLOUR_BINDING); RGBA *pDest = static_cast<RGBA*>(vbuf->lock(HardwareBuffer::HBL_DISCARD)); for (int i = 0; i < (int)mRenderOp.vertexData->vertexCount; ++i) *pDest++ = color; vbuf->unlock(); mUpdateColors = false; } const Quaternion& MovableText::getWorldOrientation(void) const { assert(mpCam); return const_cast<Quaternion&>(mpCam->getDerivedOrientation()); } // Add to build on Shoggoth: void MovableText::visitRenderables(Ogre::Renderable::Visitor* visitor, bool debugRenderables) { } const Vector3& MovableText::getWorldPosition(void) const { assert(mParentNode); return mParentNode->_getDerivedPosition(); } void MovableText::getWorldTransforms(Matrix4 *xform) const { if (this->isVisible() && mpCam) { Matrix3 rot3x3, scale3x3 = Matrix3::IDENTITY; // store rotation in a matrix mpCam->getDerivedOrientation().ToRotationMatrix(rot3x3); // parent node position Vector3 ppos = mParentNode->_getDerivedPosition() + Vector3::UNIT_Y*mGlobalTranslation; ppos += rot3x3*mLocalTranslation; // apply scale scale3x3[0][0] = mParentNode->_getDerivedScale().x / 2; scale3x3[1][1] = mParentNode->_getDerivedScale().y / 2; scale3x3[2][2] = mParentNode->_getDerivedScale().z / 2; // apply all transforms to xform *xform = (rot3x3 * scale3x3); xform->setTrans(ppos); } } void MovableText::getRenderOperation(RenderOperation &op) { if (this->isVisible()) { if (mNeedUpdate) this->_setupGeometry(); if (mUpdateColors) this->_updateColors(); op = mRenderOp; } } void MovableText::_notifyCurrentCamera(Camera *cam) { mpCam = cam; } void MovableText::_updateRenderQueue(RenderQueue* queue) { if (this->isVisible()) { if (mNeedUpdate) this->_setupGeometry(); if (mUpdateColors) this->_updateColors(); queue->addRenderable(this, mRenderQueueID, OGRE_RENDERABLE_DEFAULT_PRIORITY); //queue->addRenderable(this, mRenderQueueID, RENDER_QUEUE_SKIES_LATE); } }
Alias: MovableText_Eihort