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