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