e12620fe094b06a603b785652d882171daf2b84b
[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.xy() = thePoint;
223   myPoint.z()  = 0.0f;
224   myString.FromUnicode (theText.ToExtString());
225 }
226
227 // =======================================================================
228 // function : ~OpenGl_Text
229 // purpose  :
230 // =======================================================================
231 OpenGl_Text::~OpenGl_Text()
232 {
233   //
234 }
235
236 // =======================================================================
237 // function : releaseVbos
238 // purpose  :
239 // =======================================================================
240 void OpenGl_Text::releaseVbos (OpenGl_Context* theCtx)
241 {
242   for (Standard_Integer anIter = 0; anIter < myVertsVbo.Length(); ++anIter)
243   {
244     Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.ChangeValue (anIter);
245     Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.ChangeValue (anIter);
246
247     if (theCtx != NULL)
248     {
249       theCtx->DelayedRelease (aVerts);
250       theCtx->DelayedRelease (aTCrds);
251     }
252     aVerts.Nullify();
253     aTCrds.Nullify();
254   }
255   if (theCtx != NULL
256   && !myBndVertsVbo.IsNull())
257   {
258     theCtx->DelayedRelease (myBndVertsVbo);
259   }
260
261   myTextures.Clear();
262   myVertsVbo.Clear();
263   myTCrdsVbo.Clear();
264   myBndVertsVbo.Nullify();
265 }
266
267 // =======================================================================
268 // function : Release
269 // purpose  :
270 // =======================================================================
271 void OpenGl_Text::Release (OpenGl_Context* theCtx)
272 {
273   releaseVbos (theCtx);
274   if (!myFont.IsNull())
275   {
276     const TCollection_AsciiString aKey = myFont->ResourceKey();
277     myFont.Nullify();
278     if (theCtx != NULL)
279     {
280       theCtx->ReleaseResource (aKey, Standard_True);
281     }
282   }
283 }
284
285 // =======================================================================
286 // function : StringSize
287 // purpose  :
288 // =======================================================================
289 void OpenGl_Text::StringSize (const Handle(OpenGl_Context)& theCtx,
290                               const NCollection_String&     theText,
291                               const OpenGl_AspectText&      theTextAspect,
292                               const OpenGl_TextParam&       theParams,
293                               const unsigned int            theResolution,
294                               Standard_ShortReal&           theWidth,
295                               Standard_ShortReal&           theAscent,
296                               Standard_ShortReal&           theDescent)
297 {
298   theWidth   = 0.0f;
299   theAscent  = 0.0f;
300   theDescent = 0.0f;
301   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, theParams.Height, theResolution);
302   Handle(OpenGl_Font) aFont = FindFont (theCtx, theTextAspect, theParams.Height, theResolution, aFontKey);
303   if (aFont.IsNull() || !aFont->IsValid())
304   {
305     return;
306   }
307
308   theAscent  = aFont->Ascender();
309   theDescent = aFont->Descender();
310
311   GLfloat aWidth = 0.0f;
312   for (NCollection_Utf8Iter anIter = theText.Iterator(); *anIter != 0;)
313   {
314     const Standard_Utf32Char aCharThis =   *anIter;
315     const Standard_Utf32Char aCharNext = *++anIter;
316
317     if (aCharThis == '\x0D' // CR  (carriage return)
318      || aCharThis == '\a'   // BEL (alarm)
319      || aCharThis == '\f'   // FF  (form feed) NP (new page)
320      || aCharThis == '\b'   // BS  (backspace)
321      || aCharThis == '\v')  // VT  (vertical tab)
322     {
323       continue; // skip unsupported carriage control codes
324     }
325     else if (aCharThis == '\x0A') // LF (line feed, new line)
326     {
327       theWidth = Max (theWidth, aWidth);
328       aWidth   = 0.0f;
329       continue;
330     }
331     else if (aCharThis == ' ')
332     {
333       aWidth += aFont->FTFont()->AdvanceX (aCharThis, aCharNext);
334       continue;
335     }
336     else if (aCharThis == '\t')
337     {
338       aWidth += aFont->FTFont()->AdvanceX (' ', aCharNext) * 8.0f;
339       continue;
340     }
341
342     aWidth += aFont->FTFont()->AdvanceX (aCharThis, aCharNext);
343   }
344   theWidth = Max (theWidth, aWidth);
345
346   Handle(OpenGl_Context) aCtx = theCtx;
347   aFont.Nullify();
348   aCtx->ReleaseResource (aFontKey, Standard_True);
349 }
350
351 // =======================================================================
352 // function : Render
353 // purpose  :
354 // =======================================================================
355 void OpenGl_Text::Render (const Handle(OpenGl_Workspace)& theWorkspace) const
356 {
357   theWorkspace->SetAspectFace (&theWorkspace->FontFaceAspect());
358   theWorkspace->ApplyAspectFace();
359   const OpenGl_AspectText*      aTextAspect  = theWorkspace->ApplyAspectText();
360   const Handle(OpenGl_Context)& aCtx         = theWorkspace->GetGlContext();
361   const Handle(OpenGl_TextureSet) aPrevTexture = aCtx->BindTextures (Handle(OpenGl_TextureSet)());
362
363   // Bind custom shader program or generate default version
364   aCtx->ShaderManager()->BindFontProgram (aTextAspect->ShaderProgramRes (aCtx));
365
366   myOrientationMatrix = theWorkspace->View()->Camera()->OrientationMatrix();
367   myProjMatrix.Convert (aCtx->ProjectionState.Current());
368
369   // use highlight color or colors from aspect
370   render (aCtx,
371           *aTextAspect,
372           theWorkspace->TextColor(),
373           theWorkspace->TextSubtitleColor(),
374           aCtx->Resolution());
375
376   // restore aspects
377   if (!aPrevTexture.IsNull())
378   {
379     aCtx->BindTextures (aPrevTexture);
380   }
381
382   // restore Z buffer settings
383   if (theWorkspace->UseZBuffer())
384   {
385     glEnable (GL_DEPTH_TEST);
386   }
387 }
388
389 // =======================================================================
390 // function : Render
391 // purpose  :
392 // =======================================================================
393 void OpenGl_Text::Render (const Handle(OpenGl_Context)& theCtx,
394                           const OpenGl_AspectText&      theTextAspect,
395                           const unsigned int            theResolution) const
396 {
397   render (theCtx, theTextAspect,
398           theTextAspect.Aspect()->ColorRGBA(),
399           theTextAspect.Aspect()->ColorSubTitleRGBA(),
400           theResolution);
401 }
402
403 // =======================================================================
404 // function : setupMatrix
405 // purpose  :
406 // =======================================================================
407 void OpenGl_Text::setupMatrix (const Handle(OpenGl_Context)& theCtx,
408                                const OpenGl_AspectText&      theTextAspect,
409                                const OpenGl_Vec3             theDVec) const
410 {
411   OpenGl_Mat4d aModViewMat;
412   OpenGl_Mat4d aProjectMat;
413   if (myHasPlane && myHasAnchorPoint)
414   {
415     aProjectMat = myProjMatrix * myOrientationMatrix;
416   }
417   else
418   {
419     aProjectMat = myProjMatrix;
420   }
421
422   if (myIs2d)
423   {
424     Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, myPoint.x() + theDVec.x(), myPoint.y() + theDVec.y(), 0.f);
425     Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, 1.f, -1.f, 1.f);
426     Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.f, 0.f, 1.f);
427   }
428   else
429   {
430     // align coordinates to the nearest integer
431     // to avoid extra interpolation issues
432     GLdouble anObjX, anObjY, anObjZ;
433     Graphic3d_TransformUtils::UnProject<Standard_Real> (std::floor (myWinX + theDVec.x()),
434                                                         std::floor (myWinY + theDVec.y()),
435                                                         myWinZ + theDVec.z(),
436                                                         OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX),
437                                                         OpenGl_Mat4d::Map (aProjectMat),
438                                                         theCtx->Viewport(),
439                                                         anObjX,
440                                                         anObjY,
441                                                         anObjZ);
442
443     if (myHasPlane)
444     {
445       const gp_Dir& aVectorDir   = myOrientation.XDirection();
446       const gp_Dir& aVectorUp    = myOrientation.Direction();
447       const gp_Dir& aVectorRight = myOrientation.YDirection();
448
449       aModViewMat.SetColumn (2, OpenGl_Vec3d (aVectorUp.X(), aVectorUp.Y(), aVectorUp.Z()));
450       aModViewMat.SetColumn (1, OpenGl_Vec3d (aVectorRight.X(), aVectorRight.Y(), aVectorRight.Z()));
451       aModViewMat.SetColumn (0, OpenGl_Vec3d (aVectorDir.X(), aVectorDir.Y(), aVectorDir.Z()));
452
453       if (!myHasAnchorPoint)
454       {
455         OpenGl_Mat4d aPosMat;
456         aPosMat.SetColumn (3, OpenGl_Vec3d (myPoint.x(), myPoint.y(), myPoint.z()));
457         aPosMat *= aModViewMat;
458         aModViewMat.SetColumn (3, aPosMat.GetColumn (3));
459       }
460       else
461       {
462         aModViewMat.SetColumn (3, OpenGl_Vec3d (anObjX, anObjY, anObjZ));
463       }
464     }
465     else
466     {
467       Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, anObjX, anObjY, anObjZ);
468       Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.0, 0.0, 1.0);
469     }
470
471     if (!theTextAspect.Aspect()->GetTextZoomable())
472     {
473       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, myScaleHeight, myScaleHeight, myScaleHeight);
474     }
475     else if (theCtx->HasRenderScale())
476     {
477       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, theCtx->RenderScaleInv(), theCtx->RenderScaleInv(), theCtx->RenderScaleInv());
478     }
479   }
480
481   if (myHasPlane && !myHasAnchorPoint)
482   {
483     OpenGl_Mat4d aCurrentWorldViewMat;
484     aCurrentWorldViewMat.Convert (theCtx->WorldViewState.Current());
485     theCtx->WorldViewState.SetCurrent<Standard_Real> (aCurrentWorldViewMat * aModViewMat);
486   }
487   else
488   {
489     theCtx->WorldViewState.SetCurrent<Standard_Real> (aModViewMat);
490   }
491   theCtx->ApplyWorldViewMatrix();
492
493   if (!myIs2d)
494   {
495     theCtx->ProjectionState.SetCurrent<Standard_Real> (aProjectMat);
496     theCtx->ApplyProjectionMatrix();
497   }
498
499   // Upload updated state to shader program
500   theCtx->ShaderManager()->PushState (theCtx->ActiveProgram());
501 }
502
503 // =======================================================================
504 // function : drawText
505 // purpose  :
506 // =======================================================================
507 void OpenGl_Text::drawText (const Handle(OpenGl_Context)& theCtx,
508                             const OpenGl_AspectText&      theTextAspect) const
509 {
510   (void )theTextAspect;
511   if (myVertsVbo.Length() != myTextures.Length()
512    || myTextures.IsEmpty())
513   {
514     return;
515   }
516
517   for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
518   {
519     const GLuint aTexId = myTextures.Value (anIter);
520     glBindTexture (GL_TEXTURE_2D, aTexId);
521
522     const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
523     const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
524     aVerts->BindAttribute (theCtx, Graphic3d_TOA_POS);
525     aTCrds->BindAttribute (theCtx, Graphic3d_TOA_UV);
526
527     glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
528
529     aTCrds->UnbindAttribute (theCtx, Graphic3d_TOA_UV);
530     aVerts->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
531   }
532   glBindTexture (GL_TEXTURE_2D, 0);
533 }
534
535 // =======================================================================
536 // function : FontKey
537 // purpose  :
538 // =======================================================================
539 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
540                                               const Standard_Integer   theHeight,
541                                               const unsigned int       theResolution)
542 {
543   const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
544                                  ? theAspect.Aspect()->GetTextFontAspect()
545                                  : Font_FA_Regular;
546   return theAspect.Aspect()->Font()
547        + TCollection_AsciiString(":") + Standard_Integer(anAspect)
548        + TCollection_AsciiString(":") + Standard_Integer(theResolution)
549        + TCollection_AsciiString(":") + theHeight;
550 }
551
552 // =======================================================================
553 // function : FindFont
554 // purpose  :
555 // =======================================================================
556 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
557                                            const OpenGl_AspectText&      theAspect,
558                                            const Standard_Integer        theHeight,
559                                            const unsigned int            theResolution,
560                                            const TCollection_AsciiString theKey)
561 {
562   Handle(OpenGl_Font) aFont;
563   if (theHeight < 2)
564   {
565     return aFont; // invalid parameters
566   }
567
568   if (!theCtx->GetResource (theKey, aFont))
569   {
570     Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
571     const Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (theAspect.Aspect()->Font());
572     const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
573                                    ? theAspect.Aspect()->GetTextFontAspect()
574                                    : Font_FA_Regular;
575     Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect, theHeight);
576     Handle(Font_FTFont) aFontFt;
577     if (!aRequestedFont.IsNull())
578     {
579       aFontFt = new Font_FTFont (Handle(Font_FTLibrary)());
580
581       if (aFontFt->Init (aRequestedFont->FontPath()->ToCString(), theHeight, theResolution))
582       {
583         aFont = new OpenGl_Font (aFontFt, theKey);
584         if (!aFont->Init (theCtx))
585         {
586           TCollection_ExtendedString aMsg;
587           aMsg += "Font '";
588           aMsg += theAspect.Aspect()->Font();
589           aMsg += "' - initialization of GL resources has failed!";
590           theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
591           aFontFt.Nullify();
592           aFont->Release (theCtx.operator->());
593           aFont = new OpenGl_Font (aFontFt, theKey);
594         }
595       }
596       else
597       {
598         TCollection_ExtendedString aMsg;
599         aMsg += "Font '";
600         aMsg += theAspect.Aspect()->Font();
601         aMsg += "' is broken or has incompatible format! File path: ";
602         aMsg += aRequestedFont->FontPath()->ToCString();
603         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
604         aFontFt.Nullify();
605         aFont = new OpenGl_Font (aFontFt, theKey);
606       }
607     }
608     else
609     {
610       TCollection_ExtendedString aMsg;
611       aMsg += "Font '";
612       aMsg += theAspect.Aspect()->Font();
613       aMsg += "' is not found in the system!";
614       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
615       aFont = new OpenGl_Font (aFontFt, theKey);
616     }
617
618     theCtx->ShareResource (theKey, aFont);
619   }
620   return aFont;
621 }
622
623 // =======================================================================
624 // function : drawRect
625 // purpose  :
626 // =======================================================================
627 void OpenGl_Text::drawRect (const Handle(OpenGl_Context)& theCtx,
628                             const OpenGl_AspectText&      theTextAspect,
629                             const OpenGl_Vec4&            theColorSubs) const
630 {
631   Handle(OpenGl_ShaderProgram) aPrevProgram = theCtx->ActiveProgram();
632   if (myBndVertsVbo.IsNull())
633   {
634     OpenGl_Vec2 aQuad[4] =
635     {
636       OpenGl_Vec2(myBndBox.Right, myBndBox.Bottom),
637       OpenGl_Vec2(myBndBox.Right, myBndBox.Top),
638       OpenGl_Vec2(myBndBox.Left,  myBndBox.Bottom),
639       OpenGl_Vec2(myBndBox.Left,  myBndBox.Top)
640     };
641     if (theCtx->ToUseVbo())
642     {
643       myBndVertsVbo = new OpenGl_VertexBuffer();
644     }
645     else
646     {
647       myBndVertsVbo = new OpenGl_VertexBufferCompat();
648     }
649     myBndVertsVbo->Init (theCtx, 2, 4, aQuad[0].GetData());
650   }
651
652   // bind unlit program
653   theCtx->ShaderManager()->BindFaceProgram (Handle(OpenGl_TextureSet)(), Graphic3d_TOSM_UNLIT,
654                                             Graphic3d_AlphaMode_Opaque, Standard_False, Standard_False,
655                                             Handle(OpenGl_ShaderProgram)());
656
657 #if !defined(GL_ES_VERSION_2_0)
658   if (theCtx->core11 != NULL
659    && theCtx->ActiveProgram().IsNull())
660   {
661     glBindTexture (GL_TEXTURE_2D, 0);
662   }
663 #endif
664   theCtx->SetColor4fv (theColorSubs);
665   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
666   myBndVertsVbo->BindAttribute (theCtx, Graphic3d_TOA_POS);
667
668   theCtx->core20fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
669
670   myBndVertsVbo->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
671   theCtx->BindProgram (aPrevProgram);
672 }
673
674 // =======================================================================
675 // function : render
676 // purpose  :
677 // =======================================================================
678 void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx,
679                           const OpenGl_AspectText&      theTextAspect,
680                           const OpenGl_Vec4&            theColorText,
681                           const OpenGl_Vec4&            theColorSubs,
682                           const unsigned int            theResolution) const
683 {
684   if (myString.IsEmpty())
685   {
686     return;
687   }
688
689   // Note that using difference resolution in different Views in same Viewer
690   // will lead to performance regression (for example, text will be recreated every time).
691   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height, theResolution);
692   if (!myFont.IsNull()
693    && !myFont->ResourceKey().IsEqual (aFontKey))
694   {
695     // font changed
696     const_cast<OpenGl_Text* > (this)->Release (theCtx.operator->());
697   }
698
699   if (myFont.IsNull())
700   {
701     myFont = FindFont (theCtx, theTextAspect, myParams.Height, theResolution, aFontKey);
702   }
703   if (!myFont->WasInitialized())
704   {
705     return;
706   }
707
708   if (myTextures.IsEmpty())
709   {
710     Font_TextFormatter aFormatter;
711     aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
712     aFormatter.Reset();
713
714     aFormatter.Append (myString, *myFont->FTFont().operator->());
715     aFormatter.Format();
716
717     OpenGl_TextBuilder aBuilder;
718     aBuilder.Perform (aFormatter,
719                       theCtx,
720                       *myFont.operator->(),
721                       myTextures,
722                       myVertsVbo,
723                       myTCrdsVbo);
724
725     aFormatter.BndBox (myBndBox);
726     if (!myBndVertsVbo.IsNull())
727     {
728       myBndVertsVbo->Release (theCtx.get());
729       myBndVertsVbo.Nullify();
730     }
731   }
732
733   if (myTextures.IsEmpty())
734   {
735     return;
736   }
737
738   myExportHeight = 1.0f;
739   myScaleHeight  = 1.0f;
740
741   theCtx->WorldViewState.Push();
742   myModelMatrix.Convert (theCtx->WorldViewState.Current() * theCtx->ModelWorldState.Current());
743
744   const GLdouble aPointSize = (GLdouble )myFont->FTFont()->PointSize();
745   if (!myIs2d)
746   {
747     Graphic3d_TransformUtils::Project<Standard_Real> (myPoint.x(), myPoint.y(), myPoint.z(),
748                                                       myModelMatrix, myProjMatrix, theCtx->Viewport(),
749                                                       myWinX, myWinY, myWinZ);
750
751     // compute scale factor for constant text height
752     if (theTextAspect.Aspect()->GetTextZoomable())
753     {
754       myExportHeight = aPointSize;
755     }
756     else
757     {
758       Graphic3d_Vec3d aPnt1, aPnt2;
759       Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX, myWinY, myWinZ,
760                                                           OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX), myProjMatrix, theCtx->Viewport(),
761                                                           aPnt1.x(), aPnt1.y(), aPnt1.z());
762       Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX, myWinY + aPointSize, myWinZ,
763                                                           OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX), myProjMatrix, theCtx->Viewport(),
764                                                           aPnt2.x(), aPnt2.y(), aPnt2.z());
765       myScaleHeight = (aPnt2.y() - aPnt1.y()) / aPointSize;
766     }
767   }
768   myExportHeight = aPointSize / myExportHeight;
769
770 #if !defined(GL_ES_VERSION_2_0)
771   if (theCtx->core11 != NULL
772    && theCtx->caps->ffpEnable)
773   {
774     glDisable (GL_LIGHTING);
775   }
776
777   const Standard_Integer aPrevPolygonMode  = theCtx->SetPolygonMode (GL_FILL);
778   const bool             aPrevHatchingMode = theCtx->SetPolygonHatchEnabled (false);
779 #endif
780
781   // setup depth test
782   const bool hasDepthTest = !myIs2d
783                          && theTextAspect.Aspect()->Style() != Aspect_TOST_ANNOTATION;
784   if (!hasDepthTest)
785   {
786     glDisable (GL_DEPTH_TEST);
787   }
788
789   if (theCtx->core15fwd != NULL)
790   {
791     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0);
792   }
793 #if !defined(GL_ES_VERSION_2_0)
794   // activate texture unit
795   GLint aTexEnvParam = GL_REPLACE;
796   if (theCtx->core11 != NULL)
797   {
798     glDisable (GL_TEXTURE_1D);
799     glEnable  (GL_TEXTURE_2D);
800     glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
801     if (aTexEnvParam != GL_REPLACE)
802     {
803       glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
804     }
805   }
806 #endif
807
808   // setup blending
809   glEnable (GL_BLEND);
810   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
811   const bool anAlphaToCoverageOld = theCtx->SetSampleAlphaToCoverage (false);
812
813   // extra drawings
814   switch (theTextAspect.Aspect()->DisplayType())
815   {
816     case Aspect_TODT_BLEND:
817     {
818     #if !defined(GL_ES_VERSION_2_0)
819       glEnable  (GL_COLOR_LOGIC_OP);
820       glLogicOp (GL_XOR);
821     #endif
822       break;
823     }
824     case Aspect_TODT_SUBTITLE:
825     {
826       BackPolygonOffsetSentry aPolygonOffsetTmp (hasDepthTest ? theCtx : Handle(OpenGl_Context)());
827       drawRect (theCtx, theTextAspect, theColorSubs);
828       break;
829     }
830     case Aspect_TODT_DEKALE:
831     {
832       BackPolygonOffsetSentry aPolygonOffsetTmp (hasDepthTest ? theCtx : Handle(OpenGl_Context)());
833       theCtx->SetColor4fv (theColorSubs);
834       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.0f));
835       drawText    (theCtx, theTextAspect);
836       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.0f));
837       drawText    (theCtx, theTextAspect);
838       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.0f));
839       drawText    (theCtx, theTextAspect);
840       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.0f));
841       drawText    (theCtx, theTextAspect);
842       break;
843     }
844     case Aspect_TODT_SHADOW:
845     {
846       BackPolygonOffsetSentry aPolygonOffsetTmp (hasDepthTest ? theCtx : Handle(OpenGl_Context)());
847       theCtx->SetColor4fv (theColorSubs);
848       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.0f));
849       drawText    (theCtx, theTextAspect);
850       break;
851     }
852     case Aspect_TODT_DIMENSION:
853     case Aspect_TODT_NORMAL:
854     {
855       break;
856     }
857   }
858
859   // main draw call
860   theCtx->SetColor4fv (theColorText);
861   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
862   drawText    (theCtx, theTextAspect);
863
864   if (!myIs2d)
865   {
866     theCtx->ProjectionState.SetCurrent<Standard_Real> (myProjMatrix);
867     theCtx->ApplyProjectionMatrix();
868   }
869
870 #if !defined(GL_ES_VERSION_2_0)
871   if (theCtx->core11 != NULL)
872   {
873     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
874   }
875 #endif
876
877   if (theTextAspect.Aspect()->DisplayType() == Aspect_TODT_DIMENSION)
878   {
879     glDisable (GL_BLEND);
880     if (!myIs2d)
881     {
882       glDisable (GL_DEPTH_TEST);
883     }
884   #if !defined(GL_ES_VERSION_2_0)
885     if (theCtx->core11 != NULL)
886     {
887       glDisable (GL_TEXTURE_2D);
888     }
889   #endif
890     const bool aColorMaskBack = theCtx->SetColorMask (false);
891
892     glClear (GL_STENCIL_BUFFER_BIT);
893     glEnable (GL_STENCIL_TEST);
894     glStencilFunc (GL_ALWAYS, 1, 0xFF);
895     glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
896
897     drawRect (theCtx, theTextAspect, OpenGl_Vec4 (1.0f, 1.0f, 1.0f, 1.0f));
898
899     glStencilFunc (GL_ALWAYS, 0, 0xFF);
900
901     theCtx->SetColorMask (aColorMaskBack);
902   }
903
904   // reset OpenGL state
905   glDisable (GL_BLEND);
906   glDisable (GL_STENCIL_TEST);
907 #if !defined(GL_ES_VERSION_2_0)
908   glDisable (GL_COLOR_LOGIC_OP);
909
910   theCtx->SetPolygonMode         (aPrevPolygonMode);
911   theCtx->SetPolygonHatchEnabled (aPrevHatchingMode);
912 #endif
913   theCtx->SetSampleAlphaToCoverage (anAlphaToCoverageOld);
914
915   // model view matrix was modified
916   theCtx->WorldViewState.Pop();
917   theCtx->ApplyModelViewMatrix();
918 }