Visualization Library v1.0.3A lightweight C++ OpenGL middleware for 2D/3D graphics |
[Download] [Tutorials] [All Classes] [Grouped Classes] |
00001 /**************************************************************************************/ 00002 /* */ 00003 /* Visualization Library */ 00004 /* http://visualizationlibrary.org */ 00005 /* */ 00006 /* Copyright (c) 2005-2010, Michele Bosi */ 00007 /* All rights reserved. */ 00008 /* */ 00009 /* Redistribution and use in source and binary forms, with or without modification, */ 00010 /* are permitted provided that the following conditions are met: */ 00011 /* */ 00012 /* - Redistributions of source code must retain the above copyright notice, this */ 00013 /* list of conditions and the following disclaimer. */ 00014 /* */ 00015 /* - Redistributions in binary form must reproduce the above copyright notice, this */ 00016 /* list of conditions and the following disclaimer in the documentation and/or */ 00017 /* other materials provided with the distribution. */ 00018 /* */ 00019 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */ 00020 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */ 00021 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */ 00022 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */ 00023 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */ 00024 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */ 00025 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ 00026 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ 00027 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */ 00028 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 00029 /* */ 00030 /**************************************************************************************/ 00031 00032 #include <vlGraphics/Text.hpp> 00033 #include <vlGraphics/OpenGLContext.hpp> 00034 #include <vlGraphics/Actor.hpp> 00035 #include <vlCore/Log.hpp> 00036 00037 #include "ft2build.h" 00038 #include FT_FREETYPE_H 00039 00040 using namespace vl; 00041 00042 //----------------------------------------------------------------------------- 00043 void Text::render_Implementation(const Actor* actor, const Shader*, const Camera* camera, OpenGLContext* gl_context) const 00044 { 00045 gl_context->bindVAS(NULL, false, false); 00046 00047 VL_CHECK(font()) 00048 00049 if (!font() || !font()->mFT_Face) 00050 return; 00051 00052 if ( text().empty() ) 00053 return; 00054 00055 // Lighting can be enabled or disabled. 00056 // glDisable(GL_LIGHTING); 00057 00058 // Blending must be enabled explicity by the vl::Shader, also to perform z-sort. 00059 // glEnable(GL_BLEND); 00060 00061 // Trucchetto che usiamo per evitare z-fighting: 00062 // Pass #1 - fill color and stencil 00063 // - disable depth write mask 00064 // - depth test can be enabled or not by the user 00065 // - depth func can be choosen by the user 00066 // - render in the order: background, border, shadow, outline, text 00067 // Pass #2 - fill z-buffer 00068 // - enable depth write mask 00069 // - disable color mask 00070 // - disable stencil 00071 // - drawing background and border 00072 00073 // Pass #1 00074 00075 // disable z-writing 00076 GLboolean depth_mask=0; 00077 glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask); 00078 glDepthMask(GL_FALSE); 00079 00080 // background 00081 if (backgroundEnabled()) 00082 renderBackground( actor, camera ); 00083 00084 // border 00085 if (borderEnabled()) 00086 renderBorder( actor, camera ); 00087 00088 // to have the most correct results we should render the text twice one for color and stencil, the other for the z-buffer 00089 00090 // shadow render 00091 if (shadowEnabled()) 00092 renderText( actor, camera, shadowColor(), shadowVector() ); 00093 // outline render 00094 if (outlineEnabled()) 00095 { 00096 renderText( actor, camera, outlineColor(), fvec2(-1,0) ); 00097 renderText( actor, camera, outlineColor(), fvec2(+1,0) ); 00098 renderText( actor, camera, outlineColor(), fvec2(0,-1) ); 00099 renderText( actor, camera, outlineColor(), fvec2(0,+1) ); 00100 } 00101 // text render 00102 renderText( actor, camera, color(), fvec2(0,0) ); 00103 00104 // Pass #2 00105 // fills the z-buffer (not the stencil buffer): approximated to the text bbox 00106 00107 // restores depth mask 00108 glDepthMask(depth_mask); 00109 00110 if (depth_mask) 00111 { 00112 // disables writing to the color buffer 00113 GLboolean color_mask[4]; 00114 glGetBooleanv(GL_COLOR_WRITEMASK, color_mask); 00115 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 00116 00117 // disable writing to the stencil buffer 00118 int stencil_front_mask=0; 00119 glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_front_mask); 00120 int stencil_back_mask=0; 00121 if (Has_GL_Version_2_0) 00122 glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &stencil_back_mask); 00123 glStencilMask(0); 00124 00125 // background 00126 renderBackground( actor, camera ); 00127 00128 // border 00129 renderBorder( actor, camera ); 00130 00131 // restores color writing 00132 glColorMask(color_mask[0],color_mask[1],color_mask[2],color_mask[3]); 00133 00134 // restore the stencil masks 00135 glStencilMask(stencil_front_mask); 00136 if (Has_GL_Version_2_0) 00137 glStencilMaskSeparate(GL_BACK, stencil_back_mask); 00138 } 00139 00140 // restore the right color and normal since we changed them 00141 glColor4fv( gl_context->color().ptr() ); 00142 glNormal3fv( gl_context->normal().ptr() ); 00143 } 00144 //----------------------------------------------------------------------------- 00145 void Text::renderText(const Actor* actor, const Camera* camera, const fvec4& color, const fvec2& offset) const 00146 { 00147 if(!mFont) 00148 { 00149 Log::error("Text::renderText() error: no Font assigned to the Text object.\n"); 00150 VL_TRAP() 00151 return; 00152 } 00153 00154 if (!font()->mFT_Face) 00155 { 00156 Log::error("Text::renderText() error: invalid FT_Face: probably you tried to load an unsupported font format.\n"); 00157 VL_TRAP() 00158 return; 00159 } 00160 00161 int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() }; 00162 00163 if (viewport[2] < 1) viewport[2] = 1; 00164 if (viewport[3] < 1) viewport[3] = 1; 00165 00166 // note that we only save and restore the server side states 00167 00168 if (mode() == Text2D) 00169 { 00170 glMatrixMode(GL_MODELVIEW); 00171 glPushMatrix(); 00172 glLoadIdentity(); 00173 VL_CHECK_OGL(); 00174 00175 glMatrixMode(GL_PROJECTION); 00176 glPushMatrix(); 00177 // glLoadIdentity(); 00178 // gluOrtho2D( -0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f ); 00179 00180 // clever trick part #1 00181 fmat4 mat = fmat4::getOrtho(-0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f, -1, +1); 00182 mat.e(2,2) = 1.0f; // preserve the z value from the incoming vertex. 00183 mat.e(2,3) = 0.0f; 00184 glLoadMatrixf(mat.ptr()); 00185 00186 VL_CHECK_OGL(); 00187 } 00188 00189 AABB rbbox = rawboundingRect( text() ); // for text alignment 00190 VL_CHECK(rbbox.maxCorner().z() == 0) 00191 VL_CHECK(rbbox.minCorner().z() == 0) 00192 AABB bbox = rbbox; 00193 int applied_margin = backgroundEnabled() || borderEnabled() ? margin() : 0; 00194 bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*applied_margin,2.0f*applied_margin,0) ); 00195 VL_CHECK(bbox.maxCorner().z() == 0) 00196 VL_CHECK(bbox.minCorner().z() == 0) 00197 00198 // basic render states 00199 00200 fvec2 pen(0,0); 00201 00202 float texc[] = { 0,0, 0,0, 0,0, 0,0 }; 00203 VL_glActiveTexture( GL_TEXTURE0 ); 00204 glEnable(GL_TEXTURE_2D); 00205 VL_glClientActiveTexture( GL_TEXTURE0 ); 00206 glEnableClientState( GL_TEXTURE_COORD_ARRAY ); 00207 glTexCoordPointer(2, GL_FLOAT, 0, texc); 00208 00209 // Constant color 00210 glColor4f( color.r(), color.g(), color.b(), color.a() ); 00211 00212 // Constant normal 00213 glNormal3f( 0, 0, 1 ); 00214 00215 fvec3 vect[4]; 00216 glEnableClientState( GL_VERTEX_ARRAY ); 00217 glVertexPointer(3, GL_FLOAT, 0, vect[0].ptr()); 00218 00219 FT_Long has_kerning = FT_HAS_KERNING( font()->mFT_Face ); 00220 FT_UInt previous = 0; 00221 00222 // viewport alignment 00223 fmat4 m = mMatrix; 00224 00225 int w = camera->viewport()->width(); 00226 int h = camera->viewport()->height(); 00227 00228 if (w < 1) w = 1; 00229 if (h < 1) h = 1; 00230 00231 if ( !(actor && actor->transform()) && mode() == Text2D ) 00232 { 00233 if (viewportAlignment() & AlignHCenter) 00234 { 00235 VL_CHECK( !(viewportAlignment() & AlignRight) ) 00236 VL_CHECK( !(viewportAlignment() & AlignLeft) ) 00237 // vect[i].x() += int((viewport[2]-1.0f) / 2.0f); 00238 m.translate( (float)int((w-1.0f) / 2.0f), 0, 0); 00239 } 00240 00241 if (viewportAlignment() & AlignRight) 00242 { 00243 VL_CHECK( !(viewportAlignment() & AlignHCenter) ) 00244 VL_CHECK( !(viewportAlignment() & AlignLeft) ) 00245 // vect[i].x() += int(viewport[2]-1.0f); 00246 m.translate( (float)int(w-1.0f), 0, 0); 00247 } 00248 00249 if (viewportAlignment() & AlignTop) 00250 { 00251 VL_CHECK( !(viewportAlignment() & AlignBottom) ) 00252 VL_CHECK( !(viewportAlignment() & AlignVCenter) ) 00253 // vect[i].y() += int(viewport[3]-1.0f); 00254 m.translate( 0, (float)int(h-1.0f), 0); 00255 } 00256 00257 if (viewportAlignment() & AlignVCenter) 00258 { 00259 VL_CHECK( !(viewportAlignment() & AlignTop) ) 00260 VL_CHECK( !(viewportAlignment() & AlignBottom) ) 00261 // vect[i].y() += int((viewport[3]-1.0f) / 2.0f); 00262 m.translate( 0, (float)int((h-1.0f) / 2.0f), 0); 00263 } 00264 } 00265 00266 // split the text in different lines 00267 00268 VL_CHECK(text().length()) 00269 00270 std::vector< String > lines; 00271 lines.push_back( String() ); 00272 for(int i=0; i<text().length(); ++i) 00273 { 00274 if (text()[i] == '\n') 00275 { 00276 // start new line 00277 lines.push_back( String() ); 00278 } 00279 else 00280 lines.back() += text()[i]; 00281 } 00282 00283 for(unsigned iline=0; iline<lines.size(); iline++) 00284 { 00285 // strip spaces at the beginning and at the end of the line 00286 if (textAlignment() == TextAlignJustify) 00287 lines[iline].trim(); 00288 00289 AABB linebox = rawboundingRect( lines[iline] ); 00290 int displace = 0; 00291 int just_space = 0; 00292 int just_remained_space = 0; 00293 int space_count = 0; 00294 for(int c=0; c<(int)lines[iline].length(); c++) 00295 if ( lines[iline][c] == ' ' ) 00296 space_count++; 00297 00298 if (space_count && textAlignment() == TextAlignJustify) 00299 { 00300 just_space = int(rbbox.width() - linebox.width()) / space_count; 00301 just_remained_space = int(rbbox.width() - linebox.width()) % space_count; 00302 } 00303 00304 if (layout() == RightToLeftText) 00305 { 00306 if (textAlignment() == TextAlignRight) 00307 displace = 0; 00308 else 00309 if (textAlignment() == TextAlignLeft) 00310 displace = - int(rbbox.width() - linebox.width()); 00311 else 00312 if (textAlignment() == TextAlignCenter) 00313 displace = - int((rbbox.width() - linebox.width()) / 2.0f); 00314 } 00315 if (layout() == LeftToRightText) 00316 { 00317 if (textAlignment() == TextAlignRight) 00318 displace = int(rbbox.width() - linebox.width()); 00319 else 00320 if (textAlignment() == TextAlignLeft) 00321 displace = 0; 00322 else 00323 if (textAlignment() == TextAlignCenter) 00324 displace = + int((rbbox.width() - linebox.width()) / 2.0f); 00325 } 00326 00327 // this is needed so that empty strings generate empty lines 00328 // note that puttig '\n\n\n\n' at the beginning of a text generates 00329 // a wrong rendering (see it with background box activated). 00330 if (iline != 0 && !lines[iline].length()) 00331 { 00332 pen.y() -= mFont->mHeight; 00333 pen.x() = 0; 00334 } 00335 else 00336 for(int c=0; c<(int)lines[iline].length(); c++) 00337 { 00338 if (c == 0 && iline != 0) 00339 { 00340 pen.y() -= mFont->mHeight; 00341 pen.x() = 0; 00342 } 00343 00344 const Glyph* glyph = mFont->glyph( lines[iline][c] ); 00345 00346 if (!glyph) 00347 continue; 00348 00349 if ( kerningEnabled() && has_kerning && previous && glyph->glyphIndex() ) 00350 { 00351 FT_Vector delta; delta.y = 0; 00352 if (layout() == LeftToRightText) 00353 { 00354 FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta ); 00355 pen.x() += delta.x / 64.0f; 00356 } 00357 else 00358 if (layout() == RightToLeftText) 00359 { 00360 FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta ); 00361 pen.x() -= delta.x / 64.0f; 00362 } 00363 pen.y() += delta.y / 64.0f; 00364 } 00365 previous = glyph->glyphIndex(); 00366 00367 if (glyph->textureHandle()) 00368 { 00369 glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() ); 00370 00371 texc[0] = glyph->s0(); 00372 texc[1] = glyph->t1(); 00373 00374 texc[2] = glyph->s1(); 00375 texc[3] = glyph->t1(); 00376 00377 texc[4] = glyph->s1(); 00378 texc[5] = glyph->t0(); 00379 00380 texc[6] = glyph->s0(); 00381 texc[7] = glyph->t0(); 00382 00383 int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left(); 00384 00385 // triangle strip layout 00386 00387 vect[0].x() = pen.x() + glyph->width()*0 + left -1; 00388 vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00389 00390 vect[1].x() = pen.x() + glyph->width()*1 + left +1; 00391 vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00392 00393 vect[2].x() = pen.x() + glyph->width()*1 + left +1; 00394 vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00395 00396 vect[3].x() = pen.x() + glyph->width()*0 + left -1; 00397 vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00398 00399 if (layout() == RightToLeftText) 00400 { 00401 #if (1) 00402 vect[0].x() -= glyph->width()-1 +2; 00403 vect[1].x() -= glyph->width()-1 +2; 00404 vect[2].x() -= glyph->width()-1 +2; 00405 vect[3].x() -= glyph->width()-1 +2; 00406 #endif 00407 } 00408 00409 vect[0].y() -= mFont->mHeight; 00410 vect[1].y() -= mFont->mHeight; 00411 vect[2].y() -= mFont->mHeight; 00412 vect[3].y() -= mFont->mHeight; 00413 00414 #if (1) 00415 // normalize coordinate orgin to the bottom/left corner 00416 vect[0] -= (fvec3)bbox.minCorner(); 00417 vect[1] -= (fvec3)bbox.minCorner(); 00418 vect[2] -= (fvec3)bbox.minCorner(); 00419 vect[3] -= (fvec3)bbox.minCorner(); 00420 #endif 00421 00422 #if (1) 00423 vect[0].x() += applied_margin + displace; 00424 vect[1].x() += applied_margin + displace; 00425 vect[2].x() += applied_margin + displace; 00426 vect[3].x() += applied_margin + displace; 00427 00428 vect[0].y() += applied_margin; 00429 vect[1].y() += applied_margin; 00430 vect[2].y() += applied_margin; 00431 vect[3].y() += applied_margin; 00432 #endif 00433 00434 // apply offset for outline rendering 00435 vect[0].x() += offset.x(); 00436 vect[0].y() += offset.y(); 00437 vect[1].x() += offset.x(); 00438 vect[1].y() += offset.y(); 00439 vect[2].x() += offset.x(); 00440 vect[2].y() += offset.y(); 00441 vect[3].x() += offset.x(); 00442 vect[3].y() += offset.y(); 00443 00444 // alignment 00445 for(int i=0; i<4; ++i) 00446 { 00447 if (alignment() & AlignHCenter) 00448 { 00449 VL_CHECK( !(alignment() & AlignRight) ) 00450 VL_CHECK( !(alignment() & AlignLeft) ) 00451 vect[i].x() -= (int)(bbox.width() / 2.0f); 00452 } 00453 00454 if (alignment() & AlignRight) 00455 { 00456 VL_CHECK( !(alignment() & AlignHCenter) ) 00457 VL_CHECK( !(alignment() & AlignLeft) ) 00458 vect[i].x() -= (int)bbox.width(); 00459 } 00460 00461 if (alignment() & AlignTop) 00462 { 00463 VL_CHECK( !(alignment() & AlignBottom) ) 00464 VL_CHECK( !(alignment() & AlignVCenter) ) 00465 vect[i].y() -= (int)bbox.height(); 00466 } 00467 00468 if (alignment() & AlignVCenter) 00469 { 00470 VL_CHECK( !(alignment() & AlignTop) ) 00471 VL_CHECK( !(alignment() & AlignBottom) ) 00472 vect[i].y() -= int(bbox.height() / 2.0); 00473 } 00474 } 00475 00476 // apply text transform 00477 vect[0] = m * vect[0]; 00478 vect[1] = m * vect[1]; 00479 vect[2] = m * vect[2]; 00480 vect[3] = m * vect[3]; 00481 00482 // actor's transform following in Text2D 00483 if ( actor->transform() && mode() == Text2D ) 00484 { 00485 vec4 v(0,0,0,1); 00486 v = actor->transform()->worldMatrix() * v; 00487 00488 camera->project(v,v); 00489 00490 // from screen space to viewport space 00491 v.x() -= viewport[0]; 00492 v.y() -= viewport[1]; 00493 00494 v.x() = (float)int(v.x()); 00495 v.y() = (float)int(v.y()); 00496 00497 vect[0].x() += (float)v.x(); 00498 vect[0].y() += (float)v.y(); 00499 vect[1].x() += (float)v.x(); 00500 vect[1].y() += (float)v.y(); 00501 vect[2].x() += (float)v.x(); 00502 vect[2].y() += (float)v.y(); 00503 vect[3].x() += (float)v.x(); 00504 vect[3].y() += (float)v.y(); 00505 00506 // clever trick part #2 00507 vect[0].z() = 00508 vect[1].z() = 00509 vect[2].z() = 00510 vect[3].z() = float((v.z() - 0.5f) / 0.5f); 00511 } 00512 00513 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); VL_CHECK_OGL(); 00514 00515 #if (0) 00516 glDisable(GL_TEXTURE_2D); 00517 glColor3fv(vec3(1,0,0).ptr()); 00518 glDrawArrays(GL_LINE_LOOP, 0, 4); 00519 glColor4fv(color.ptr()); 00520 glEnable(GL_TEXTURE_2D); 00521 #endif 00522 } 00523 00524 if (just_space && lines[iline][c] == ' ' && iline != lines.size()-1) 00525 { 00526 if (layout() == LeftToRightText) 00527 { 00528 pen.x() += just_space + (just_remained_space?1:0); 00529 // pen.y() += glyph->advance().y(); 00530 } 00531 else 00532 if (layout() == RightToLeftText) 00533 { 00534 pen.x() -= just_space + (just_remained_space?1:0); 00535 // pen.y() -= glyph->advance().y(); 00536 } 00537 if(just_remained_space) 00538 just_remained_space--; 00539 } 00540 00541 if (layout() == LeftToRightText) 00542 { 00543 pen.x() += glyph->advance().x(); 00544 // pen.y() += glyph->advance().y(); 00545 } 00546 else 00547 if (layout() == RightToLeftText) 00548 { 00549 pen.x() -= glyph->advance().x(); 00550 // pen.y() -= glyph->advance().y(); 00551 } 00552 00553 } 00554 } 00555 00556 glDisableClientState( GL_VERTEX_ARRAY ); VL_CHECK_OGL(); 00557 glDisableClientState( GL_TEXTURE_COORD_ARRAY ); VL_CHECK_OGL(); 00558 00559 VL_CHECK_OGL(); 00560 00561 if (mode() == Text2D) 00562 { 00563 glMatrixMode(GL_MODELVIEW); 00564 glPopMatrix(); VL_CHECK_OGL() 00565 00566 glMatrixMode(GL_PROJECTION); 00567 glPopMatrix(); VL_CHECK_OGL() 00568 } 00569 00570 glDisable(GL_TEXTURE_2D); 00571 glBindTexture(GL_TEXTURE_2D,0); 00572 } 00573 //----------------------------------------------------------------------------- 00574 // returns the raw bounding box of the string, i.e. without alignment, margin and matrix transform. 00575 AABB Text::rawboundingRect(const String& text) const 00576 { 00577 AABB aabb; 00578 00579 if(!font()) 00580 { 00581 Log::error("Text::rawboundingRect() error: no Font assigned to the Text object.\n"); 00582 VL_TRAP() 00583 return aabb; 00584 } 00585 00586 if (!font()->mFT_Face) 00587 { 00588 Log::error("Text::rawboundingRect() error: invalid FT_Face: probably you tried to load an unsupported font format.\n"); 00589 VL_TRAP() 00590 return aabb; 00591 } 00592 00593 fvec2 pen(0,0); 00594 fvec3 vect[4]; 00595 00596 FT_Long has_kerning = FT_HAS_KERNING( font()->mFT_Face ); 00597 FT_UInt previous = 0; 00598 00599 for(int c=0; c<(int)text.length(); c++) 00600 { 00601 if (text[c] == '\n') 00602 { 00603 pen.y() -= mFont->mHeight ? mFont->mHeight : mFont->mSize; 00604 pen.x() = 0; 00605 continue; 00606 } 00607 00608 const ref<Glyph>& glyph = mFont->glyph(text[c]); 00609 00610 // if glyph == NULL there was an error during its creation... 00611 if (glyph.get() == NULL) 00612 continue; 00613 00614 if ( kerningEnabled() && has_kerning && previous && glyph->glyphIndex()) 00615 { 00616 FT_Vector delta; delta.y = 0; 00617 if (layout() == LeftToRightText) 00618 { 00619 FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta ); 00620 pen.x() += delta.x / 64.0f; 00621 } 00622 else 00623 if (layout() == RightToLeftText) 00624 { 00625 FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta ); 00626 pen.x() -= delta.x / 64.0f; 00627 } 00628 pen.y() += delta.y / 64.0f; 00629 } 00630 previous = glyph->glyphIndex(); 00631 00632 if ( glyph->textureHandle() ) 00633 { 00634 int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left(); 00635 00636 vect[0].x() = pen.x() + glyph->width()*0 + left -1; 00637 vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00638 00639 vect[1].x() = pen.x() + glyph->width()*1 + left +1; 00640 vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00641 00642 vect[2].x() = pen.x() + glyph->width()*1 + left +1; 00643 vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00644 00645 vect[3].x() = pen.x() + glyph->width()*0 + left -1; 00646 vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00647 00648 if (layout() == RightToLeftText) 00649 { 00650 #if (1) 00651 vect[0].x() -= glyph->width()-1 +2; 00652 vect[1].x() -= glyph->width()-1 +2; 00653 vect[2].x() -= glyph->width()-1 +2; 00654 vect[3].x() -= glyph->width()-1 +2; 00655 #endif 00656 } 00657 00658 vect[0].y() -= mFont->mHeight; 00659 vect[1].y() -= mFont->mHeight; 00660 vect[2].y() -= mFont->mHeight; 00661 vect[3].y() -= mFont->mHeight; 00662 00663 #if(0) 00664 // apply margin 00665 //if (layout() == LeftToRightText) 00666 //{ 00667 vect[0].x() += margin(); 00668 vect[1].x() += margin(); 00669 vect[2].x() += margin(); 00670 vect[3].x() += margin(); 00671 //} 00672 //else 00673 //if (layout() == RightToLeftText) 00674 //{ 00675 // vect[0].x() -= margin(); 00676 // vect[1].x() -= margin(); 00677 // vect[2].x() -= margin(); 00678 // vect[3].x() -= margin(); 00679 //} 00680 00681 vect[0].y() += margin(); 00682 vect[1].y() += margin(); 00683 vect[2].y() += margin(); 00684 vect[3].y() += margin(); 00685 #endif 00686 00687 } 00688 00689 aabb.addPoint( (vec3)vect[0] ); 00690 aabb.addPoint( (vec3)vect[1] ); 00691 aabb.addPoint( (vec3)vect[2] ); 00692 aabb.addPoint( (vec3)vect[3] ); 00693 00694 if (layout() == LeftToRightText) 00695 pen += glyph->advance(); 00696 else 00697 if (layout() == RightToLeftText) 00698 pen -= glyph->advance(); 00699 } 00700 00701 return aabb; 00702 } 00703 //----------------------------------------------------------------------------- 00704 void Text::renderBackground(const Actor* actor, const Camera* camera) const 00705 { 00706 int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() }; 00707 00708 if (viewport[2] < 1) viewport[2] = 1; 00709 if (viewport[3] < 1) viewport[3] = 1; 00710 00711 if (mode() == Text2D) 00712 { 00713 glMatrixMode(GL_MODELVIEW); 00714 glPushMatrix(); 00715 glLoadIdentity(); 00716 VL_CHECK_OGL(); 00717 00718 glMatrixMode(GL_PROJECTION); 00719 glPushMatrix(); 00720 //glLoadIdentity(); 00721 //gluOrtho2D( -0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f ); 00722 00723 // clever trick part #1 00724 fmat4 mat = fmat4::getOrtho(-0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f, -1, +1); 00725 mat.e(2,2) = 1.0f; 00726 mat.e(2,3) = 0.0f; 00727 glLoadMatrixf(mat.ptr()); 00728 VL_CHECK_OGL(); 00729 } 00730 00731 // Constant color 00732 glColor4f(mBackgroundColor.r(),mBackgroundColor.g(), mBackgroundColor.b(), mBackgroundColor.a()); 00733 00734 // Constant normal 00735 glNormal3f(0, 0, 1); 00736 00737 vec3 a,b,c,d; 00738 boundingRectTransformed( a, b, c, d, camera, mode() == Text2D ? actor : NULL ); 00739 fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d }; 00740 glEnableClientState( GL_VERTEX_ARRAY ); 00741 glVertexPointer(3, GL_FLOAT, 0, vect); 00742 00743 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 00744 00745 glDisableClientState( GL_VERTEX_ARRAY ); 00746 00747 if (mode() == Text2D) 00748 { 00749 glMatrixMode(GL_MODELVIEW); 00750 glPopMatrix(); VL_CHECK_OGL() 00751 00752 glMatrixMode(GL_PROJECTION); 00753 glPopMatrix(); VL_CHECK_OGL() 00754 } 00755 } 00756 //----------------------------------------------------------------------------- 00757 void Text::renderBorder(const Actor* actor, const Camera* camera) const 00758 { 00759 int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() }; 00760 00761 if (viewport[2] < 1) viewport[2] = 1; 00762 if (viewport[3] < 1) viewport[3] = 1; 00763 00764 if (mode() == Text2D) 00765 { 00766 glMatrixMode(GL_MODELVIEW); 00767 glPushMatrix(); 00768 glLoadIdentity(); 00769 VL_CHECK_OGL(); 00770 00771 glMatrixMode(GL_PROJECTION); 00772 glPushMatrix(); 00773 //glLoadIdentity(); 00774 //gluOrtho2D( -0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f ); 00775 00776 // clever trick part #1 00777 fmat4 mat = fmat4::getOrtho(-0.5f, viewport[2]-0.5f, -0.5f, viewport[3]-0.5f, -1, +1); 00778 mat.e(2,2) = 1.0f; 00779 mat.e(2,3) = 0.0f; 00780 glLoadMatrixf(mat.ptr()); 00781 VL_CHECK_OGL(); 00782 } 00783 00784 // Constant color 00785 glColor4f(mBorderColor.r(), mBorderColor.g(), mBorderColor.b(), mBorderColor.a()); 00786 00787 // Constant normal 00788 glNormal3f( 0, 0, 1 ); 00789 00790 vec3 a,b,c,d; 00791 boundingRectTransformed( a, b, c, d, camera, mode() == Text2D ? actor : NULL ); 00792 fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d }; 00793 glEnableClientState( GL_VERTEX_ARRAY ); 00794 glVertexPointer(3, GL_FLOAT, 0, vect); 00795 00796 glDrawArrays(GL_LINE_LOOP, 0, 4); 00797 00798 glDisableClientState( GL_VERTEX_ARRAY ); 00799 00800 if (mode() == Text2D) 00801 { 00802 glMatrixMode(GL_MODELVIEW); 00803 glPopMatrix(); VL_CHECK_OGL() 00804 00805 glMatrixMode(GL_PROJECTION); 00806 glPopMatrix(); VL_CHECK_OGL() 00807 } 00808 } 00809 //----------------------------------------------------------------------------- 00812 AABB Text::boundingRect() const 00813 { 00814 return boundingRect(text()); 00815 } 00816 //----------------------------------------------------------------------------- 00817 AABB Text::boundingRect(const String& text) const 00818 { 00819 int applied_margin = backgroundEnabled() || borderEnabled() ? margin() : 0; 00820 AABB bbox = rawboundingRect( text ); 00821 bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*applied_margin,2.0f*applied_margin,0) ); 00822 00823 // normalize coordinate orgin to the bottom/left corner 00824 vec3 min = bbox.minCorner() - bbox.minCorner(); 00825 vec3 max = bbox.maxCorner() - bbox.minCorner(); 00826 00827 // normalize coordinate orgin to the bottom/left corner 00828 00829 // alignment 00830 00831 if (alignment() & AlignHCenter) 00832 { 00833 VL_CHECK( !(alignment() & AlignRight) ) 00834 VL_CHECK( !(alignment() & AlignLeft) ) 00835 min.x() -= int(bbox.width() / 2.0); 00836 max.x() -= int(bbox.width() / 2.0); 00837 } 00838 00839 if (alignment() & AlignRight) 00840 { 00841 VL_CHECK( !(alignment() & AlignHCenter) ) 00842 VL_CHECK( !(alignment() & AlignLeft) ) 00843 min.x() -= (int)bbox.width(); 00844 max.x() -= (int)bbox.width(); 00845 } 00846 00847 if (alignment() & AlignTop) 00848 { 00849 VL_CHECK( !(alignment() & AlignBottom) ) 00850 VL_CHECK( !(alignment() & AlignVCenter) ) 00851 min.y() -= (int)bbox.height(); 00852 max.y() -= (int)bbox.height(); 00853 } 00854 00855 if (alignment() & AlignVCenter) 00856 { 00857 VL_CHECK( !(alignment() & AlignTop) ) 00858 VL_CHECK( !(alignment() & AlignBottom) ) 00859 min.y() -= int(bbox.height() / 2.0); 00860 max.y() -= int(bbox.height() / 2.0); 00861 } 00862 00863 // no matrix transform applied 00864 // ... 00865 00866 // no actor's transform applied 00867 // ... 00868 00869 AABB aabb; 00870 aabb.setMinCorner(min); 00871 aabb.setMaxCorner(max); 00872 return aabb; 00873 } 00874 //----------------------------------------------------------------------------- 00888 AABB Text::boundingRectTransformed(const Camera* camera, const Actor* actor) const 00889 { 00890 vec3 a, b, c, d; 00891 return boundingRectTransformed(a, b, c, d, camera, actor); 00892 } 00893 //----------------------------------------------------------------------------- 00894 AABB Text::boundingRectTransformed(vec3& a, vec3& b, vec3& c, vec3& d, const Camera* camera, const Actor* actor) const 00895 { 00896 AABB bbox = boundingRect(); 00897 00898 a = bbox.minCorner(); 00899 b.x() = (float)bbox.maxCorner().x(); 00900 b.y() = (float)bbox.minCorner().y(); 00901 c = bbox.maxCorner(); 00902 d.x() = (float)bbox.minCorner().x(); 00903 d.y() = (float)bbox.maxCorner().y(); 00904 // set z to 0 00905 a.z() = b.z() = c.z() = d.z() = 0; 00906 00907 // viewport alignment 00908 fmat4 m = mMatrix; 00909 00910 int w = camera->viewport()->width(); 00911 int h = camera->viewport()->height(); 00912 00913 if (w < 1) w = 1; 00914 if (h < 1) h = 1; 00915 00916 if ( !(actor && actor->transform()) && mode() == Text2D ) 00917 { 00918 if (viewportAlignment() & AlignHCenter) 00919 { 00920 VL_CHECK( !(viewportAlignment() & AlignRight) ) 00921 VL_CHECK( !(viewportAlignment() & AlignLeft) ) 00922 // vect[i].x() += int((viewport[2]-1.0f) / 2.0f); 00923 m.translate( (float)int((w-1.0f) / 2.0f), 0, 0); 00924 } 00925 00926 if (viewportAlignment() & AlignRight) 00927 { 00928 VL_CHECK( !(viewportAlignment() & AlignHCenter) ) 00929 VL_CHECK( !(viewportAlignment() & AlignLeft) ) 00930 // vect[i].x() += int(viewport[2]-1.0f); 00931 m.translate( (float)int(w-1.0f), 0, 0); 00932 } 00933 00934 if (viewportAlignment() & AlignTop) 00935 { 00936 VL_CHECK( !(viewportAlignment() & AlignBottom) ) 00937 VL_CHECK( !(viewportAlignment() & AlignVCenter) ) 00938 // vect[i].y() += int(viewport[3]-1.0f); 00939 m.translate( 0, (float)int(h-1.0f), 0); 00940 } 00941 00942 if (viewportAlignment() & AlignVCenter) 00943 { 00944 VL_CHECK( !(viewportAlignment() & AlignTop) ) 00945 VL_CHECK( !(viewportAlignment() & AlignBottom) ) 00946 // vect[i].y() += int((viewport[3]-1.0f) / 2.0f); 00947 m.translate( 0, (float)int((h-1.0f) / 2.0f), 0); 00948 } 00949 } 00950 00951 // ??? mix fixme: remove all these castings! 00952 // apply matrix transform 00953 a = (mat4)m * a; 00954 b = (mat4)m * b; 00955 c = (mat4)m * c; 00956 d = (mat4)m * d; 00957 00958 // apply actor's transform 00959 if ( actor && actor->transform() ) 00960 { 00961 if ( mode() == Text3D ) 00962 { 00963 a = actor->transform()->worldMatrix() * a; 00964 b = actor->transform()->worldMatrix() * b; 00965 c = actor->transform()->worldMatrix() * c; 00966 d = actor->transform()->worldMatrix() * d; 00967 } 00968 else 00969 if ( mode() == Text2D ) 00970 { 00971 // transform v 00972 vec4 v(0,0,0,1); 00973 v = actor->transform()->worldMatrix() * v; 00974 00975 // project to screen 00976 camera->project(v,v); 00977 00978 // from screen space to viewport space 00979 int viewport[] = { camera->viewport()->x(), camera->viewport()->y(), camera->viewport()->width(), camera->viewport()->height() }; 00980 v.x() -= viewport[0]; 00981 v.y() -= viewport[1]; 00982 00983 v.x() = (float)int(v.x()); 00984 v.y() = (float)int(v.y()); 00985 00986 a += v.xyz(); 00987 b += v.xyz(); 00988 c += v.xyz(); 00989 d += v.xyz(); 00990 00991 // clever trick part #2 00992 a.z() = 00993 b.z() = 00994 c.z() = 00995 d.z() = (v.z() - 0.5f) / 0.5f; 00996 } 00997 } 00998 00999 bbox.setNull(); 01000 bbox.addPoint(a); 01001 bbox.addPoint(b); 01002 bbox.addPoint(c); 01003 bbox.addPoint(d); 01004 return bbox; 01005 } 01006 //----------------------------------------------------------------------------- 01007 void Text::translate(float x, float y, float z) 01008 { 01009 mMatrix.translate(x,y,z); 01010 } 01011 //----------------------------------------------------------------------------- 01012 void Text::rotate(float degrees, float x, float y, float z) 01013 { 01014 mMatrix.rotate(degrees,x,y,z); 01015 } 01016 //----------------------------------------------------------------------------- 01017 void Text::resetMatrix() 01018 { 01019 mMatrix.setIdentity(); 01020 } 01021 //-----------------------------------------------------------------------------