Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
Text.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include <vlGraphics/Text.hpp>
34 #include <vlGraphics/Actor.hpp>
35 #include <vlCore/Log.hpp>
36 
37 #include "ft2build.h"
38 #include FT_FREETYPE_H
39 
40 using namespace vl;
41 
42 //-----------------------------------------------------------------------------
43 void Text::render_Implementation(const Actor* actor, const Shader*, const Camera* camera, OpenGLContext* gl_context) const
44 {
45  gl_context->bindVAS(NULL, false, false);
46 
47  VL_CHECK(font())
48 
49  if (!font() || !font()->mFT_Face)
50  return;
51 
52  if ( text().empty() )
53  return;
54 
55  // Lighting can be enabled or disabled.
56  // glDisable(GL_LIGHTING);
57 
58  // Blending must be enabled explicity by the vl::Shader, also to perform z-sort.
59  // glEnable(GL_BLEND);
60 
61  // Trucchetto che usiamo per evitare z-fighting:
62  // Pass #1 - fill color and stencil
63  // - disable depth write mask
64  // - depth test can be enabled or not by the user
65  // - depth func can be choosen by the user
66  // - render in the order: background, border, shadow, outline, text
67  // Pass #2 - fill z-buffer
68  // - enable depth write mask
69  // - disable color mask
70  // - disable stencil
71  // - drawing background and border
72 
73  // Pass #1
74 
75  // disable z-writing
76  GLboolean depth_mask=0;
77  glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask);
78  glDepthMask(GL_FALSE);
79 
80  // background
81  if (backgroundEnabled())
82  renderBackground( actor, camera );
83 
84  // border
85  if (borderEnabled())
86  renderBorder( actor, camera );
87 
88  // to have the most correct results we should render the text twice one for color and stencil, the other for the z-buffer
89 
90  // shadow render
91  if (shadowEnabled())
92  renderText( actor, camera, shadowColor(), shadowVector() );
93  // outline render
94  if (outlineEnabled())
95  {
96  renderText( actor, camera, outlineColor(), fvec2(-1,0) );
97  renderText( actor, camera, outlineColor(), fvec2(+1,0) );
98  renderText( actor, camera, outlineColor(), fvec2(0,-1) );
99  renderText( actor, camera, outlineColor(), fvec2(0,+1) );
100  }
101  // text render
102  renderText( actor, camera, color(), fvec2(0,0) );
103 
104  // Pass #2
105  // fills the z-buffer (not the stencil buffer): approximated to the text bbox
106 
107  // restores depth mask
108  glDepthMask(depth_mask);
109 
110  if (depth_mask)
111  {
112  // disables writing to the color buffer
113  GLboolean color_mask[4];
114  glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
115  glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
116 
117  // disable writing to the stencil buffer
118  int stencil_front_mask=0;
119  glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_front_mask);
120  int stencil_back_mask=0;
121  if (Has_GL_Version_2_0)
122  glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &stencil_back_mask);
123  glStencilMask(0);
124 
125  // background
126  renderBackground( actor, camera );
127 
128  // border
129  renderBorder( actor, camera );
130 
131  // restores color writing
132  glColorMask(color_mask[0],color_mask[1],color_mask[2],color_mask[3]);
133 
134  // restore the stencil masks
135  glStencilMask(stencil_front_mask);
136  if (Has_GL_Version_2_0)
137  glStencilMaskSeparate(GL_BACK, stencil_back_mask);
138  }
139 
140  // restore the right color and normal since we changed them
141  glColor4fv( gl_context->color().ptr() );
142  glNormal3fv( gl_context->normal().ptr() );
143 }
144 //-----------------------------------------------------------------------------
145 void Text::renderText(const Actor* actor, const Camera* camera, const fvec4& color, const fvec2& offset) const
146 {
147  if(!mFont)
148  {
149  Log::error("Text::renderText() error: no Font assigned to the Text object.\n");
150  VL_TRAP()
151  return;
152  }
153 
154  if (!font()->mFT_Face)
155  {
156  Log::error("Text::renderText() error: invalid FT_Face: probably you tried to load an unsupported font format.\n");
157  VL_TRAP()
158  return;
159  }
160 
161  int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() };
162 
163  if (viewport[2] < 1) viewport[2] = 1;
164  if (viewport[3] < 1) viewport[3] = 1;
165 
166  // note that we only save and restore the server side states
167 
168  if (mode() == Text2D)
169  {
170  glMatrixMode(GL_MODELVIEW);
171  glPushMatrix();
172  glLoadIdentity();
173  VL_CHECK_OGL();
174 
175  glMatrixMode(GL_PROJECTION);
176  glPushMatrix();
177  // glLoadIdentity();
178  // gluOrtho2D( -0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f );
179 
180  // clever trick part #1
181  fmat4 mat = fmat4::getOrtho(-0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f, -1, +1);
182  mat.e(2,2) = 1.0f; // preserve the z value from the incoming vertex.
183  mat.e(2,3) = 0.0f;
184  glLoadMatrixf(mat.ptr());
185 
186  VL_CHECK_OGL();
187  }
188 
189  AABB rbbox = rawboundingRect( text() ); // for text alignment
190  VL_CHECK(rbbox.maxCorner().z() == 0)
191  VL_CHECK(rbbox.minCorner().z() == 0)
192  AABB bbox = rbbox;
193  int applied_margin = backgroundEnabled() || borderEnabled() ? margin() : 0;
194  bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*applied_margin,2.0f*applied_margin,0) );
195  VL_CHECK(bbox.maxCorner().z() == 0)
196  VL_CHECK(bbox.minCorner().z() == 0)
197 
198  // basic render states
199 
200  fvec2 pen(0,0);
201 
202  float texc[] = { 0,0, 0,0, 0,0, 0,0 };
203  VL_glActiveTexture( GL_TEXTURE0 );
204  glEnable(GL_TEXTURE_2D);
205  VL_glClientActiveTexture( GL_TEXTURE0 );
206  glEnableClientState( GL_TEXTURE_COORD_ARRAY );
207  glTexCoordPointer(2, GL_FLOAT, 0, texc);
208 
209  // Constant color
210  glColor4f( color.r(), color.g(), color.b(), color.a() );
211 
212  // Constant normal
213  glNormal3f( 0, 0, 1 );
214 
215  fvec3 vect[4];
216  glEnableClientState( GL_VERTEX_ARRAY );
217  glVertexPointer(3, GL_FLOAT, 0, vect[0].ptr());
218 
219  FT_Long has_kerning = FT_HAS_KERNING( font()->mFT_Face );
220  FT_UInt previous = 0;
221 
222  // viewport alignment
223  fmat4 m = mMatrix;
224 
225  int w = camera->viewport()->width();
226  int h = camera->viewport()->height();
227 
228  if (w < 1) w = 1;
229  if (h < 1) h = 1;
230 
231  if ( !(actor && actor->transform()) && mode() == Text2D )
232  {
234  {
237  // vect[i].x() += int((viewport[2]-1.0f) / 2.0f);
238  m.translate( (float)int((w-1.0f) / 2.0f), 0, 0);
239  }
240 
242  {
245  // vect[i].x() += int(viewport[2]-1.0f);
246  m.translate( (float)int(w-1.0f), 0, 0);
247  }
248 
249  if (viewportAlignment() & AlignTop)
250  {
253  // vect[i].y() += int(viewport[3]-1.0f);
254  m.translate( 0, (float)int(h-1.0f), 0);
255  }
256 
258  {
261  // vect[i].y() += int((viewport[3]-1.0f) / 2.0f);
262  m.translate( 0, (float)int((h-1.0f) / 2.0f), 0);
263  }
264  }
265 
266  // split the text in different lines
267 
268  VL_CHECK(text().length())
269 
270  std::vector< String > lines;
271  lines.push_back( String() );
272  for(int i=0; i<text().length(); ++i)
273  {
274  if (text()[i] == '\n')
275  {
276  // start new line
277  lines.push_back( String() );
278  }
279  else
280  lines.back() += text()[i];
281  }
282 
283  for(unsigned iline=0; iline<lines.size(); iline++)
284  {
285  // strip spaces at the beginning and at the end of the line
287  lines[iline].trim();
288 
289  AABB linebox = rawboundingRect( lines[iline] );
290  int displace = 0;
291  int just_space = 0;
292  int just_remained_space = 0;
293  int space_count = 0;
294  for(int c=0; c<(int)lines[iline].length(); c++)
295  if ( lines[iline][c] == ' ' )
296  space_count++;
297 
298  if (space_count && textAlignment() == TextAlignJustify)
299  {
300  just_space = int(rbbox.width() - linebox.width()) / space_count;
301  just_remained_space = int(rbbox.width() - linebox.width()) % space_count;
302  }
303 
304  if (layout() == RightToLeftText)
305  {
306  if (textAlignment() == TextAlignRight)
307  displace = 0;
308  else
309  if (textAlignment() == TextAlignLeft)
310  displace = - int(rbbox.width() - linebox.width());
311  else
313  displace = - int((rbbox.width() - linebox.width()) / 2.0f);
314  }
315  if (layout() == LeftToRightText)
316  {
317  if (textAlignment() == TextAlignRight)
318  displace = int(rbbox.width() - linebox.width());
319  else
320  if (textAlignment() == TextAlignLeft)
321  displace = 0;
322  else
324  displace = + int((rbbox.width() - linebox.width()) / 2.0f);
325  }
326 
327  // this is needed so that empty strings generate empty lines
328  // note that puttig '\n\n\n\n' at the beginning of a text generates
329  // a wrong rendering (see it with background box activated).
330  if (iline != 0 && !lines[iline].length())
331  {
332  pen.y() -= mFont->mHeight;
333  pen.x() = 0;
334  }
335  else
336  for(int c=0; c<(int)lines[iline].length(); c++)
337  {
338  if (c == 0 && iline != 0)
339  {
340  pen.y() -= mFont->mHeight;
341  pen.x() = 0;
342  }
343 
344  const Glyph* glyph = mFont->glyph( lines[iline][c] );
345 
346  if (!glyph)
347  continue;
348 
349  if ( kerningEnabled() && has_kerning && previous && glyph->glyphIndex() )
350  {
351  FT_Vector delta; delta.y = 0;
352  if (layout() == LeftToRightText)
353  {
354  FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta );
355  pen.x() += delta.x / 64.0f;
356  }
357  else
358  if (layout() == RightToLeftText)
359  {
360  FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta );
361  pen.x() -= delta.x / 64.0f;
362  }
363  pen.y() += delta.y / 64.0f;
364  }
365  previous = glyph->glyphIndex();
366 
367  if (glyph->textureHandle())
368  {
369  glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() );
370 
371  texc[0] = glyph->s0();
372  texc[1] = glyph->t1();
373 
374  texc[2] = glyph->s1();
375  texc[3] = glyph->t1();
376 
377  texc[4] = glyph->s1();
378  texc[5] = glyph->t0();
379 
380  texc[6] = glyph->s0();
381  texc[7] = glyph->t0();
382 
383  int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left();
384 
385  // triangle strip layout
386 
387  vect[0].x() = pen.x() + glyph->width()*0 + left -1;
388  vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
389 
390  vect[1].x() = pen.x() + glyph->width()*1 + left +1;
391  vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
392 
393  vect[2].x() = pen.x() + glyph->width()*1 + left +1;
394  vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
395 
396  vect[3].x() = pen.x() + glyph->width()*0 + left -1;
397  vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
398 
399  if (layout() == RightToLeftText)
400  {
401  #if (1)
402  vect[0].x() -= glyph->width()-1 +2;
403  vect[1].x() -= glyph->width()-1 +2;
404  vect[2].x() -= glyph->width()-1 +2;
405  vect[3].x() -= glyph->width()-1 +2;
406  #endif
407  }
408 
409  vect[0].y() -= mFont->mHeight;
410  vect[1].y() -= mFont->mHeight;
411  vect[2].y() -= mFont->mHeight;
412  vect[3].y() -= mFont->mHeight;
413 
414  #if (1)
415  // normalize coordinate orgin to the bottom/left corner
416  vect[0] -= (fvec3)bbox.minCorner();
417  vect[1] -= (fvec3)bbox.minCorner();
418  vect[2] -= (fvec3)bbox.minCorner();
419  vect[3] -= (fvec3)bbox.minCorner();
420  #endif
421 
422  #if (1)
423  vect[0].x() += applied_margin + displace;
424  vect[1].x() += applied_margin + displace;
425  vect[2].x() += applied_margin + displace;
426  vect[3].x() += applied_margin + displace;
427 
428  vect[0].y() += applied_margin;
429  vect[1].y() += applied_margin;
430  vect[2].y() += applied_margin;
431  vect[3].y() += applied_margin;
432  #endif
433 
434  // apply offset for outline rendering
435  vect[0].x() += offset.x();
436  vect[0].y() += offset.y();
437  vect[1].x() += offset.x();
438  vect[1].y() += offset.y();
439  vect[2].x() += offset.x();
440  vect[2].y() += offset.y();
441  vect[3].x() += offset.x();
442  vect[3].y() += offset.y();
443 
444  // alignment
445  for(int i=0; i<4; ++i)
446  {
447  if (alignment() & AlignHCenter)
448  {
449  VL_CHECK( !(alignment() & AlignRight) )
450  VL_CHECK( !(alignment() & AlignLeft) )
451  vect[i].x() -= (int)(bbox.width() / 2.0f);
452  }
453 
454  if (alignment() & AlignRight)
455  {
457  VL_CHECK( !(alignment() & AlignLeft) )
458  vect[i].x() -= (int)bbox.width();
459  }
460 
461  if (alignment() & AlignTop)
462  {
463  VL_CHECK( !(alignment() & AlignBottom) )
465  vect[i].y() -= (int)bbox.height();
466  }
467 
468  if (alignment() & AlignVCenter)
469  {
470  VL_CHECK( !(alignment() & AlignTop) )
471  VL_CHECK( !(alignment() & AlignBottom) )
472  vect[i].y() -= int(bbox.height() / 2.0);
473  }
474  }
475 
476  // apply text transform
477  vect[0] = m * vect[0];
478  vect[1] = m * vect[1];
479  vect[2] = m * vect[2];
480  vect[3] = m * vect[3];
481 
482  // actor's transform following in Text2D
483  if ( actor->transform() && mode() == Text2D )
484  {
485  vec4 v(0,0,0,1);
486  v = actor->transform()->worldMatrix() * v;
487 
488  camera->project(v,v);
489 
490  // from screen space to viewport space
491  v.x() -= viewport[0];
492  v.y() -= viewport[1];
493 
494  v.x() = (float)int(v.x());
495  v.y() = (float)int(v.y());
496 
497  vect[0].x() += (float)v.x();
498  vect[0].y() += (float)v.y();
499  vect[1].x() += (float)v.x();
500  vect[1].y() += (float)v.y();
501  vect[2].x() += (float)v.x();
502  vect[2].y() += (float)v.y();
503  vect[3].x() += (float)v.x();
504  vect[3].y() += (float)v.y();
505 
506  // clever trick part #2
507  vect[0].z() =
508  vect[1].z() =
509  vect[2].z() =
510  vect[3].z() = float((v.z() - 0.5f) / 0.5f);
511  }
512 
513  glDrawArrays(GL_TRIANGLE_FAN, 0, 4); VL_CHECK_OGL();
514 
515  #if (0)
516  glDisable(GL_TEXTURE_2D);
517  glColor3fv(vec3(1,0,0).ptr());
518  glDrawArrays(GL_LINE_LOOP, 0, 4);
519  glColor4fv(color.ptr());
520  glEnable(GL_TEXTURE_2D);
521  #endif
522  }
523 
524  if (just_space && lines[iline][c] == ' ' && iline != lines.size()-1)
525  {
526  if (layout() == LeftToRightText)
527  {
528  pen.x() += just_space + (just_remained_space?1:0);
529  // pen.y() += glyph->advance().y();
530  }
531  else
532  if (layout() == RightToLeftText)
533  {
534  pen.x() -= just_space + (just_remained_space?1:0);
535  // pen.y() -= glyph->advance().y();
536  }
537  if(just_remained_space)
538  just_remained_space--;
539  }
540 
541  if (layout() == LeftToRightText)
542  {
543  pen.x() += glyph->advance().x();
544  // pen.y() += glyph->advance().y();
545  }
546  else
547  if (layout() == RightToLeftText)
548  {
549  pen.x() -= glyph->advance().x();
550  // pen.y() -= glyph->advance().y();
551  }
552 
553  }
554  }
555 
556  glDisableClientState( GL_VERTEX_ARRAY ); VL_CHECK_OGL();
557  glDisableClientState( GL_TEXTURE_COORD_ARRAY ); VL_CHECK_OGL();
558 
559  VL_CHECK_OGL();
560 
561  if (mode() == Text2D)
562  {
563  glMatrixMode(GL_MODELVIEW);
564  glPopMatrix(); VL_CHECK_OGL()
565 
566  glMatrixMode(GL_PROJECTION);
567  glPopMatrix(); VL_CHECK_OGL()
568  }
569 
570  glDisable(GL_TEXTURE_2D);
571  glBindTexture(GL_TEXTURE_2D,0);
572 }
573 //-----------------------------------------------------------------------------
574 // returns the raw bounding box of the string, i.e. without alignment, margin and matrix transform.
576 {
577  AABB aabb;
578 
579  if(!font())
580  {
581  Log::error("Text::rawboundingRect() error: no Font assigned to the Text object.\n");
582  VL_TRAP()
583  return aabb;
584  }
585 
586  if (!font()->mFT_Face)
587  {
588  Log::error("Text::rawboundingRect() error: invalid FT_Face: probably you tried to load an unsupported font format.\n");
589  VL_TRAP()
590  return aabb;
591  }
592 
593  fvec2 pen(0,0);
594  fvec3 vect[4];
595 
596  FT_Long has_kerning = FT_HAS_KERNING( font()->mFT_Face );
597  FT_UInt previous = 0;
598 
599  for(int c=0; c<(int)text.length(); c++)
600  {
601  if (text[c] == '\n')
602  {
603  pen.y() -= mFont->mHeight ? mFont->mHeight : mFont->mSize;
604  pen.x() = 0;
605  continue;
606  }
607 
608  const ref<Glyph>& glyph = mFont->glyph(text[c]);
609 
610  // if glyph == NULL there was an error during its creation...
611  if (glyph.get() == NULL)
612  continue;
613 
614  if ( kerningEnabled() && has_kerning && previous && glyph->glyphIndex())
615  {
616  FT_Vector delta; delta.y = 0;
617  if (layout() == LeftToRightText)
618  {
619  FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta );
620  pen.x() += delta.x / 64.0f;
621  }
622  else
623  if (layout() == RightToLeftText)
624  {
625  FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta );
626  pen.x() -= delta.x / 64.0f;
627  }
628  pen.y() += delta.y / 64.0f;
629  }
630  previous = glyph->glyphIndex();
631 
632  if ( glyph->textureHandle() )
633  {
634  int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left();
635 
636  vect[0].x() = pen.x() + glyph->width()*0 + left -1;
637  vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
638 
639  vect[1].x() = pen.x() + glyph->width()*1 + left +1;
640  vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
641 
642  vect[2].x() = pen.x() + glyph->width()*1 + left +1;
643  vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
644 
645  vect[3].x() = pen.x() + glyph->width()*0 + left -1;
646  vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
647 
648  if (layout() == RightToLeftText)
649  {
650 #if (1)
651  vect[0].x() -= glyph->width()-1 +2;
652  vect[1].x() -= glyph->width()-1 +2;
653  vect[2].x() -= glyph->width()-1 +2;
654  vect[3].x() -= glyph->width()-1 +2;
655 #endif
656  }
657 
658  vect[0].y() -= mFont->mHeight;
659  vect[1].y() -= mFont->mHeight;
660  vect[2].y() -= mFont->mHeight;
661  vect[3].y() -= mFont->mHeight;
662 
663 #if(0)
664  // apply margin
665  //if (layout() == LeftToRightText)
666  //{
667  vect[0].x() += margin();
668  vect[1].x() += margin();
669  vect[2].x() += margin();
670  vect[3].x() += margin();
671  //}
672  //else
673  //if (layout() == RightToLeftText)
674  //{
675  // vect[0].x() -= margin();
676  // vect[1].x() -= margin();
677  // vect[2].x() -= margin();
678  // vect[3].x() -= margin();
679  //}
680 
681  vect[0].y() += margin();
682  vect[1].y() += margin();
683  vect[2].y() += margin();
684  vect[3].y() += margin();
685 #endif
686 
687  }
688 
689  aabb.addPoint( (vec3)vect[0] );
690  aabb.addPoint( (vec3)vect[1] );
691  aabb.addPoint( (vec3)vect[2] );
692  aabb.addPoint( (vec3)vect[3] );
693 
694  if (layout() == LeftToRightText)
695  pen += glyph->advance();
696  else
697  if (layout() == RightToLeftText)
698  pen -= glyph->advance();
699  }
700 
701  return aabb;
702 }
703 //-----------------------------------------------------------------------------
704 void Text::renderBackground(const Actor* actor, const Camera* camera) const
705 {
706  int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() };
707 
708  if (viewport[2] < 1) viewport[2] = 1;
709  if (viewport[3] < 1) viewport[3] = 1;
710 
711  if (mode() == Text2D)
712  {
713  glMatrixMode(GL_MODELVIEW);
714  glPushMatrix();
715  glLoadIdentity();
716  VL_CHECK_OGL();
717 
718  glMatrixMode(GL_PROJECTION);
719  glPushMatrix();
720  //glLoadIdentity();
721  //gluOrtho2D( -0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f );
722 
723  // clever trick part #1
724  fmat4 mat = fmat4::getOrtho(-0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f, -1, +1);
725  mat.e(2,2) = 1.0f;
726  mat.e(2,3) = 0.0f;
727  glLoadMatrixf(mat.ptr());
728  VL_CHECK_OGL();
729  }
730 
731  // Constant color
733 
734  // Constant normal
735  glNormal3f(0, 0, 1); VL_CHECK_OGL()
736 
737  vec3 a, b, c, d;
738  boundingRectTransformed( a, b, c, d, camera, mode() == Text2D ? actor : NULL );
739  fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d };
740  glEnableClientState( GL_VERTEX_ARRAY ); VL_CHECK_OGL()
741  glVertexPointer(3, GL_FLOAT, 0, vect); VL_CHECK_OGL()
742 
743  glDrawArrays(GL_TRIANGLE_FAN, 0, 4); VL_CHECK_OGL()
744 
745  glDisableClientState( GL_VERTEX_ARRAY ); VL_CHECK_OGL()
746 
747  if (mode() == Text2D)
748  {
749  glMatrixMode(GL_MODELVIEW);
750  glPopMatrix(); VL_CHECK_OGL()
751 
752  glMatrixMode(GL_PROJECTION);
753  glPopMatrix(); VL_CHECK_OGL()
754  }
755 }
756 //-----------------------------------------------------------------------------
757 void Text::renderBorder(const Actor* actor, const Camera* camera) const
758 {
759  int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() };
760 
761  if (viewport[2] < 1) viewport[2] = 1;
762  if (viewport[3] < 1) viewport[3] = 1;
763 
764  if (mode() == Text2D)
765  {
766  glMatrixMode(GL_MODELVIEW);
767  glPushMatrix();
768  glLoadIdentity();
769  VL_CHECK_OGL();
770 
771  glMatrixMode(GL_PROJECTION);
772  glPushMatrix();
773  //glLoadIdentity();
774  //gluOrtho2D( -0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f );
775 
776  // clever trick part #1
777  fmat4 mat = fmat4::getOrtho(-0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f, -1, +1);
778  mat.e(2,2) = 1.0f;
779  mat.e(2,3) = 0.0f;
780  glLoadMatrixf(mat.ptr());
781  VL_CHECK_OGL();
782  }
783 
784  // Constant color
785  glColor4f(mBorderColor.r(), mBorderColor.g(), mBorderColor.b(), mBorderColor.a());
786 
787  // Constant normal
788  glNormal3f( 0, 0, 1 );
789 
790  vec3 a,b,c,d;
791  boundingRectTransformed( a, b, c, d, camera, mode() == Text2D ? actor : NULL );
792  fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d };
793  glEnableClientState( GL_VERTEX_ARRAY );
794  glVertexPointer(3, GL_FLOAT, 0, vect);
795 
796  glDrawArrays(GL_LINE_LOOP, 0, 4);
797 
798  glDisableClientState( GL_VERTEX_ARRAY );
799 
800  if (mode() == Text2D)
801  {
802  glMatrixMode(GL_MODELVIEW);
803  glPopMatrix(); VL_CHECK_OGL()
804 
805  glMatrixMode(GL_PROJECTION);
806  glPopMatrix(); VL_CHECK_OGL()
807  }
808 }
809 //-----------------------------------------------------------------------------
813 {
814  return boundingRect(text());
815 }
816 //-----------------------------------------------------------------------------
818 {
819  int applied_margin = backgroundEnabled() || borderEnabled() ? margin() : 0;
820  AABB bbox = rawboundingRect( text );
821  bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*applied_margin,2.0f*applied_margin,0) );
822 
823  // normalize coordinate orgin to the bottom/left corner
824  vec3 min = bbox.minCorner() - bbox.minCorner();
825  vec3 max = bbox.maxCorner() - bbox.minCorner();
826 
827  // normalize coordinate orgin to the bottom/left corner
828 
829  // alignment
830 
831  if (alignment() & AlignHCenter)
832  {
833  VL_CHECK( !(alignment() & AlignRight) )
834  VL_CHECK( !(alignment() & AlignLeft) )
835  min.x() -= int(bbox.width() / 2.0);
836  max.x() -= int(bbox.width() / 2.0);
837  }
838 
839  if (alignment() & AlignRight)
840  {
842  VL_CHECK( !(alignment() & AlignLeft) )
843  min.x() -= (int)bbox.width();
844  max.x() -= (int)bbox.width();
845  }
846 
847  if (alignment() & AlignTop)
848  {
849  VL_CHECK( !(alignment() & AlignBottom) )
851  min.y() -= (int)bbox.height();
852  max.y() -= (int)bbox.height();
853  }
854 
855  if (alignment() & AlignVCenter)
856  {
857  VL_CHECK( !(alignment() & AlignTop) )
858  VL_CHECK( !(alignment() & AlignBottom) )
859  min.y() -= int(bbox.height() / 2.0);
860  max.y() -= int(bbox.height() / 2.0);
861  }
862 
863  // no matrix transform applied
864  // ...
865 
866  // no actor's transform applied
867  // ...
868 
869  AABB aabb;
870  aabb.setMinCorner(min);
871  aabb.setMaxCorner(max);
872  return aabb;
873 }
874 //-----------------------------------------------------------------------------
888 AABB Text::boundingRectTransformed(const Camera* camera, const Actor* actor) const
889 {
890  vec3 a, b, c, d;
891  return boundingRectTransformed(a, b, c, d, camera, actor);
892 }
893 //-----------------------------------------------------------------------------
894 AABB Text::boundingRectTransformed(vec3& a, vec3& b, vec3& c, vec3& d, const Camera* camera, const Actor* actor) const
895 {
896  AABB bbox = boundingRect();
897 
898  a = bbox.minCorner();
899  b.x() = (float)bbox.maxCorner().x();
900  b.y() = (float)bbox.minCorner().y();
901  c = bbox.maxCorner();
902  d.x() = (float)bbox.minCorner().x();
903  d.y() = (float)bbox.maxCorner().y();
904  // set z to 0
905  a.z() = b.z() = c.z() = d.z() = 0;
906 
907  // viewport alignment
908  fmat4 m = mMatrix;
909 
910  int w = camera->viewport()->width();
911  int h = camera->viewport()->height();
912 
913  if (w < 1) w = 1;
914  if (h < 1) h = 1;
915 
916  if ( !(actor && actor->transform()) && mode() == Text2D )
917  {
919  {
922  // vect[i].x() += int((viewport[2]-1.0f) / 2.0f);
923  m.translate( (float)int((w-1.0f) / 2.0f), 0, 0);
924  }
925 
927  {
930  // vect[i].x() += int(viewport[2]-1.0f);
931  m.translate( (float)int(w-1.0f), 0, 0);
932  }
933 
934  if (viewportAlignment() & AlignTop)
935  {
938  // vect[i].y() += int(viewport[3]-1.0f);
939  m.translate( 0, (float)int(h-1.0f), 0);
940  }
941 
943  {
946  // vect[i].y() += int((viewport[3]-1.0f) / 2.0f);
947  m.translate( 0, (float)int((h-1.0f) / 2.0f), 0);
948  }
949  }
950 
951  // ??? mix fixme: remove all these castings!
952  // apply matrix transform
953  a = (mat4)m * a;
954  b = (mat4)m * b;
955  c = (mat4)m * c;
956  d = (mat4)m * d;
957 
958  // apply actor's transform
959  if ( actor && actor->transform() )
960  {
961  if ( mode() == Text3D )
962  {
963  a = actor->transform()->worldMatrix() * a;
964  b = actor->transform()->worldMatrix() * b;
965  c = actor->transform()->worldMatrix() * c;
966  d = actor->transform()->worldMatrix() * d;
967  }
968  else
969  if ( mode() == Text2D )
970  {
971  // transform v
972  vec4 v(0,0,0,1);
973  v = actor->transform()->worldMatrix() * v;
974 
975  // project to screen
976  camera->project(v,v);
977 
978  // from screen space to viewport space
979  int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() };
980  v.x() -= viewport[0];
981  v.y() -= viewport[1];
982 
983  v.x() = (float)int(v.x());
984  v.y() = (float)int(v.y());
985 
986  a += v.xyz();
987  b += v.xyz();
988  c += v.xyz();
989  d += v.xyz();
990 
991  // clever trick part #2
992  a.z() =
993  b.z() =
994  c.z() =
995  d.z() = (v.z() - 0.5f) / 0.5f;
996  }
997  }
998 
999  bbox.setNull();
1000  bbox.addPoint(a);
1001  bbox.addPoint(b);
1002  bbox.addPoint(c);
1003  bbox.addPoint(d);
1004  return bbox;
1005 }
1006 //-----------------------------------------------------------------------------
1007 void Text::translate(float x, float y, float z)
1008 {
1009  mMatrix.translate(x,y,z);
1010 }
1011 //-----------------------------------------------------------------------------
1012 void Text::rotate(float degrees, float x, float y, float z)
1013 {
1014  mMatrix.rotate(degrees,x,y,z);
1015 }
1016 //-----------------------------------------------------------------------------
1018 {
1019  mMatrix.setIdentity();
1020 }
1021 //-----------------------------------------------------------------------------
const fvec4 & color() const
Associates a Renderable object to an Effect and Transform.
Definition: Actor.hpp:130
float s0() const
Definition: Font.hpp:86
int margin() const
Definition: Text.hpp:83
const T_Scalar & z() const
Definition: Vector4.hpp:103
int y() const
Definition: Viewport.hpp:67
AABB rawboundingRect(const String &text) const
Definition: Text.cpp:575
int alignment() const
Definition: Text.hpp:93
Matrix4 & rotate(T_Scalar degrees, const Vector3< T_Scalar > &v)
Definition: Matrix4.hpp:490
const T_Scalar & e(int i, int j) const
Definition: Matrix4.hpp:660
const T_Scalar & x() const
Definition: Vector4.hpp:101
Vector3< float > fvec3
A 3 components vector with float precision.
Definition: Vector3.hpp:252
int left() const
Definition: Font.hpp:80
Transform * transform()
Returns the Transform bound tho an Actor.
Definition: Actor.hpp:190
const T * get() const
Definition: Object.hpp:128
void resetMatrix()
Definition: Text.cpp:1017
bool borderEnabled() const
Definition: Text.hpp:111
int width() const
Definition: Font.hpp:74
T degrees(T radians)
Definition: glsl_math.hpp:173
const fvec2 & shadowVector() const
Definition: Text.hpp:80
The String class implements an advanced UTF16 (Unicode BMP) string manipulation engine.
Definition: String.hpp:62
const T_Scalar & z() const
Definition: Vector3.hpp:91
Matrix4 & translate(T_Scalar x, T_Scalar y, T_Scalar z)
Definition: Matrix4.hpp:574
const T_Scalar & r() const
Definition: Vector4.hpp:111
bool kerningEnabled() const
Definition: Text.hpp:117
int top() const
Definition: Font.hpp:83
Represents an OpenGL context, possibly a widget or a pbuffer, which can also respond to keyboard...
fvec4 mBorderColor
Definition: Text.hpp:153
The Glyph associated to a character of a given Font.
Definition: Font.hpp:55
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
Vector3< T_Scalar > xyz() const
Definition: Vector4.hpp:131
static Matrix4 getOrtho(float pleft, float pright, float pbottom, float ptop, float pnear, float pfar)
Definition: Matrix4.hpp:870
unsigned int textureHandle() const
Definition: Font.hpp:71
bool outlineEnabled() const
Definition: Text.hpp:120
void setNull()
Sets ths AABB as null, that is, empty.
Definition: AABB.hpp:57
int viewportAlignment() const
Definition: Text.hpp:96
ETextMode mode() const
Definition: Text.hpp:102
void renderBorder(const Actor *actor, const Camera *camera) const
Definition: Text.cpp:757
const fvec3 & normal() const
Viewport * viewport()
The viewport bound to a camera.
Definition: Camera.hpp:141
const fvec4 & color() const
Definition: Text.hpp:65
Visualization Library main namespace.
ref< Font > mFont
Definition: Text.hpp:150
int length() const
Returns the length of the string.
Definition: String.hpp:133
bool project(const vec4 &in_world, vec4 &out_viewp) const
Projects a vector from world coordinates to viewport coordinates.
Definition: Camera.cpp:248
Vector2< float > fvec2
A 2 components vector with float precision.
Definition: Vector2.hpp:282
const fvec2 & advance() const
Definition: Font.hpp:98
virtual void render_Implementation(const Actor *actor, const Shader *shader, const Camera *camera, OpenGLContext *gl_context) const
Definition: Text.cpp:43
const T_Scalar & g() const
Definition: Vector4.hpp:112
int height() const
Definition: Viewport.hpp:71
#define VL_TRAP()
Definition: checks.hpp:70
int width() const
Definition: Viewport.hpp:69
The AABB class implements an axis-aligned bounding box using vl::real precision.
Definition: AABB.hpp:44
float t0() const
Definition: Font.hpp:89
real width() const
Returns the width of the AABB computed as max.x - min.x.
Definition: AABB.cpp:151
const vec3 & maxCorner() const
Returns the corner of the AABB with the maximum x y z coordinates.
Definition: AABB.hpp:193
float max(float a, float b)
Definition: Vector2.hpp:311
float min(float a, float b)
Definition: Vector2.hpp:307
void setMinCorner(real x, real y, real z)
Sets the corner of the AABB with the minimum x y z coordinates.
Definition: AABB.hpp:196
bool backgroundEnabled() const
Definition: Text.hpp:114
void rotate(float degrees, float x, float y, float z)
Definition: Text.cpp:1012
void renderText(const Actor *, const Camera *camera, const fvec4 &color, const fvec2 &offset) const
Definition: Text.cpp:145
int height() const
Definition: Font.hpp:77
AABB boundingRect() const
Returns the plain 2D bounding box of the text, without taking into consideration the Text&#39;s matrix tr...
Definition: Text.cpp:812
void setMaxCorner(real x, real y, real z)
Sets the corner of the AABB with the maximum x y z coordinates.
Definition: AABB.hpp:202
void addPoint(const vec3 &p, real radius)
Updates the AABB to contain the given point.
Definition: AABB.cpp:135
const T_Scalar & y() const
Definition: Vector3.hpp:90
fvec4 mBackgroundColor
Definition: Text.hpp:154
const T_Scalar & y() const
Definition: Vector4.hpp:102
int x() const
Definition: Viewport.hpp:65
bool shadowEnabled() const
Definition: Text.hpp:123
const T_Scalar & b() const
Definition: Vector4.hpp:113
unsigned int glyphIndex() const
Definition: Font.hpp:101
#define NULL
Definition: OpenGLDefs.hpp:81
const mat4 & worldMatrix() const
Returns the world matrix used for rendering.
Definition: Transform.hpp:168
Manages most of the OpenGL rendering states responsible of the final aspect of the rendered objects...
Definition: Shader.hpp:1830
const fvec4 & outlineColor() const
Definition: Text.hpp:71
T_Scalar * ptr()
Definition: Vector3.hpp:86
const vec3 & minCorner() const
Returns the corner of the AABB with the minimum x y z coordinates.
Definition: AABB.hpp:190
void bindVAS(const IVertexAttribSet *vas, bool use_vbo, bool force)
Activates the specified vertex attribute set - For internal use only.
#define VL_CHECK_OGL()
Definition: OpenGL.hpp:156
fvec3 vec3
Defined as: &#39;typedef fvec3 vec3&#39;. See also VL_PIPELINE_PRECISION.
Definition: Vector3.hpp:269
void translate(float x, float y, float z)
Definition: Text.cpp:1007
fmat4 mMatrix
Definition: Text.hpp:158
fmat4 mat4
Defined as: &#39;typedef fmat4 mat4&#39;. See also VL_PIPELINE_PRECISION.
Definition: Matrix4.hpp:1240
Matrix4 & setIdentity()
Definition: Matrix4.hpp:406
T_Scalar * ptr()
Definition: Vector4.hpp:98
const fvec4 & shadowColor() const
Definition: Text.hpp:77
T_Scalar * ptr()
Definition: Matrix4.hpp:340
bool Has_GL_Version_2_0
Definition: OpenGL.cpp:54
const T_Scalar & x() const
Definition: Vector3.hpp:89
float t1() const
Definition: Font.hpp:95
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
const String & text() const
Definition: Text.hpp:62
ETextAlign textAlignment() const
Definition: Text.hpp:108
const T_Scalar & x() const
Definition: Vector2.hpp:132
T length(T v)
Definition: glsl_math.hpp:1084
const Font * font() const
Definition: Text.hpp:86
Represents a virtual camera defining, among other things, the point of view from which scenes can be ...
Definition: Camera.hpp:50
ETextLayout layout() const
Definition: Text.hpp:105
AABB boundingRectTransformed(vec3 &a, vec3 &b, vec3 &c, vec3 &d, const Camera *camera, const Actor *actor=NULL) const
Definition: Text.cpp:894
float s1() const
Definition: Font.hpp:92
void renderBackground(const Actor *actor, const Camera *camera) const
Definition: Text.cpp:704
const T_Scalar & y() const
Definition: Vector2.hpp:133
#define VL_CHECK(expr)
Definition: checks.hpp:73
const T_Scalar & a() const
Definition: Vector4.hpp:114
real height() const
Returns the height of the AABB computed as max.y - min.y.
Definition: AABB.cpp:158