ObjectTitle         Display a title (text overlay) above a MovableObject

Yet Another?

I was having some wonky perspective issues with ObjectTextDisplay and I needed something that could automatically center the title-- so I decided to write my own solution. (See ObjectTitle::update for a walk-through of my approach).

The Code

ObjectTitle.h

#ifndef __ObjectTitle_H__
#define __ObjectTitle_H__

#include "Ogre.h"

class ObjectTitle
{
    const Ogre::MovableObject* object;
    const Ogre::Camera* camera;
    Ogre::Overlay* overlay;
    Ogre::OverlayElement* textArea;
    Ogre::OverlayContainer* container;
    Ogre::Vector2 textDim;
    Ogre::Font* font;

    Ogre::Vector2 getTextDimensions(Ogre::String text);
public:

    ObjectTitle(const Ogre::String& name, Ogre::MovableObject* object, Ogre::Camera* camera, const Ogre::String& title,
        const Ogre::String& fontName, const Ogre::ColourValue& color = Ogre::ColourValue::White);

    ~ObjectTitle();

    void setTitle(const Ogre::String& title);

    void update();
};

#endif

ObjectTitle.cpp

#include "ObjectTitle.h"
#include <OgreFontManager.h>

using namespace Ogre;

ObjectTitle::ObjectTitle(const String& name, MovableObject* object, Camera* camera, const String& title, 
    const String& fontName, const Ogre::ColourValue& color) : object(object), camera(camera)
{
    overlay = OverlayManager::getSingleton().create(name+"_TitleOverlay");
    container = (OverlayContainer*)OverlayManager::getSingleton().createOverlayElement("Panel", name+"_TitleContainer");
    overlay->add2D(container);

    textArea = OverlayManager::getSingleton().createOverlayElement("TextArea", name+"_TitleTextArea");
    textArea->setDimensions(0.8, 0.8);
    textArea->setMetricsMode(GMM_PIXELS);
    textArea->setPosition(0.1, 0.1);

    FontManager::getSingleton().load(fontName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    font = (Font*)FontManager::getSingleton().getByName(fontName).getPointer();
    textArea->setParameter("font_name", fontName);
    textArea->setParameter("char_height", font->getParameter("size"));
    textArea->setParameter("horz_align", "left");
    textArea->setColour(color);
    textArea->setCaption(title);

    container->addChild(textArea);
    overlay->show();

    textDim = getTextDimensions(title);
    container->setDimensions(textDim.x, textDim.y);
}

ObjectTitle::~ObjectTitle()
{
    OverlayManager *overlayManager = OverlayManager::getSingletonPtr();
    container->removeChild(textArea->getName());
    overlay->remove2D(container);
    overlayManager->destroyOverlayElement(textArea);
    overlayManager->destroyOverlayElement(container);
    overlayManager->destroy(overlay);
}

void ObjectTitle::setTitle(const String& title)
{
    textArea->setCaption(title);
    textDim = getTextDimensions(title);
    container->setDimensions(textDim.x, textDim.y);
}

void ObjectTitle::update()
{
    if(!object->isInScene())
    {
        overlay->hide();
        return;
    }

    // Derive the average point between the top-most corners of the object's bounding box
    const Ogre::AxisAlignedBox &AABB = object->getWorldBoundingBox(true);
    Ogre::Vector3 point = (AABB.getCorner(AxisAlignedBox::FAR_LEFT_TOP)
        + AABB.getCorner(AxisAlignedBox::FAR_RIGHT_TOP)
        + AABB.getCorner(AxisAlignedBox::NEAR_LEFT_TOP)
        + AABB.getCorner(AxisAlignedBox::NEAR_RIGHT_TOP)) / 4;

    // Is the camera facing that point? If not, hide the overlay and return.
    Ogre::Plane cameraPlane = Plane(Vector3(camera->getDerivedOrientation().zAxis()), camera->getDerivedPosition());
    if(cameraPlane.getSide(point) != Plane::NEGATIVE_SIDE)
    {
        overlay->hide();
        return;
    }
    
    // Derive the 2D screen-space coordinates for that point
    point = camera->getProjectionMatrix() * (camera->getViewMatrix() * point);

    // Transform from coordinate space [-1, 1] to [0, 1]
    Real x = (point.x / 2) + 0.5f;
    Real y = 1 - ((point.y / 2) + 0.5f);

    // Update the position (centering the text)
    container->setPosition(x - (textDim.x / 2), y);
    overlay->show();
}

Vector2 ObjectTitle::getTextDimensions(String text)
{
    Real charHeight = StringConverter::parseReal(font->getParameter("size"));

    Vector2 result(0, 0);

    for(String::iterator i = text.begin(); i < text.end(); i++)
    {   
        if (*i == 0x0020)
            result.x += font->getGlyphAspectRatio(0x0030);
        else
            result.x += font->getGlyphAspectRatio(*i);
    }

    result.x = (result.x * charHeight) / (float)camera->getViewport()->getActualWidth();
    result.y = charHeight / (float)camera->getViewport()->getActualHeight();

    return result;
}


--Ajs15822 08:08, 7 February 2008 (GMT)