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