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