0029076: Visualization - implement element shrinking Shader
[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   const bool anAlphaToCoverageOld = theCtx->SetSampleAlphaToCoverage (false);
397 #if !defined(GL_ES_VERSION_2_0)
398   const Standard_Integer aPrevPolygonMode  = theCtx->SetPolygonMode (GL_FILL);
399   const bool             aPrevHatchingMode = theCtx->SetPolygonHatchEnabled (false);
400 #endif
401
402   render (theCtx, theTextAspect,
403           theTextAspect.Aspect()->ColorRGBA(),
404           theTextAspect.Aspect()->ColorSubTitleRGBA(),
405           theResolution);
406
407 #if !defined(GL_ES_VERSION_2_0)
408   theCtx->SetPolygonMode         (aPrevPolygonMode);
409   theCtx->SetPolygonHatchEnabled (aPrevHatchingMode);
410 #endif
411   theCtx->SetSampleAlphaToCoverage (anAlphaToCoverageOld);
412 }
413
414 // =======================================================================
415 // function : setupMatrix
416 // purpose  :
417 // =======================================================================
418 void OpenGl_Text::setupMatrix (const Handle(OpenGl_Context)& theCtx,
419                                const OpenGl_AspectText&      theTextAspect,
420                                const OpenGl_Vec3             theDVec) const
421 {
422   OpenGl_Mat4d aModViewMat;
423   OpenGl_Mat4d aProjectMat;
424   if (myHasPlane && myHasAnchorPoint)
425   {
426     aProjectMat = myProjMatrix * myOrientationMatrix;
427   }
428   else
429   {
430     aProjectMat = myProjMatrix;
431   }
432
433   if (myIs2d)
434   {
435     Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, myPoint.x() + theDVec.x(), myPoint.y() + theDVec.y(), 0.f);
436     Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, 1.f, -1.f, 1.f);
437     Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.f, 0.f, 1.f);
438   }
439   else
440   {
441     // align coordinates to the nearest integer
442     // to avoid extra interpolation issues
443     GLdouble anObjX, anObjY, anObjZ;
444     Graphic3d_TransformUtils::UnProject<Standard_Real> (std::floor (myWinX + theDVec.x()),
445                                                         std::floor (myWinY + theDVec.y()),
446                                                         myWinZ + theDVec.z(),
447                                                         OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX),
448                                                         OpenGl_Mat4d::Map (aProjectMat),
449                                                         theCtx->Viewport(),
450                                                         anObjX,
451                                                         anObjY,
452                                                         anObjZ);
453
454     if (myHasPlane)
455     {
456       const gp_Dir& aVectorDir   = myOrientation.XDirection();
457       const gp_Dir& aVectorUp    = myOrientation.Direction();
458       const gp_Dir& aVectorRight = myOrientation.YDirection();
459
460       aModViewMat.SetColumn (2, OpenGl_Vec3d (aVectorUp.X(), aVectorUp.Y(), aVectorUp.Z()));
461       aModViewMat.SetColumn (1, OpenGl_Vec3d (aVectorRight.X(), aVectorRight.Y(), aVectorRight.Z()));
462       aModViewMat.SetColumn (0, OpenGl_Vec3d (aVectorDir.X(), aVectorDir.Y(), aVectorDir.Z()));
463
464       if (!myHasAnchorPoint)
465       {
466         OpenGl_Mat4d aPosMat;
467         aPosMat.SetColumn (3, OpenGl_Vec3d (myPoint.x(), myPoint.y(), myPoint.z()));
468         aPosMat *= aModViewMat;
469         aModViewMat.SetColumn (3, aPosMat.GetColumn (3));
470       }
471       else
472       {
473         aModViewMat.SetColumn (3, OpenGl_Vec3d (anObjX, anObjY, anObjZ));
474       }
475     }
476     else
477     {
478       Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, anObjX, anObjY, anObjZ);
479       Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.0, 0.0, 1.0);
480     }
481
482     if (!theTextAspect.Aspect()->GetTextZoomable())
483     {
484       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, myScaleHeight, myScaleHeight, myScaleHeight);
485     }
486     else if (theCtx->HasRenderScale())
487     {
488       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, theCtx->RenderScaleInv(), theCtx->RenderScaleInv(), theCtx->RenderScaleInv());
489     }
490   }
491
492   if (myHasPlane && !myHasAnchorPoint)
493   {
494     OpenGl_Mat4d aCurrentWorldViewMat;
495     aCurrentWorldViewMat.Convert (theCtx->WorldViewState.Current());
496     theCtx->WorldViewState.SetCurrent<Standard_Real> (aCurrentWorldViewMat * aModViewMat);
497   }
498   else
499   {
500     theCtx->WorldViewState.SetCurrent<Standard_Real> (aModViewMat);
501   }
502   theCtx->ApplyWorldViewMatrix();
503
504   if (!myIs2d)
505   {
506     theCtx->ProjectionState.SetCurrent<Standard_Real> (aProjectMat);
507     theCtx->ApplyProjectionMatrix();
508   }
509
510   // Upload updated state to shader program
511   theCtx->ShaderManager()->PushState (theCtx->ActiveProgram());
512 }
513
514 // =======================================================================
515 // function : drawText
516 // purpose  :
517 // =======================================================================
518 void OpenGl_Text::drawText (const Handle(OpenGl_Context)& theCtx,
519                             const OpenGl_AspectText&      theTextAspect) const
520 {
521   (void )theTextAspect;
522   if (myVertsVbo.Length() != myTextures.Length()
523    || myTextures.IsEmpty())
524   {
525     return;
526   }
527
528   for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
529   {
530     const GLuint aTexId = myTextures.Value (anIter);
531     glBindTexture (GL_TEXTURE_2D, aTexId);
532
533     const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
534     const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
535     aVerts->BindAttribute (theCtx, Graphic3d_TOA_POS);
536     aTCrds->BindAttribute (theCtx, Graphic3d_TOA_UV);
537
538     glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
539
540     aTCrds->UnbindAttribute (theCtx, Graphic3d_TOA_UV);
541     aVerts->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
542   }
543   glBindTexture (GL_TEXTURE_2D, 0);
544 }
545
546 // =======================================================================
547 // function : FontKey
548 // purpose  :
549 // =======================================================================
550 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
551                                               const Standard_Integer   theHeight,
552                                               const unsigned int       theResolution)
553 {
554   const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
555                                  ? theAspect.Aspect()->GetTextFontAspect()
556                                  : Font_FA_Regular;
557   return theAspect.Aspect()->Font()
558        + TCollection_AsciiString(":") + Standard_Integer(anAspect)
559        + TCollection_AsciiString(":") + Standard_Integer(theResolution)
560        + TCollection_AsciiString(":") + theHeight;
561 }
562
563 // =======================================================================
564 // function : FindFont
565 // purpose  :
566 // =======================================================================
567 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
568                                            const OpenGl_AspectText&      theAspect,
569                                            const Standard_Integer        theHeight,
570                                            const unsigned int            theResolution,
571                                            const TCollection_AsciiString theKey)
572 {
573   Handle(OpenGl_Font) aFont;
574   if (theHeight < 2)
575   {
576     return aFont; // invalid parameters
577   }
578
579   if (!theCtx->GetResource (theKey, aFont))
580   {
581     Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
582     const TCollection_AsciiString& aFontName = theAspect.Aspect()->Font();
583     Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
584                              ? theAspect.Aspect()->GetTextFontAspect()
585                              : Font_FA_Regular;
586     Handle(Font_FTFont) aFontFt;
587     if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect))
588     {
589       aFontFt = new Font_FTFont (Handle(Font_FTLibrary)());
590
591       if (aFontFt->Init (aRequestedFont->FontPathAny (anAspect).ToCString(), theHeight, theResolution))
592       {
593         aFont = new OpenGl_Font (aFontFt, theKey);
594         if (!aFont->Init (theCtx))
595         {
596           TCollection_ExtendedString aMsg;
597           aMsg += "Font '";
598           aMsg += theAspect.Aspect()->Font();
599           aMsg += "' - initialization of GL resources has failed!";
600           theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
601           aFontFt.Nullify();
602           aFont->Release (theCtx.operator->());
603           aFont = new OpenGl_Font (aFontFt, theKey);
604         }
605       }
606       else
607       {
608         TCollection_ExtendedString aMsg;
609         aMsg += "Font '";
610         aMsg += theAspect.Aspect()->Font();
611         aMsg += "' is broken or has incompatible format! File path: ";
612         aMsg += aRequestedFont->FontPathAny (anAspect);
613         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
614         aFontFt.Nullify();
615         aFont = new OpenGl_Font (aFontFt, theKey);
616       }
617     }
618     else
619     {
620       TCollection_ExtendedString aMsg;
621       aMsg += "Font '";
622       aMsg += theAspect.Aspect()->Font();
623       aMsg += "' is not found in the system!";
624       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
625       aFont = new OpenGl_Font (aFontFt, theKey);
626     }
627
628     theCtx->ShareResource (theKey, aFont);
629   }
630   return aFont;
631 }
632
633 // =======================================================================
634 // function : drawRect
635 // purpose  :
636 // =======================================================================
637 void OpenGl_Text::drawRect (const Handle(OpenGl_Context)& theCtx,
638                             const OpenGl_AspectText&      theTextAspect,
639                             const OpenGl_Vec4&            theColorSubs) const
640 {
641   Handle(OpenGl_ShaderProgram) aPrevProgram = theCtx->ActiveProgram();
642   if (myBndVertsVbo.IsNull())
643   {
644     OpenGl_Vec2 aQuad[4] =
645     {
646       OpenGl_Vec2(myBndBox.Right, myBndBox.Bottom),
647       OpenGl_Vec2(myBndBox.Right, myBndBox.Top),
648       OpenGl_Vec2(myBndBox.Left,  myBndBox.Bottom),
649       OpenGl_Vec2(myBndBox.Left,  myBndBox.Top)
650     };
651     if (theCtx->ToUseVbo())
652     {
653       myBndVertsVbo = new OpenGl_VertexBuffer();
654     }
655     else
656     {
657       myBndVertsVbo = new OpenGl_VertexBufferCompat();
658     }
659     myBndVertsVbo->Init (theCtx, 2, 4, aQuad[0].GetData());
660   }
661
662   // bind unlit program
663   theCtx->ShaderManager()->BindFaceProgram (Handle(OpenGl_TextureSet)(), Graphic3d_TOSM_UNLIT,
664                                             Graphic3d_AlphaMode_Opaque, Standard_False, Standard_False,
665                                             Handle(OpenGl_ShaderProgram)());
666
667 #if !defined(GL_ES_VERSION_2_0)
668   if (theCtx->core11 != NULL
669    && theCtx->ActiveProgram().IsNull())
670   {
671     glBindTexture (GL_TEXTURE_2D, 0);
672   }
673 #endif
674   theCtx->SetColor4fv (theColorSubs);
675   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
676   myBndVertsVbo->BindAttribute (theCtx, Graphic3d_TOA_POS);
677
678   theCtx->core20fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
679
680   myBndVertsVbo->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
681   theCtx->BindProgram (aPrevProgram);
682 }
683
684 // =======================================================================
685 // function : render
686 // purpose  :
687 // =======================================================================
688 void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx,
689                           const OpenGl_AspectText&      theTextAspect,
690                           const OpenGl_Vec4&            theColorText,
691                           const OpenGl_Vec4&            theColorSubs,
692                           const unsigned int            theResolution) const
693 {
694   if (myString.IsEmpty())
695   {
696     return;
697   }
698
699   // Note that using difference resolution in different Views in same Viewer
700   // will lead to performance regression (for example, text will be recreated every time).
701   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height, theResolution);
702   if (!myFont.IsNull()
703    && !myFont->ResourceKey().IsEqual (aFontKey))
704   {
705     // font changed
706     const_cast<OpenGl_Text* > (this)->Release (theCtx.operator->());
707   }
708
709   if (myFont.IsNull())
710   {
711     myFont = FindFont (theCtx, theTextAspect, myParams.Height, theResolution, aFontKey);
712   }
713   if (!myFont->WasInitialized())
714   {
715     return;
716   }
717
718   if (myTextures.IsEmpty())
719   {
720     Font_TextFormatter aFormatter;
721     aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
722     aFormatter.Reset();
723
724     aFormatter.Append (myString, *myFont->FTFont().operator->());
725     aFormatter.Format();
726
727     OpenGl_TextBuilder aBuilder;
728     aBuilder.Perform (aFormatter,
729                       theCtx,
730                       *myFont.operator->(),
731                       myTextures,
732                       myVertsVbo,
733                       myTCrdsVbo);
734
735     aFormatter.BndBox (myBndBox);
736     if (!myBndVertsVbo.IsNull())
737     {
738       myBndVertsVbo->Release (theCtx.get());
739       myBndVertsVbo.Nullify();
740     }
741   }
742
743   if (myTextures.IsEmpty())
744   {
745     return;
746   }
747
748   myExportHeight = 1.0f;
749   myScaleHeight  = 1.0f;
750
751   theCtx->WorldViewState.Push();
752   myModelMatrix.Convert (theCtx->WorldViewState.Current() * theCtx->ModelWorldState.Current());
753
754   const GLdouble aPointSize = (GLdouble )myFont->FTFont()->PointSize();
755   if (!myIs2d)
756   {
757     Graphic3d_TransformUtils::Project<Standard_Real> (myPoint.x(), myPoint.y(), myPoint.z(),
758                                                       myModelMatrix, myProjMatrix, theCtx->Viewport(),
759                                                       myWinX, myWinY, myWinZ);
760
761     // compute scale factor for constant text height
762     if (theTextAspect.Aspect()->GetTextZoomable())
763     {
764       myExportHeight = aPointSize;
765     }
766     else
767     {
768       Graphic3d_Vec3d aPnt1, aPnt2;
769       Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX, myWinY, myWinZ,
770                                                           OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX), myProjMatrix, theCtx->Viewport(),
771                                                           aPnt1.x(), aPnt1.y(), aPnt1.z());
772       Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX, myWinY + aPointSize, myWinZ,
773                                                           OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX), myProjMatrix, theCtx->Viewport(),
774                                                           aPnt2.x(), aPnt2.y(), aPnt2.z());
775       myScaleHeight = (aPnt2.y() - aPnt1.y()) / aPointSize;
776     }
777   }
778   myExportHeight = aPointSize / myExportHeight;
779
780 #if !defined(GL_ES_VERSION_2_0)
781   if (theCtx->core11 != NULL
782    && theCtx->caps->ffpEnable)
783   {
784     glDisable (GL_LIGHTING);
785   }
786 #endif
787
788   // setup depth test
789   const bool hasDepthTest = !myIs2d
790                          && theTextAspect.Aspect()->Style() != Aspect_TOST_ANNOTATION;
791   if (!hasDepthTest)
792   {
793     glDisable (GL_DEPTH_TEST);
794   }
795
796   if (theCtx->core15fwd != NULL)
797   {
798     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0);
799   }
800 #if !defined(GL_ES_VERSION_2_0)
801   // activate texture unit
802   GLint aTexEnvParam = GL_REPLACE;
803   if (theCtx->core11 != NULL)
804   {
805     glDisable (GL_TEXTURE_1D);
806     glEnable  (GL_TEXTURE_2D);
807     glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
808     if (aTexEnvParam != GL_REPLACE)
809     {
810       glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
811     }
812   }
813 #endif
814
815   // setup blending
816   glEnable (GL_BLEND);
817   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
818
819   // extra drawings
820   switch (theTextAspect.Aspect()->DisplayType())
821   {
822     case Aspect_TODT_BLEND:
823     {
824     #if !defined(GL_ES_VERSION_2_0)
825       glEnable  (GL_COLOR_LOGIC_OP);
826       glLogicOp (GL_XOR);
827     #endif
828       break;
829     }
830     case Aspect_TODT_SUBTITLE:
831     {
832       BackPolygonOffsetSentry aPolygonOffsetTmp (hasDepthTest ? theCtx : Handle(OpenGl_Context)());
833       drawRect (theCtx, theTextAspect, theColorSubs);
834       break;
835     }
836     case Aspect_TODT_DEKALE:
837     {
838       BackPolygonOffsetSentry aPolygonOffsetTmp (hasDepthTest ? theCtx : Handle(OpenGl_Context)());
839       theCtx->SetColor4fv (theColorSubs);
840       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.0f));
841       drawText    (theCtx, theTextAspect);
842       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.0f));
843       drawText    (theCtx, theTextAspect);
844       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.0f));
845       drawText    (theCtx, theTextAspect);
846       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.0f));
847       drawText    (theCtx, theTextAspect);
848       break;
849     }
850     case Aspect_TODT_SHADOW:
851     {
852       BackPolygonOffsetSentry aPolygonOffsetTmp (hasDepthTest ? theCtx : Handle(OpenGl_Context)());
853       theCtx->SetColor4fv (theColorSubs);
854       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.0f));
855       drawText    (theCtx, theTextAspect);
856       break;
857     }
858     case Aspect_TODT_DIMENSION:
859     case Aspect_TODT_NORMAL:
860     {
861       break;
862     }
863   }
864
865   // main draw call
866   theCtx->SetColor4fv (theColorText);
867   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
868   drawText    (theCtx, theTextAspect);
869
870   if (!myIs2d)
871   {
872     theCtx->ProjectionState.SetCurrent<Standard_Real> (myProjMatrix);
873     theCtx->ApplyProjectionMatrix();
874   }
875
876 #if !defined(GL_ES_VERSION_2_0)
877   if (theCtx->core11 != NULL)
878   {
879     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
880   }
881 #endif
882
883   if (theTextAspect.Aspect()->DisplayType() == Aspect_TODT_DIMENSION)
884   {
885     glDisable (GL_BLEND);
886     if (!myIs2d)
887     {
888       glDisable (GL_DEPTH_TEST);
889     }
890   #if !defined(GL_ES_VERSION_2_0)
891     if (theCtx->core11 != NULL)
892     {
893       glDisable (GL_TEXTURE_2D);
894     }
895   #endif
896     const bool aColorMaskBack = theCtx->SetColorMask (false);
897
898     glClear (GL_STENCIL_BUFFER_BIT);
899     glEnable (GL_STENCIL_TEST);
900     glStencilFunc (GL_ALWAYS, 1, 0xFF);
901     glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
902
903     drawRect (theCtx, theTextAspect, OpenGl_Vec4 (1.0f, 1.0f, 1.0f, 1.0f));
904
905     glStencilFunc (GL_ALWAYS, 0, 0xFF);
906
907     theCtx->SetColorMask (aColorMaskBack);
908   }
909
910   // reset OpenGL state
911   glDisable (GL_BLEND);
912   glDisable (GL_STENCIL_TEST);
913 #if !defined(GL_ES_VERSION_2_0)
914   glDisable (GL_COLOR_LOGIC_OP);
915 #endif
916
917   // model view matrix was modified
918   theCtx->WorldViewState.Pop();
919   theCtx->ApplyModelViewMatrix();
920 }