RigsofRods
Soft-body Physics Simulation
MovableText.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2005-2012 Pierre-Michel Ricordel
4  Copyright 2007-2012 Thomas Fischer
5 
6  For more information, see http://www.rigsofrods.org/
7 
8  Rigs of Rods is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License version 3, as
10  published by the Free Software Foundation.
11 
12  Rigs of Rods is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
25 
26 #include "MovableText.h"
27 
28 #include <Ogre.h>
29 #include <Overlay/OgreFontManager.h>
30 
31 using namespace Ogre;
32 using namespace RoR;
33 
34 #define POS_TEX_BINDING 0
35 #define COLOUR_BINDING 1
36 
37 MovableText::MovableText(const UTFString& name, const UTFString& caption, const UTFString& fontName, Real charHeight, const ColourValue& color)
38  : mpCam(NULL)
39  , mpWin(NULL)
40  , mpFont(NULL)
41  , mName(name)
42  , mCaption(caption)
43  , mFontName(fontName)
44  , mCharHeight(charHeight)
45  , mColor(color)
46  , mType("MovableText")
47  , mTimeUntilNextToggle(0)
48  , mSpaceWidth(0.2f)
49  , mUpdateColors(true)
50  , mOnTop(false)
51  , mHorizontalAlignment(H_LEFT)
52  , mVerticalAlignment(V_BELOW)
53  , mAdditionalHeight(0.0)
54 {
55  if (name == "")
56  throw Exception(Exception::ERR_INVALIDPARAMS, "Trying to create MovableText without name", "MovableText::MovableText");
57 
58  if (caption == "")
59  // throw Exception(Exception::ERR_INVALIDPARAMS, "Trying to create MovableText without caption", "MovableText::MovableText");
60  mCaption = ".";
61 
62  mRenderOp.vertexData = NULL;
63  this->setFontName(mFontName);
64  this->_setupGeometry();
65 }
66 
68 {
69  if (mRenderOp.vertexData)
70  delete mRenderOp.vertexData;
71 }
72 
73 void MovableText::setFontName(const UTFString& fontName)
74 {
75  if ((Ogre::MaterialManager::getSingletonPtr()->resourceExists(mName + "Material")))
76  {
77  Ogre::MaterialManager::getSingleton().remove(mName + "Material");
78  }
79 
80  if (mFontName != fontName || mpMaterial.isNull() || !mpFont)
81  {
82  mFontName = fontName;
83  mpFont = (Ogre::Font *)FontManager::getSingleton().getResourceByName(mFontName).getPointer();
84 
85  if (!mpFont)
86  throw Exception(Exception::ERR_ITEM_NOT_FOUND, "Could not find font " + fontName, "MovableText::setFontName");
87 
88  mpFont->load();
89  if (!mpMaterial.isNull())
90  {
91  MaterialManager::getSingletonPtr()->remove(mpMaterial->getName());
92  mpMaterial.setNull();
93  }
94 
95  mpMaterial = mpFont->getMaterial()->clone(mName + "Material");
96  if (!mpMaterial->isLoaded())
97  mpMaterial->load();
98 
99  mpMaterial->setDepthCheckEnabled(!mOnTop);
100  mpMaterial->setDepthBias(1.0, 1.0);
101  mpMaterial->setFog(true);
102  mpMaterial->setDepthWriteEnabled(mOnTop);
103  mpMaterial->setLightingEnabled(false);
104  mNeedUpdate = true;
105  }
106 }
107 
108 void MovableText::setCaption(const UTFString& caption)
109 {
110  if (caption != mCaption)
111  {
112  mCaption = caption;
113  mNeedUpdate = true;
114  }
115 }
116 
117 void MovableText::setColor(const ColourValue& color)
118 {
119  if (color != mColor)
120  {
121  mColor = color;
122  mUpdateColors = true;
123  }
124 }
125 
127 {
128  if (fabs(height - mCharHeight) > 0.00001f)
129  {
130  mCharHeight = height;
131  mNeedUpdate = true;
132  }
133 }
134 
136 {
137  if (fabs(width - mSpaceWidth) > 0.00001f)
138  {
139  mSpaceWidth = width;
140  mNeedUpdate = true;
141  }
142 }
143 
144 void MovableText::setTextAlignment(const HorizontalAlignment& horizontalAlignment, const VerticalAlignment& verticalAlignment)
145 {
146  if (mHorizontalAlignment != horizontalAlignment)
147  {
148  mHorizontalAlignment = horizontalAlignment;
149  mNeedUpdate = true;
150  }
151  if (mVerticalAlignment != verticalAlignment)
152  {
153  mVerticalAlignment = verticalAlignment;
154  mNeedUpdate = true;
155  }
156 }
157 
159 {
160  if (fabs(mAdditionalHeight - height) > 0.00001f)
161  {
162  mAdditionalHeight = height;
163  mNeedUpdate = true;
164  }
165 }
166 
167 void MovableText::showOnTop(bool show)
168 {
169  if (mOnTop != show && !mpMaterial.isNull())
170  {
171  mOnTop = show;
172  mpMaterial->setDepthBias(1.0, 1.0);
173  mpMaterial->setDepthCheckEnabled(!mOnTop);
174  mpMaterial->setDepthWriteEnabled(mOnTop);
175  }
176 }
177 
179 {
181  ROR_ASSERT(!mpMaterial.isNull());
182 
183  uint vertexCount = static_cast<uint>(mCaption.size() * 6);
184 
185  if (mRenderOp.vertexData)
186  {
187  // Removed this test as it causes problems when replacing a caption
188  // of the same size: replacing "Hello" with "hello"
189  // as well as when changing the text alignment
190  //if (mRenderOp.vertexData->vertexCount != vertexCount)
191  {
192  delete mRenderOp.vertexData;
193  mRenderOp.vertexData = NULL;
194  mUpdateColors = true;
195  }
196  }
197 
198  if (!mRenderOp.vertexData)
199  mRenderOp.vertexData = new VertexData();
200 
201  mRenderOp.indexData = 0;
202  mRenderOp.vertexData->vertexStart = 0;
203  mRenderOp.vertexData->vertexCount = vertexCount;
204  mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST;
205  mRenderOp.useIndexes = false;
206 
207  VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration;
208  VertexBufferBinding* bind = mRenderOp.vertexData->vertexBufferBinding;
209  size_t offset = 0;
210 
211  // create/bind positions/tex.ccord. buffer
212  if (!decl->findElementBySemantic(VES_POSITION))
213  decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT3, VES_POSITION);
214 
215  offset += VertexElement::getTypeSize(VET_FLOAT3);
216 
217  if (!decl->findElementBySemantic(VES_TEXTURE_COORDINATES))
218  decl->addElement(POS_TEX_BINDING, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);
219 
220  HardwareVertexBufferSharedPtr ptbuf = HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(POS_TEX_BINDING),
221  mRenderOp.vertexData->vertexCount,
222  HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
223  bind->setBinding(POS_TEX_BINDING, ptbuf);
224 
225  // Colours - store these in a separate buffer because they change less often
226  if (!decl->findElementBySemantic(VES_DIFFUSE))
227  decl->addElement(COLOUR_BINDING, 0, VET_COLOUR, VES_DIFFUSE);
228 
229  HardwareVertexBufferSharedPtr cbuf = HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(COLOUR_BINDING),
230  mRenderOp.vertexData->vertexCount,
231  HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
232  bind->setBinding(COLOUR_BINDING, cbuf);
233 
234  //Real *pPCBuff = static_cast<Real*>(ptbuf->lock(HardwareBuffer::HBL_NORMAL));
235  Real* pPCBuff = (Real*)malloc(ptbuf->getSizeInBytes());
236  Real* oPCBuff = pPCBuff;
237 
238  float largestWidth = 0;
239  float left = 0 * 2.0 - 1.0;
240  float top = -((0 * 2.0) - 1.0);
241 
242  // Derive space width from a capital A
243  if (fabs(mSpaceWidth) < 0.00001f)
244  mSpaceWidth = mpFont->getGlyphAspectRatio('A') * mCharHeight * 2.0;
245 
246  // for calculation of AABB
247  Ogre::Vector3 min = Ogre::Vector3::ZERO, max = Ogre::Vector3::ZERO, currPos = Ogre::Vector3::ZERO;
248  Ogre::Real maxSquaredRadius = 0.0f;
249  bool first = true;
250 
251  // Use iterator
252  UTFString::iterator i, iend;
253  iend = mCaption.end();
254  bool newLine = true;
255  Real len = 0.0f;
256 
258  {
259  // Raise the first line of the caption
260  top += mCharHeight;
261  for (i = mCaption.begin(); i != iend; ++i)
262  {
263  if (*i == '\n')
264  top += mCharHeight * 2.0;
265  }
266  }
267 
268  for (i = mCaption.begin(); i != iend; ++i)
269  {
270  if (newLine)
271  {
272  len = 0.0f;
273  for (UTFString::iterator j = i; j != iend && *j != '\n'; j++)
274  {
275  if (*j == ' ')
276  len += mSpaceWidth;
277  else
278  len += mpFont->getGlyphAspectRatio(*j) * mCharHeight * 2.0;
279  }
280  newLine = false;
281  }
282 
283  if (*i == '\n')
284  {
285  left = 0 * 2.0 - 1.0;
286  top -= mCharHeight * 2.0;
287  newLine = true;
288  continue;
289  }
290 
291  if (*i == ' ')
292  {
293  // Just leave a gap, no tris
294  left += mSpaceWidth;
295  // Also reduce tri count
296  mRenderOp.vertexData->vertexCount -= 6;
297  continue;
298  }
299 
300  Real horiz_height = mpFont->getGlyphAspectRatio(*i);
301  Real u1, u2, v1, v2;
302  Ogre::Font::UVRect utmp;
303  utmp = mpFont->getGlyphTexCoords(*i);
304  u1 = utmp.left;
305  u2 = utmp.right;
306  v1 = utmp.top;
307  v2 = utmp.bottom;
308 
309  // each vert is (x, y, z, u, v)
310  //-------------------------------------------------------------------------------------
311  // First tri
312  //
313  // Upper left
315  *pPCBuff++ = left;
316  else
317  *pPCBuff++ = left - (len / 2);
318  *pPCBuff++ = top;
319  *pPCBuff++ = -1.0;
320  *pPCBuff++ = u1;
321  *pPCBuff++ = v1;
322 
323  // Deal with bounds
325  currPos = Ogre::Vector3(left, top, -1.0);
326  else
327  currPos = Ogre::Vector3(left - (len / 2), top, -1.0);
328  if (first)
329  {
330  min = max = currPos;
331  maxSquaredRadius = currPos.squaredLength();
332  first = false;
333  }
334  else
335  {
336  min.makeFloor(currPos);
337  max.makeCeil(currPos);
338  maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
339  }
340 
341  top -= mCharHeight * 2.0;
342 
343  // Bottom left
345  *pPCBuff++ = left;
346  else
347  *pPCBuff++ = left - (len / 2);
348  *pPCBuff++ = top;
349  *pPCBuff++ = -1.0;
350  *pPCBuff++ = u1;
351  *pPCBuff++ = v2;
352 
353  // Deal with bounds
355  currPos = Ogre::Vector3(left, top, -1.0);
356  else
357  currPos = Ogre::Vector3(left - (len / 2), top, -1.0);
358  min.makeFloor(currPos);
359  max.makeCeil(currPos);
360  maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
361 
362  top += mCharHeight * 2.0;
363  left += horiz_height * mCharHeight * 2.0;
364 
365  // Top right
367  *pPCBuff++ = left;
368  else
369  *pPCBuff++ = left - (len / 2);
370  *pPCBuff++ = top;
371  *pPCBuff++ = -1.0;
372  *pPCBuff++ = u2;
373  *pPCBuff++ = v1;
374  //-------------------------------------------------------------------------------------
375 
376  // Deal with bounds
378  currPos = Ogre::Vector3(left, top, -1.0);
379  else
380  currPos = Ogre::Vector3(left - (len / 2), top, -1.0);
381  min.makeFloor(currPos);
382  max.makeCeil(currPos);
383  maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
384 
385  //-------------------------------------------------------------------------------------
386  // Second tri
387  //
388  // Top right (again)
390  *pPCBuff++ = left;
391  else
392  *pPCBuff++ = left - (len / 2);
393  *pPCBuff++ = top;
394  *pPCBuff++ = -1.0;
395  *pPCBuff++ = u2;
396  *pPCBuff++ = v1;
397 
398  currPos = Ogre::Vector3(left, top, -1.0);
399  min.makeFloor(currPos);
400  max.makeCeil(currPos);
401  maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
402 
403  top -= mCharHeight * 2.0;
404  left -= horiz_height * mCharHeight * 2.0;
405 
406  // Bottom left (again)
408  *pPCBuff++ = left;
409  else
410  *pPCBuff++ = left - (len / 2);
411  *pPCBuff++ = top;
412  *pPCBuff++ = -1.0;
413  *pPCBuff++ = u1;
414  *pPCBuff++ = v2;
415 
416  currPos = Ogre::Vector3(left, top, -1.0);
417  min.makeFloor(currPos);
418  max.makeCeil(currPos);
419  maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
420 
421  left += horiz_height * mCharHeight * 2.0;
422 
423  // Bottom right
425  *pPCBuff++ = left;
426  else
427  *pPCBuff++ = left - (len / 2);
428  *pPCBuff++ = top;
429  *pPCBuff++ = -1.0;
430  *pPCBuff++ = u2;
431  *pPCBuff++ = v2;
432  //-------------------------------------------------------------------------------------
433 
434  currPos = Ogre::Vector3(left, top, -1.0);
435  min.makeFloor(currPos);
436  max.makeCeil(currPos);
437  maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
438 
439  // Go back up with top
440  top += mCharHeight * 2.0;
441 
442  float currentWidth = (left + 1) / 2 - 0;
443  if (currentWidth > largestWidth)
444  largestWidth = currentWidth;
445  }
446 
447  // Unlock vertex buffer
448  //ptbuf->unlock();
449  ptbuf->writeData(0, ptbuf->getSizeInBytes(), oPCBuff, true);
450  free(oPCBuff);
451 
452  // update AABB/Sphere radius
453  mAABB = Ogre::AxisAlignedBox(min, max);
454  mRadius = Ogre::Math::Sqrt(maxSquaredRadius);
455 
456  if (mUpdateColors)
457  this->_updateColors();
458 
459  mNeedUpdate = false;
460 }
461 
463 {
465  ROR_ASSERT(!mpMaterial.isNull());
466 
467  // Convert to system-specific
468  RGBA color;
469  Root::getSingleton().convertColourValue(mColor, &color);
470  HardwareVertexBufferSharedPtr vbuf = mRenderOp.vertexData->vertexBufferBinding->getBuffer(COLOUR_BINDING);
471  //RGBA *pDest = static_cast<RGBA*>(vbuf->lock(HardwareBuffer::HBL_NORMAL));
472  RGBA* pDest = (RGBA*)malloc(vbuf->getSizeInBytes());
473  RGBA* oDest = pDest;
474  for (uint i = 0; i < mRenderOp.vertexData->vertexCount; ++i)
475  *pDest++ = color;
476  //vbuf->unlock();
477  vbuf->writeData(0, vbuf->getSizeInBytes(), oDest, true);
478  free(oDest);
479  mUpdateColors = false;
480 }
481 
482 const Quaternion& MovableText::getWorldOrientation(void) const
483 {
484  ROR_ASSERT(mpCam);
485  return const_cast<Quaternion&>(mpCam->getDerivedOrientation());
486 }
487 
488 const Vector3& MovableText::getWorldPosition(void) const
489 {
490  ROR_ASSERT(mParentNode);
491  return mParentNode->_getDerivedPosition();
492 }
493 
494 void MovableText::getWorldTransforms(Matrix4* xform) const
495 {
496  if (this->isVisible() && mpCam)
497  {
498  Matrix3 rot3x3, scale3x3 = Matrix3::IDENTITY;
499 
500  // store rotation in a matrix
501  mpCam->getDerivedOrientation().ToRotationMatrix(rot3x3);
502 
503  // parent node position
504  Vector3 ppos = mParentNode->_getDerivedPosition() + Vector3::UNIT_Y * mAdditionalHeight;
505 
506  // apply scale
507  scale3x3[0][0] = mParentNode->_getDerivedScale().x / 2;
508  scale3x3[1][1] = mParentNode->_getDerivedScale().y / 2;
509  scale3x3[2][2] = mParentNode->_getDerivedScale().z / 2;
510 
511  // apply all transforms to xform
512  *xform = (rot3x3 * scale3x3);
513  xform->setTrans(ppos);
514  }
515 }
516 
517 void MovableText::getRenderOperation(RenderOperation& op)
518 {
519  if (this->isVisible())
520  {
521  if (mNeedUpdate)
522  this->_setupGeometry();
523  if (mUpdateColors)
524  this->_updateColors();
525  op = mRenderOp;
526  }
527 }
528 
530 {
531  mpCam = cam;
532 }
533 
534 void MovableText::_updateRenderQueue(RenderQueue* queue)
535 {
536  if (this->isVisible())
537  {
538  if (mNeedUpdate)
539  this->_setupGeometry();
540  if (mUpdateColors)
541  this->_updateColors();
542 
543  queue->addRenderable(this, mRenderQueueID, OGRE_RENDERABLE_DEFAULT_PRIORITY);
544  // queue->addRenderable(this, mRenderQueueID, RENDER_QUEUE_SKIES_LATE);
545  }
546 }
RoR::MovableText::setTextAlignment
void setTextAlignment(const HorizontalAlignment &horizontalAlignment, const VerticalAlignment &verticalAlignment)
Definition: MovableText.cpp:144
COLOUR_BINDING
#define COLOUR_BINDING
Definition: MovableText.cpp:35
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
RoR::MovableText::setColor
void setColor(const Ogre::ColourValue &color)
Definition: MovableText.cpp:117
RoR::MovableText::setCaption
void setCaption(const Ogre::UTFString &caption)
Definition: MovableText.cpp:108
RoR::MovableText::mColor
Ogre::ColourValue mColor
Definition: MovableText.h:54
RoR::MovableText::mSpaceWidth
Ogre::Real mSpaceWidth
Definition: MovableText.h:60
POS_TEX_BINDING
#define POS_TEX_BINDING
Definition: MovableText.cpp:34
RoR::MovableText::showOnTop
void showOnTop(bool show=true)
Definition: MovableText.cpp:167
RoR::MovableText::setSpaceWidth
void setSpaceWidth(Ogre::Real width)
Definition: MovableText.cpp:135
RoR::MovableText::mVerticalAlignment
VerticalAlignment mVerticalAlignment
Definition: MovableText.h:52
RoR::MovableText::mpCam
Ogre::Camera * mpCam
Definition: MovableText.h:70
RoR::MovableText::setFontName
void setFontName(const Ogre::UTFString &fontName)
Definition: MovableText.cpp:73
RoR::MovableText::getWorldPosition
const Ogre::Vector3 & getWorldPosition(void) const
Definition: MovableText.cpp:488
MovableText.h
This creates a billboarding object that displays a text.
RoR::MovableText::mCharHeight
Ogre::Real mCharHeight
Definition: MovableText.h:59
RoR::MovableText::mFontName
Ogre::UTFString mFontName
Definition: MovableText.h:47
RoR::MovableText::mUpdateColors
bool mUpdateColors
Definition: MovableText.h:63
RoR::MovableText::getRenderOperation
void getRenderOperation(Ogre::RenderOperation &op)
Definition: MovableText.cpp:517
RoR::MovableText::getWorldOrientation
const Ogre::Quaternion & getWorldOrientation(void) const
Definition: MovableText.cpp:482
RoR::MovableText::mAABB
Ogre::AxisAlignedBox mAABB
Definition: MovableText.h:56
RoR::MovableText::mpMaterial
Ogre::MaterialPtr mpMaterial
Definition: MovableText.h:73
RoR::MovableText::getWorldTransforms
void getWorldTransforms(Ogre::Matrix4 *xform) const
Definition: MovableText.cpp:494
RoR::MovableText::mOnTop
bool mOnTop
Definition: MovableText.h:64
RoR::MovableText::_setupGeometry
void _setupGeometry()
Definition: MovableText.cpp:178
RoR::MovableText::mCaption
Ogre::UTFString mCaption
Definition: MovableText.h:50
RoR::MovableText::mRenderOp
Ogre::RenderOperation mRenderOp
Definition: MovableText.h:55
RoR::MovableText::VerticalAlignment
VerticalAlignment
Definition: MovableText.h:44
RoR::MovableText::mName
Ogre::String mName
Definition: MovableText.h:49
RoR::MovableText::mpFont
Ogre::Font * mpFont
Definition: MovableText.h:72
RoR::MovableText::H_LEFT
@ H_LEFT
Definition: MovableText.h:43
RoR::MovableText::setCharacterHeight
void setCharacterHeight(Ogre::Real height)
Definition: MovableText.cpp:126
RoR::MovableText::_updateColors
void _updateColors()
Definition: MovableText.cpp:462
RoR::MovableText::HorizontalAlignment
HorizontalAlignment
Definition: MovableText.h:43
RoR::MovableText::mNeedUpdate
bool mNeedUpdate
Definition: MovableText.h:62
RoR::MovableText::~MovableText
virtual ~MovableText()
Definition: MovableText.cpp:67
RoR::MovableText::_updateRenderQueue
void _updateRenderQueue(Ogre::RenderQueue *queue)
Definition: MovableText.cpp:534
RoR::MovableText::mHorizontalAlignment
HorizontalAlignment mHorizontalAlignment
Definition: MovableText.h:51
RoR::MovableText::_notifyCurrentCamera
void _notifyCurrentCamera(Ogre::Camera *cam)
Definition: MovableText.cpp:529
RoR::MovableText::setAdditionalHeight
void setAdditionalHeight(Ogre::Real height)
Definition: MovableText.cpp:158
RoR::MovableText::mRadius
Ogre::Real mRadius
Definition: MovableText.h:67
Ogre
Definition: ExtinguishableFireAffector.cpp:35
RoR
Definition: AppContext.h:36
RoR::MovableText::mAdditionalHeight
Ogre::Real mAdditionalHeight
Definition: MovableText.h:68
RoR::MovableText::V_ABOVE
@ V_ABOVE
Definition: MovableText.h:44