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