0027860: Visualization - clean up Transformation Persistence API
[occt.git] / src / OpenGl / OpenGl_Text.cxx
1 // Created on: 2011-07-13
2 // Created by: Sergey ZERCHANINOV
3 // Copyright (c) 2011-2013 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <OpenGl_AspectText.hxx>
17 #include <OpenGl_GlCore11.hxx>
18 #include <OpenGl_GraphicDriver.hxx>
19 #include <OpenGl_ShaderManager.hxx>
20 #include <OpenGl_ShaderProgram.hxx>
21 #include <OpenGl_ShaderStates.hxx>
22 #include <OpenGl_Text.hxx>
23 #include <OpenGl_Workspace.hxx>
24 #include <OpenGl_View.hxx>
25
26 #include <Font_FontMgr.hxx>
27 #include <Font_FTFont.hxx>
28 #include <Graphic3d_TransformUtils.hxx>
29 #include <TCollection_HAsciiString.hxx>
30
31 #ifdef HAVE_GL2PS
32   #include <gl2ps.h>
33 #endif
34
35 namespace
36 {
37   static const GLdouble THE_IDENTITY_MATRIX[16] =
38   {
39     1.0, 0.0, 0.0, 0.0,
40     0.0, 1.0, 0.0, 0.0,
41     0.0, 0.0, 1.0, 0.0,
42     0.0, 0.0, 0.0, 1.0
43   };
44
45 #ifdef HAVE_GL2PS
46   static char const* TheFamily[] = {"Helvetica", "Courier", "Times"};
47   static char const* TheItalic[] = {"Oblique",   "Oblique", "Italic"};
48   static char const* TheBase[]   = {"", "", "-Roman"};
49
50   //! Convert font name used for rendering to some "good" font names
51   //! that produce good vector text.
52   static void getGL2PSFontName (const char* theSrcFont,
53                                 char*       thePsFont)
54   {
55     if (strstr (theSrcFont, "Symbol"))
56     {
57       sprintf (thePsFont, "%s", "Symbol");
58       return;
59     }
60     else if (strstr (theSrcFont, "ZapfDingbats"))
61     {
62       sprintf (thePsFont, "%s", "WingDings");
63       return;
64     }
65
66     int  aFontId  = 0;
67     bool isBold   = false;
68     bool isItalic = false;
69     if (strstr (theSrcFont, "Courier"))
70     {
71       aFontId = 1;
72     }
73     else if (strstr (theSrcFont, "Times"))
74     {
75       aFontId = 2;
76     }
77
78     if (strstr (theSrcFont, "Bold"))
79     {
80       isBold = true;
81     }
82     if (strstr (theSrcFont, "Italic")
83      || strstr (theSrcFont, "Oblique"))
84     {
85       isItalic = true;
86     }
87
88     if (isBold)
89     {
90       if (isItalic)
91       {
92         sprintf (thePsFont, "%s-Bold%s", TheFamily[aFontId], TheItalic[aFontId]);
93       }
94       else
95       {
96         sprintf (thePsFont, "%s-Bold", TheFamily[aFontId]);
97       }
98     }
99     else if (isItalic)
100     {
101       sprintf (thePsFont, "%s-%s", TheFamily[aFontId], TheItalic[aFontId]);
102     }
103     else
104     {
105       sprintf (thePsFont, "%s%s", TheFamily[aFontId], TheBase[aFontId]);
106     }
107   }
108
109   static void exportText (const NCollection_String& theText,
110                           const Standard_Boolean    theIs2d,
111                           const OpenGl_AspectText&  theAspect,
112                           const Standard_Integer    theHeight)
113   {
114
115     char aPsFont[64];
116     getGL2PSFontName (theAspect.Aspect()->Font().ToCString(), aPsFont);
117
118   #if !defined(GL_ES_VERSION_2_0)
119     if (theIs2d)
120     {
121       glRasterPos2f (0.0f, 0.0f);
122     }
123     else
124     {
125       glRasterPos3f (0.0f, 0.0f, 0.0f);
126     }
127
128     GLubyte aZero = 0;
129     glBitmap (1, 1, 0, 0, 0, 0, &aZero);
130   #endif
131
132     // Standard GL2PS's alignment isn't used, because it doesn't work correctly
133     // for all formats, therefore alignment is calculated manually relative
134     // to the bottom-left corner, which corresponds to the GL2PS_TEXT_BL value
135     gl2psTextOpt (theText.ToCString(), aPsFont, (GLshort)theHeight, GL2PS_TEXT_BL, (float )theAspect.Aspect()->GetTextAngle());
136   }
137 #endif
138
139 } // anonymous namespace
140
141 // =======================================================================
142 // function : OpenGl_Text
143 // purpose  :
144 // =======================================================================
145 OpenGl_Text::OpenGl_Text()
146 : myWinX (0.0f),
147   myWinY (0.0f),
148   myWinZ (0.0f),
149   myScaleHeight (1.0f),
150   myPoint  (0.0f, 0.0f, 0.0f),
151   myIs2d   (false),
152   myHasPlane (false),
153   myHasAnchorPoint (true)
154 {
155   myParams.Height = 10;
156   myParams.HAlign = Graphic3d_HTA_LEFT;
157   myParams.VAlign = Graphic3d_VTA_BOTTOM;
158 }
159
160 // =======================================================================
161 // function : OpenGl_Text
162 // purpose  :
163 // =======================================================================
164 OpenGl_Text::OpenGl_Text (const Standard_Utf8Char* theText,
165                           const OpenGl_Vec3&       thePoint,
166                           const OpenGl_TextParam&  theParams)
167 : myWinX (0.0f),
168   myWinY (0.0f),
169   myWinZ (0.0f),
170   myScaleHeight  (1.0f),
171   myExportHeight (1.0f),
172   myParams (theParams),
173   myString (theText),
174   myPoint  (thePoint),
175   myIs2d   (false),
176   myHasPlane (false),
177   myHasAnchorPoint (true)
178 {
179   //
180 }
181
182 // =======================================================================
183 // function : OpenGl_Text
184 // purpose  :
185 // =======================================================================
186 OpenGl_Text::OpenGl_Text (const Standard_Utf8Char* theText,
187                           const gp_Ax2&            theOrientation,
188                           const OpenGl_TextParam&  theParams,
189                           const bool               theHasOwnAnchor)
190 : myWinX         (0.0),
191   myWinY         (0.0),
192   myWinZ         (0.0),
193   myScaleHeight  (1.0),
194   myExportHeight (1.0),
195   myParams       (theParams),
196   myString       (theText),
197   myIs2d         (false),
198   myOrientation  (theOrientation),
199   myHasPlane     (true),
200   myHasAnchorPoint (theHasOwnAnchor)
201 {
202   const gp_Pnt& aPoint = theOrientation.Location();
203   myPoint = OpenGl_Vec3 (static_cast<Standard_ShortReal> (aPoint.X()),
204                          static_cast<Standard_ShortReal> (aPoint.Y()),
205                          static_cast<Standard_ShortReal> (aPoint.Z()));
206 }
207
208 // =======================================================================
209 // function : SetPosition
210 // purpose  :
211 // =======================================================================
212 void OpenGl_Text::SetPosition (const OpenGl_Vec3& thePoint)
213 {
214   myPoint = thePoint;
215 }
216
217 // =======================================================================
218 // function : SetFontSize
219 // purpose  :
220 // =======================================================================
221 void OpenGl_Text::SetFontSize (const Handle(OpenGl_Context)& theCtx,
222                                const Standard_Integer        theFontSize)
223 {
224   if (myParams.Height != theFontSize)
225   {
226     Release (theCtx.operator->());
227   }
228   myParams.Height = theFontSize;
229 }
230
231 // =======================================================================
232 // function : Init
233 // purpose  :
234 // =======================================================================
235 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
236                         const Standard_Utf8Char*      theText,
237                         const OpenGl_Vec3&            thePoint)
238 {
239   releaseVbos (theCtx.operator->());
240   myIs2d   = false;
241   myPoint  = thePoint;
242   myString.FromUnicode (theText);
243 }
244
245 // =======================================================================
246 // function : Init
247 // purpose  :
248 // =======================================================================
249 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
250                         const Standard_Utf8Char*      theText,
251                         const OpenGl_Vec3&            thePoint,
252                         const OpenGl_TextParam&       theParams)
253 {
254   if (myParams.Height != theParams.Height)
255   {
256     Release (theCtx.operator->());
257   }
258   else
259   {
260     releaseVbos (theCtx.operator->());
261   }
262   myIs2d   = false;
263   myParams = theParams;
264   myPoint  = thePoint;
265   myString.FromUnicode (theText);
266 }
267
268 // =======================================================================
269 // function : Init
270 // purpose  :
271 // =======================================================================
272 void OpenGl_Text::Init (const Handle(OpenGl_Context)&     theCtx,
273                         const TCollection_ExtendedString& theText,
274                         const OpenGl_Vec2&                thePoint,
275                         const OpenGl_TextParam&           theParams)
276 {
277   if (myParams.Height != theParams.Height)
278   {
279     Release (theCtx.operator->());
280   }
281   else
282   {
283     releaseVbos (theCtx.operator->());
284   }
285   myIs2d       = true;
286   myParams     = theParams;
287   myPoint.xy() = thePoint;
288   myPoint.z()  = 0.0f;
289   myString.FromUnicode (theText.ToExtString());
290 }
291
292 // =======================================================================
293 // function : ~OpenGl_Text
294 // purpose  :
295 // =======================================================================
296 OpenGl_Text::~OpenGl_Text()
297 {
298   //
299 }
300
301 // =======================================================================
302 // function : releaseVbos
303 // purpose  :
304 // =======================================================================
305 void OpenGl_Text::releaseVbos (OpenGl_Context* theCtx)
306 {
307   for (Standard_Integer anIter = 0; anIter < myVertsVbo.Length(); ++anIter)
308   {
309     Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.ChangeValue (anIter);
310     Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.ChangeValue (anIter);
311
312     if (theCtx)
313     {
314       theCtx->DelayedRelease (aVerts);
315       theCtx->DelayedRelease (aTCrds);
316     }
317     aVerts.Nullify();
318     aTCrds.Nullify();
319   }
320   myTextures.Clear();
321   myVertsVbo.Clear();
322   myTCrdsVbo.Clear();
323 }
324
325 // =======================================================================
326 // function : Release
327 // purpose  :
328 // =======================================================================
329 void OpenGl_Text::Release (OpenGl_Context* theCtx)
330 {
331   releaseVbos (theCtx);
332   if (!myFont.IsNull())
333   {
334     Handle(OpenGl_Context) aCtx = theCtx;
335     const TCollection_AsciiString aKey = myFont->ResourceKey();
336     myFont.Nullify();
337     if (! aCtx.IsNull())
338       aCtx->ReleaseResource (aKey, Standard_True);
339   }
340 }
341
342 // =======================================================================
343 // function : StringSize
344 // purpose  :
345 // =======================================================================
346 void OpenGl_Text::StringSize (const Handle(OpenGl_Context)& theCtx,
347                               const NCollection_String&     theText,
348                               const OpenGl_AspectText&      theTextAspect,
349                               const OpenGl_TextParam&       theParams,
350                               const unsigned int            theResolution,
351                               Standard_ShortReal&           theWidth,
352                               Standard_ShortReal&           theAscent,
353                               Standard_ShortReal&           theDescent)
354 {
355   theWidth   = 0.0f;
356   theAscent  = 0.0f;
357   theDescent = 0.0f;
358   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, theParams.Height, theResolution);
359   Handle(OpenGl_Font) aFont = FindFont (theCtx, theTextAspect, theParams.Height, theResolution, aFontKey);
360   if (aFont.IsNull() || !aFont->IsValid())
361   {
362     return;
363   }
364
365   theAscent  = aFont->Ascender();
366   theDescent = aFont->Descender();
367
368   GLfloat aWidth = 0.0f;
369   for (NCollection_Utf8Iter anIter = theText.Iterator(); *anIter != 0;)
370   {
371     const Standard_Utf32Char aCharThis =   *anIter;
372     const Standard_Utf32Char aCharNext = *++anIter;
373
374     if (aCharThis == '\x0D' // CR  (carriage return)
375      || aCharThis == '\a'   // BEL (alarm)
376      || aCharThis == '\f'   // FF  (form feed) NP (new page)
377      || aCharThis == '\b'   // BS  (backspace)
378      || aCharThis == '\v')  // VT  (vertical tab)
379     {
380       continue; // skip unsupported carriage control codes
381     }
382     else if (aCharThis == '\x0A') // LF (line feed, new line)
383     {
384       theWidth = Max (theWidth, aWidth);
385       aWidth   = 0.0f;
386       continue;
387     }
388     else if (aCharThis == ' ')
389     {
390       aWidth += aFont->FTFont()->AdvanceX (aCharThis, aCharNext);
391       continue;
392     }
393     else if (aCharThis == '\t')
394     {
395       aWidth += aFont->FTFont()->AdvanceX (' ', aCharNext) * 8.0f;
396       continue;
397     }
398
399     aWidth += aFont->FTFont()->AdvanceX (aCharThis, aCharNext);
400   }
401   theWidth = Max (theWidth, aWidth);
402
403   Handle(OpenGl_Context) aCtx = theCtx;
404   aFont.Nullify();
405   aCtx->ReleaseResource (aFontKey, Standard_True);
406 }
407
408 // =======================================================================
409 // function : Render
410 // purpose  :
411 // =======================================================================
412 void OpenGl_Text::Render (const Handle(OpenGl_Workspace)& theWorkspace) const
413 {
414   const OpenGl_AspectText*      aTextAspect  = theWorkspace->ApplyAspectText();
415   const Handle(OpenGl_Texture)  aPrevTexture = theWorkspace->DisableTexture();
416   const Handle(OpenGl_Context)& aCtx         = theWorkspace->GetGlContext();
417 #if !defined(GL_ES_VERSION_2_0)
418   const Standard_Integer aPrevPolygonMode  = aCtx->SetPolygonMode (GL_FILL);
419   const bool             aPrevHatchingMode = aCtx->SetPolygonHatchEnabled (false);
420 #endif
421
422   // Bind custom shader program or generate default version
423   if (aCtx->core20fwd != NULL)
424   {
425     aCtx->ShaderManager()->BindFontProgram (aTextAspect->ShaderProgramRes (aCtx));
426   }
427
428   myOrientationMatrix = theWorkspace->View()->Camera()->OrientationMatrix();
429   myProjMatrix.Convert (aCtx->ProjectionState.Current());
430
431   // use highlight color or colors from aspect
432   render (aCtx,
433           *aTextAspect,
434           theWorkspace->TextColor(),
435           theWorkspace->TextSubtitleColor(),
436           theWorkspace->View()->RenderingParams().Resolution);
437
438   aCtx->BindProgram (NULL);
439
440   // restore aspects
441   if (!aPrevTexture.IsNull())
442   {
443     theWorkspace->EnableTexture (aPrevTexture);
444   }
445 #if !defined(GL_ES_VERSION_2_0)
446   aCtx->SetPolygonMode         (aPrevPolygonMode);
447   aCtx->SetPolygonHatchEnabled (aPrevHatchingMode);
448 #endif
449
450   // restore Z buffer settings
451   if (theWorkspace->UseZBuffer())
452   {
453     glEnable (GL_DEPTH_TEST);
454   }
455 }
456
457 // =======================================================================
458 // function : Render
459 // purpose  :
460 // =======================================================================
461 void OpenGl_Text::Render (const Handle(OpenGl_Context)& theCtx,
462                           const OpenGl_AspectText&      theTextAspect,
463                           const unsigned int            theResolution) const
464 {
465   render (theCtx, theTextAspect,
466           theTextAspect.Aspect()->ColorRGBA(),
467           theTextAspect.Aspect()->ColorSubTitleRGBA(),
468           theResolution);
469 }
470
471 // =======================================================================
472 // function : setupMatrix
473 // purpose  :
474 // =======================================================================
475 void OpenGl_Text::setupMatrix (const Handle(OpenGl_Context)& theCtx,
476                                const OpenGl_AspectText&      theTextAspect,
477                                const OpenGl_Vec3             theDVec) const
478 {
479   OpenGl_Mat4d aModViewMat;
480   OpenGl_Mat4d aProjectMat;
481   if (myHasPlane && myHasAnchorPoint)
482   {
483     aProjectMat = myProjMatrix * myOrientationMatrix;
484   }
485   else
486   {
487     aProjectMat = myProjMatrix;
488   }
489
490   if (myIs2d)
491   {
492     Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, myPoint.x() + theDVec.x(), myPoint.y() + theDVec.y(), 0.f);
493     Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, 1.f, -1.f, 1.f);
494     Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.f, 0.f, 1.f);
495   }
496   else
497   {
498     // align coordinates to the nearest integer
499     // to avoid extra interpolation issues
500     GLdouble anObjX, anObjY, anObjZ;
501     Graphic3d_TransformUtils::UnProject<Standard_Real> (std::floor (myWinX + theDVec.x()),
502                                                         std::floor (myWinY + theDVec.y()),
503                                                         myWinZ + theDVec.z(),
504                                                         OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX),
505                                                         OpenGl_Mat4d::Map (aProjectMat),
506                                                         theCtx->Viewport(),
507                                                         anObjX,
508                                                         anObjY,
509                                                         anObjZ);
510
511     if (myHasPlane)
512     {
513       const gp_Dir& aVectorDir   = myOrientation.XDirection();
514       const gp_Dir& aVectorUp    = myOrientation.Direction();
515       const gp_Dir& aVectorRight = myOrientation.YDirection();
516
517       aModViewMat.SetColumn (2, OpenGl_Vec3d (aVectorUp.X(), aVectorUp.Y(), aVectorUp.Z()));
518       aModViewMat.SetColumn (1, OpenGl_Vec3d (aVectorRight.X(), aVectorRight.Y(), aVectorRight.Z()));
519       aModViewMat.SetColumn (0, OpenGl_Vec3d (aVectorDir.X(), aVectorDir.Y(), aVectorDir.Z()));
520
521       if (!myHasAnchorPoint)
522       {
523         OpenGl_Mat4d aPosMat;
524         aPosMat.SetColumn (3, OpenGl_Vec3d (myPoint.x(), myPoint.y(), myPoint.z()));
525         aPosMat *= aModViewMat;
526         aModViewMat.SetColumn (3, aPosMat.GetColumn (3));
527       }
528       else
529       {
530         aModViewMat.SetColumn (3, OpenGl_Vec3d (anObjX, anObjY, anObjZ));
531       }
532     }
533     else
534     {
535       Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, anObjX, anObjY, anObjZ);
536       Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.0, 0.0, 1.0);
537     }
538
539     if (!theTextAspect.Aspect()->GetTextZoomable())
540     {
541       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, myScaleHeight, myScaleHeight, myScaleHeight);
542     }
543   }
544
545   if (myHasPlane && !myHasAnchorPoint)
546   {
547     OpenGl_Mat4d aCurrentWorldViewMat;
548     aCurrentWorldViewMat.Convert (theCtx->WorldViewState.Current());
549     theCtx->WorldViewState.SetCurrent<Standard_Real> (aCurrentWorldViewMat * aModViewMat);
550   }
551   else
552   {
553     theCtx->WorldViewState.SetCurrent<Standard_Real> (aModViewMat);
554   }
555   theCtx->ApplyWorldViewMatrix();
556
557   if (!myIs2d)
558   {
559     theCtx->ProjectionState.SetCurrent<Standard_Real> (aProjectMat);
560     theCtx->ApplyProjectionMatrix();
561   }
562
563   if (!theCtx->ActiveProgram().IsNull())
564   {
565     // Upload updated state to shader program
566     theCtx->ShaderManager()->PushState (theCtx->ActiveProgram());
567   }
568 }
569
570 // =======================================================================
571 // function : drawText
572 // purpose  :
573 // =======================================================================
574 void OpenGl_Text::drawText (const Handle(OpenGl_Context)& theCtx,
575                             const OpenGl_AspectText&      theTextAspect) const
576 {
577 #ifdef HAVE_GL2PS
578   if (theCtx->IsFeedback())
579   {
580     // position of the text and alignment is calculated by transformation matrix
581     exportText (myString, myIs2d, theTextAspect, (Standard_Integer )myExportHeight);
582     return;
583   }
584 #else
585   (void )theTextAspect;
586 #endif
587
588   if (myVertsVbo.Length() != myTextures.Length()
589    || myTextures.IsEmpty())
590   {
591     return;
592   }
593
594   for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
595   {
596     const GLuint aTexId = myTextures.Value (anIter);
597     glBindTexture (GL_TEXTURE_2D, aTexId);
598
599     const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
600     const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
601     aVerts->BindAttribute (theCtx, Graphic3d_TOA_POS);
602     aTCrds->BindAttribute (theCtx, Graphic3d_TOA_UV);
603
604     glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
605
606     aTCrds->UnbindAttribute (theCtx, Graphic3d_TOA_UV);
607     aVerts->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
608   }
609   glBindTexture (GL_TEXTURE_2D, 0);
610 }
611
612 // =======================================================================
613 // function : FontKey
614 // purpose  :
615 // =======================================================================
616 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
617                                               const Standard_Integer   theHeight,
618                                               const unsigned int       theResolution)
619 {
620   const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
621                                  ? theAspect.Aspect()->GetTextFontAspect()
622                                  : Font_FA_Regular;
623   return theAspect.Aspect()->Font()
624        + TCollection_AsciiString(":") + Standard_Integer(anAspect)
625        + TCollection_AsciiString(":") + Standard_Integer(theResolution)
626        + TCollection_AsciiString(":") + theHeight;
627 }
628
629 // =======================================================================
630 // function : FindFont
631 // purpose  :
632 // =======================================================================
633 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
634                                            const OpenGl_AspectText&      theAspect,
635                                            const Standard_Integer        theHeight,
636                                            const unsigned int            theResolution,
637                                            const TCollection_AsciiString theKey)
638 {
639   Handle(OpenGl_Font) aFont;
640   if (theHeight < 2)
641   {
642     return aFont; // invalid parameters
643   }
644
645   if (!theCtx->GetResource (theKey, aFont))
646   {
647     Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
648     const Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (theAspect.Aspect()->Font());
649     const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
650                                    ? theAspect.Aspect()->GetTextFontAspect()
651                                    : Font_FA_Regular;
652     Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect, theHeight);
653     Handle(Font_FTFont) aFontFt;
654     if (!aRequestedFont.IsNull())
655     {
656       aFontFt = new Font_FTFont (NULL);
657
658       if (aFontFt->Init (aRequestedFont->FontPath()->ToCString(), theHeight, theResolution))
659       {
660         aFont = new OpenGl_Font (aFontFt, theKey);
661         if (!aFont->Init (theCtx))
662         {
663           TCollection_ExtendedString aMsg;
664           aMsg += "Font '";
665           aMsg += theAspect.Aspect()->Font();
666           aMsg += "' - initialization of GL resources has failed!";
667           theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
668           aFontFt.Nullify();
669           aFont->Release (theCtx.operator->());
670           aFont = new OpenGl_Font (aFontFt, theKey);
671         }
672       }
673       else
674       {
675         TCollection_ExtendedString aMsg;
676         aMsg += "Font '";
677         aMsg += theAspect.Aspect()->Font();
678         aMsg += "' is broken or has incompatible format! File path: ";
679         aMsg += aRequestedFont->FontPath()->ToCString();
680         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
681         aFontFt.Nullify();
682         aFont = new OpenGl_Font (aFontFt, theKey);
683       }
684     }
685     else
686     {
687       TCollection_ExtendedString aMsg;
688       aMsg += "Font '";
689       aMsg += theAspect.Aspect()->Font();
690       aMsg += "' is not found in the system!";
691       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
692       aFont = new OpenGl_Font (aFontFt, theKey);
693     }
694
695     theCtx->ShareResource (theKey, aFont);
696   }
697   return aFont;
698 }
699
700 // =======================================================================
701 // function : render
702 // purpose  :
703 // =======================================================================
704 void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx,
705                           const OpenGl_AspectText&      theTextAspect,
706                           const OpenGl_Vec4&            theColorText,
707                           const OpenGl_Vec4&            theColorSubs,
708                           const unsigned int            theResolution) const
709 {
710   if (myString.IsEmpty())
711   {
712     return;
713   }
714
715   // Note that using difference resolution in different Views in same Viewer
716   // will lead to performance regression (for example, text will be recreated every time).
717   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height, theResolution);
718   if (!myFont.IsNull()
719    && !myFont->ResourceKey().IsEqual (aFontKey))
720   {
721     // font changed
722     const_cast<OpenGl_Text* > (this)->Release (theCtx.operator->());
723   }
724
725   if (myFont.IsNull())
726   {
727     myFont = FindFont (theCtx, theTextAspect, myParams.Height, theResolution, aFontKey);
728   }
729   if (!myFont->WasInitialized())
730   {
731     return;
732   }
733
734   if (myTextures.IsEmpty())
735   {
736     Font_TextFormatter aFormatter;
737     aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
738     aFormatter.Reset();
739
740     aFormatter.Append (myString, *myFont->FTFont().operator->());
741     aFormatter.Format();
742
743     OpenGl_TextBuilder aBuilder;
744     aBuilder.Perform (aFormatter,
745                       theCtx,
746                       *myFont.operator->(),
747                       myTextures,
748                       myVertsVbo,
749                       myTCrdsVbo);
750
751     aFormatter.BndBox (myBndBox);
752   }
753
754   if (myTextures.IsEmpty())
755   {
756     return;
757   }
758
759   myExportHeight = 1.0f;
760   myScaleHeight  = 1.0f;
761
762   if (myHasPlane && !myHasAnchorPoint)
763   {
764     OpenGl_Mat4d aWorldViewMat;
765     aWorldViewMat.Convert (theCtx->WorldViewState.Current());
766     theCtx->WorldViewState.Push();
767     theCtx->WorldViewState.SetCurrent<Standard_Real> (aWorldViewMat);
768     theCtx->ApplyWorldViewMatrix();
769   }
770   else
771   {
772     theCtx->WorldViewState.Push();
773   }
774
775   myModelMatrix.Convert (theCtx->WorldViewState.Current() * theCtx->ModelWorldState.Current());
776
777   if (!myIs2d)
778   {
779     Graphic3d_TransformUtils::Project<Standard_Real> (myPoint.x(),
780                                                       myPoint.y(),
781                                                       myPoint.z(),
782                                                       myModelMatrix,
783                                                       myProjMatrix,
784                                                       theCtx->Viewport(),
785                                                       myWinX,
786                                                       myWinY,
787                                                       myWinZ);
788
789     // compute scale factor for constant text height
790     GLdouble x1, y1, z1;
791     Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX,
792                                                         myWinY,
793                                                         myWinZ,
794                                                         OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX),
795                                                         myProjMatrix,
796                                                         theCtx->Viewport(),
797                                                         x1,
798                                                         y1,
799                                                         z1);
800
801     GLdouble x2, y2, z2;
802     const GLdouble h = (GLdouble )myFont->FTFont()->PointSize();
803     Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX,
804                                                         myWinY + h,
805                                                         myWinZ,
806                                                         OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX),
807                                                         myProjMatrix,
808                                                         theCtx->Viewport(),
809                                                         x2,
810                                                         y2,
811                                                         z2);
812
813     myScaleHeight = (y2 - y1) / h;
814     if (theTextAspect.Aspect()->GetTextZoomable())
815     {
816       myExportHeight = (float )h;
817     }
818   }
819   myExportHeight = (float )myFont->FTFont()->PointSize() / myExportHeight;
820
821 #if !defined(GL_ES_VERSION_2_0)
822   if (theCtx->core11 != NULL)
823   {
824     glDisable (GL_LIGHTING);
825   }
826 #endif
827
828   // setup depth test
829   if (myIs2d
830    || theTextAspect.Aspect()->Style() == Aspect_TOST_ANNOTATION)
831   {
832     glDisable (GL_DEPTH_TEST);
833   }
834
835   if (theCtx->core15fwd != NULL)
836   {
837     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0);
838   }
839 #if !defined(GL_ES_VERSION_2_0)
840   // activate texture unit
841   GLint aTexEnvParam = GL_REPLACE;
842   if (theCtx->core11 != NULL)
843   {
844     // setup alpha test
845     glAlphaFunc (GL_GEQUAL, 0.285f);
846     glEnable (GL_ALPHA_TEST);
847
848     glDisable (GL_TEXTURE_1D);
849     glEnable  (GL_TEXTURE_2D);
850     glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
851     if (aTexEnvParam != GL_REPLACE)
852     {
853       glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
854     }
855   }
856 #endif
857
858   // setup blending
859   glEnable (GL_BLEND);
860   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
861
862   // extra drawings
863   switch (theTextAspect.Aspect()->DisplayType())
864   {
865     case Aspect_TODT_BLEND:
866     {
867     #if !defined(GL_ES_VERSION_2_0)
868       glEnable  (GL_COLOR_LOGIC_OP);
869       glLogicOp (GL_XOR);
870     #endif
871       break;
872     }
873     case Aspect_TODT_SUBTITLE:
874     {
875     #if !defined(GL_ES_VERSION_2_0)
876       if (theCtx->core11 != NULL)
877       {
878         theCtx->SetColor4fv (theColorSubs);
879         setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
880
881         glBindTexture (GL_TEXTURE_2D, 0);
882         glBegin (GL_QUADS);
883         glVertex2f (myBndBox.Left,  myBndBox.Top);
884         glVertex2f (myBndBox.Right, myBndBox.Top);
885         glVertex2f (myBndBox.Right, myBndBox.Bottom);
886         glVertex2f (myBndBox.Left,  myBndBox.Bottom);
887         glEnd();
888       }
889     #endif
890       break;
891     }
892     case Aspect_TODT_DEKALE:
893     {
894       theCtx->SetColor4fv (theColorSubs);
895       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.00001f));
896       drawText    (theCtx, theTextAspect);
897       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.00001f));
898       drawText    (theCtx, theTextAspect);
899       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.00001f));
900       drawText    (theCtx, theTextAspect);
901       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.00001f));
902       drawText    (theCtx, theTextAspect);
903       break;
904     }
905     case Aspect_TODT_DIMENSION:
906     case Aspect_TODT_NORMAL:
907     {
908       break;
909     }
910   }
911
912   // main draw call
913   theCtx->SetColor4fv (theColorText);
914   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
915   drawText    (theCtx, theTextAspect);
916
917   if (!myIs2d)
918   {
919     theCtx->ProjectionState.SetCurrent<Standard_Real> (myProjMatrix);
920     theCtx->ApplyProjectionMatrix();
921   }
922
923 #if !defined(GL_ES_VERSION_2_0)
924   if (theCtx->core11 != NULL)
925   {
926     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
927   }
928 #endif
929
930   if (theTextAspect.Aspect()->DisplayType() == Aspect_TODT_DIMENSION)
931   {
932     setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
933
934     glDisable (GL_BLEND);
935     if (!myIs2d)
936     {
937       glDisable (GL_DEPTH_TEST);
938     }
939   #if !defined(GL_ES_VERSION_2_0)
940     if (theCtx->core11 != NULL)
941     {
942       glDisable (GL_TEXTURE_2D);
943       glDisable (GL_ALPHA_TEST);
944     }
945   #endif
946     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
947
948     glClear (GL_STENCIL_BUFFER_BIT);
949     glEnable (GL_STENCIL_TEST);
950     glStencilFunc (GL_ALWAYS, 1, 0xFF);
951     glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
952
953   #if !defined(GL_ES_VERSION_2_0)
954     if (theCtx->core11 != NULL)
955     {
956       glBegin (GL_QUADS);
957       glVertex2f (myBndBox.Left,  myBndBox.Top);
958       glVertex2f (myBndBox.Right, myBndBox.Top);
959       glVertex2f (myBndBox.Right, myBndBox.Bottom);
960       glVertex2f (myBndBox.Left,  myBndBox.Bottom);
961       glEnd();
962     }
963   #endif
964
965     glStencilFunc (GL_ALWAYS, 0, 0xFF);
966
967     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
968   }
969
970   // reset OpenGL state
971   glDisable (GL_BLEND);
972   glDisable (GL_STENCIL_TEST);
973 #if !defined(GL_ES_VERSION_2_0)
974   if (theCtx->core11 != NULL)
975   {
976     glDisable (GL_ALPHA_TEST);
977   }
978   glDisable (GL_COLOR_LOGIC_OP);
979 #endif
980
981   // model view matrix was modified
982   theCtx->WorldViewState.Pop();
983   theCtx->ApplyModelViewMatrix();
984 }