Table of contents
Introduction
This article presents a class that extends the TextAreaOverlayElement allowing for different colors to be applied to each character in the string individually. The behaviour was designed to mimic the Quake 3 coloring scheme. This should be very easy to integrate into existing code since the interface is virtually identical to the TextAreaOverlayElement interface. For example, consider the following code:
TextAreaOverlayElement *pTextArea = (TextAreaOverlayElement*)OverlayManager.createOverlayElement("TextArea", "MyTextArea"); pTextArea->setCaption("Some plain text"); ...
Simply needs to be changed to:
TextAreaOverlayElement *pTextArea = (TextAreaOverlayElement*)OverlayManager.createOverlayElement("ColoredTextArea", "MyTextArea"); pTextArea->setCaption("Some ^1red^7 text"); ...
Before this code will work however, the new overlay must be registered using the ColoredTextAreaOverlayElementFactory. Here is some sample code on how to do that:
In header file
ColoredTextAreaOverlayElementFactory* pColoredTextAreaOverlayElementFactory;
Initialisation code
// Register the overlay element m_pColoredTextAreaOverlayElementFactory = new ColoredTextAreaOverlayElementFactory(); OverlayManager::getSingleton().addOverlayElementFactory(pColoredTextAreaOverlayElementFactory);
Deinitialisation code
// This must be called before the ogre root is destroyed or crashes may occur on exit delete pColoredTextAreaOverlayElementFactory
Having done this you should also be able to use this class in your overlay scripts (although I haven't tested that).
Similarly to the TextAreaOverlayElement class, a gradient can be applied from the top to the bottom. Instead of specifying an absolute color, a value can be specified instead with the following functions:
void setValueBottom(float Value); void setValueTop(float Value);
This is essentially the value component of the HSV color.
Here is a screenshot of the class in use (see text at the bottom left):
Important
Keep in mind that this is not thread safe! We had much trouble to figure out why it was broken: "e used updateColours() from a different thread, never do that!
Code
ColoredTextAreaOverlayElement.h
#pragma once class ColoredTextAreaOverlayElement : public TextAreaOverlayElement { public: ColoredTextAreaOverlayElement(const String& name); ~ColoredTextAreaOverlayElement(void); void setValueBottom(float Value); void setValueTop(float Value); void setCaption(const DisplayString& text); static DisplayString StripColors(const DisplayString& text); static ColourValue GetColor(unsigned char ID, float Value = 1.0f); protected: void updateColours(void); vector<unsigned char> m_Colors; float m_ValueTop; float m_ValueBottom; };
ColoredTextAreaOverlayElement.cpp
#include "ColoredTextAreaOverlayElement.h" #define POS_TEX_BINDING 0 #define COLOUR_BINDING 1 ColoredTextAreaOverlayElement::ColoredTextAreaOverlayElement(const String& name) : TextAreaOverlayElement(name) , m_ValueTop(1.0f) , m_ValueBottom(0.8f) { } ColoredTextAreaOverlayElement::~ColoredTextAreaOverlayElement(void) { } void ColoredTextAreaOverlayElement::setValueBottom(float Value) { m_ValueTop = Value; mColoursChanged = true; } void ColoredTextAreaOverlayElement::setValueTop(float Value) { m_ValueBottom = Value; mColoursChanged = true; } ColourValue ColoredTextAreaOverlayElement::GetColor(unsigned char ID, float Value) { switch (ID) { case 0: return ColourValue(0, 0, 0); // Black case 1: return ColourValue(Value, 0, 0); // Red case 2: return ColourValue(0, Value, 0); // Green case 3: return ColourValue(Value, Value, 0); // Yellow case 4: return ColourValue(0, 0, Value); // Blue case 5: return ColourValue(0, Value, Value); // Cyan case 6: return ColourValue(Value, 0, Value); // Magenta case 7: return ColourValue(Value, Value, Value); // White } return ColourValue::Black; } DisplayString ColoredTextAreaOverlayElement::StripColors(const DisplayString& text) { DisplayString StrippedText; int i; for (i = 0; i < (int)text.size()-1; ++i) { if (text[i] == '^' && text[i+1] >= '0' && text[i+1] <= '7') // This is a color code, ignore it { ++i; } else { StrippedText.append(1, text[i]); } } // One last character to add because loop went to size()-1 if (i < (int)text.size()) StrippedText.append(1, text[i]); return StrippedText; } void ColoredTextAreaOverlayElement::setCaption(const DisplayString& text) { m_Colors.resize(text.size(), 7); int i, iNumColorCodes = 0, iNumSpaces = 0; for (i = 0; i < (int)text.size()-1; ++i) { if (text[i] == ' ' || text[i] == '\n') { // Spaces and newlines are skipped when rendering and as such can't have a color ++iNumSpaces; } else if (text[i] == '^' && text[i+1] >= '0' && text[i+1] <= '7') // This is a color code { // Fill the color array starting from this point to the end with the new color code // adjustments need to made because color codes will be removed and spaces are not counted fill(m_Colors.begin()+i-(2*iNumColorCodes)-iNumSpaces, m_Colors.end(), text[i+1]-'0'); ++i; ++iNumColorCodes; } } // Set the caption using the base class, but strip the color codes from it first TextAreaOverlayElement::setCaption(StripColors(text)); } void ColoredTextAreaOverlayElement::updateColours(void) { // Convert to system-specific RGBA topColour, bottomColour; // Set default to white Root::getSingleton().convertColourValue(ColourValue::White, &topColour); Root::getSingleton().convertColourValue(ColourValue::White, &bottomColour); HardwareVertexBufferSharedPtr vbuf = mRenderOp.vertexData->vertexBufferBinding->getBuffer(COLOUR_BINDING); RGBA* pDest = static_cast<RGBA*>( vbuf->lock(HardwareBuffer::HBL_DISCARD) ); for (size_t i = 0; i < mAllocSize; ++i) { if (i < m_Colors.size()) { Root::getSingleton().convertColourValue(GetColor(m_Colors[i], m_ValueTop), &topColour); Root::getSingleton().convertColourValue(GetColor(m_Colors[i], m_ValueBottom), &bottomColour); } // First tri (top, bottom, top) *pDest++ = topColour; *pDest++ = bottomColour; *pDest++ = topColour; // Second tri (top, bottom, bottom) *pDest++ = topColour; *pDest++ = bottomColour; *pDest++ = bottomColour; } vbuf->unlock(); }
ColoredTextAreaOverlayElementFactory.h
#pragma once #include <OgreOverlayElementFactory.h> #include "ColoredTextAreaOverlayElement.h" /** Factory for creating TextAreaOverlayElement instances. */ class ColoredTextAreaOverlayElementFactory : public OverlayElementFactory { public: /** See OverlayElementFactory */ OverlayElement* createOverlayElement(const String& instanceName) { return new ColoredTextAreaOverlayElement(instanceName); } /** See OverlayElementFactory */ const String& getTypeName() const { static String name = "ColoredTextArea"; return name; } };
Updated version
A slightly updated version can be found there:
- http://rigsofrods.svn.sourceforge.net/viewvc/rigsofrods/trunk/build/main/source/ColoredTextAreaOverlayElement.cpp
- http://rigsofrods.svn.sourceforge.net/viewvc/rigsofrods/trunk/build/main/source/ColoredTextAreaOverlayElement.h
- http://rigsofrods.svn.sourceforge.net/viewvc/rigsofrods/trunk/build/main/source/ColoredTextAreaOverlayElementFactory.h