Skip to main content

History: MovableText

Source of version: 7

Copy to clipboard
            !!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:
{CODE(wrap="1", colors="c++")}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); {CODE}

!! OGRE v1.7 and Above Notes
To use this with OGRE 1.7 or newer versions, you need this resource package below:
{ATTACH(name="Updated Resources",file="fonts.zip",showdesc="1",bullets="0",dls="1",icon="1")}{ATTACH}
(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:
{CODE(wrap="1", colors="c++")}virtual void visitRenderables(Ogre::Renderable::Visitor* visitor, bool debugRenderables = false) {};{CODE}
in the public method section.
* Default font: See note on the ((Talk:MovableText|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|Mogre)), you can use ((MOGRE MovableText))

!!Source code
!!!-MovableText.h
{CODE(wrap="1", colors="c++")}
/**
* 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
{CODE}

!!!-MovableText.cpp
{CODE(wrap="1", colors="c++")}
/**
* 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);
    }
}
{CODE}

---
Alias: (alias(MovableText_Eihort))
        

History

Information Version
Wed 23 of Dec, 2020 15:29 GMT-0000 paroj 13
Mon 14 of Dec, 2020 02:56 GMT-0000 paroj 12
Thu 26 of Mar, 2015 20:19 GMT-0000 Jayray 11
Fri 26 of Oct, 2012 09:45 GMT-0000 Mind Calamity Added a bugfix to reduce triangle count by Wladimir Lukutin. 10
Wed 26 of Sep, 2012 10:09 GMT-0000 Mind Calamity 9
Tue 31 of Jul, 2012 03:58 GMT-0000 Mind Calamity Added table of contents and updated code to work with the latest OGRE versions. 8
Tue 31 of Jul, 2012 03:51 GMT-0000 Mind Calamity 7
Sat 03 of Mar, 2012 19:01 GMT-0000 Diamond145 Never throw exceptions in ctor's 6
Tue 29 of Mar, 2011 09:24 GMT-0000 jacmoe 5
Sat 02 of Jan, 2010 04:22 GMT-0000 jacmoe 4
Thu 24 of Dec, 2009 17:10 GMT-0000 jacmoe 3
Thu 24 of Dec, 2009 17:10 GMT-0000 jacmoe 2
Tue 17 of Nov, 2009 17:26 GMT-0000 Spacegaier 1