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