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