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