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/CoreText.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 // mic fixme: implement me. 00043 00044 // Goals: 00045 // - Run on OpenGL Core/ES. 00046 // - Achieve a much greater rendering speed. 00047 // - In order to do this we might sacrifice some of the usability of vl::Text 00048 00049 // Guidelines: 00050 // - Viewport alignment, text transformation, transform tracking done externally. 00051 // - Does not manipulate the GL_PROJECTION and GL_MODELVIEW matrices. 00052 // - Text geometry and bounding boxes should be pre-computed on text change. 00053 // - Line splitting should not be done at rendering time. 00054 // - Should use only OpenGL Core routines. 00055 // - Should use texture atlases and perform lazy texture binding. 00056 // - Cleaner left to right / right to left text reversing. 00057 // - Outline rendering should use 2 pass with enlarged glyphs instead of 5 passes or precompute an high quality outline texture. 00058 // - Avoid using doubles and floats if possible, use integer and Rect rather floats and AABBs. 00059 00060 //----------------------------------------------------------------------------- 00061 void CoreText::render_Implementation(const Actor* actor, const Shader*, const Camera* camera, OpenGLContext* gl_context) const 00062 { 00063 gl_context->bindVAS(NULL, false, false); 00064 00065 VL_CHECK(font()) 00066 00067 if (!font() || !font()->mFT_Face) 00068 return; 00069 00070 if ( text().empty() ) 00071 return; 00072 00073 // Lighting can be enabled or disabled. 00074 // glDisable(GL_LIGHTING); 00075 00076 // Blending must be enabled explicity by the vl::Shader, also to perform z-sort. 00077 // glEnable(GL_BLEND); 00078 00079 // Trucchetto che usiamo per evitare z-fighting: 00080 // Pass #1 - fill color and stencil 00081 // - disable depth write mask 00082 // - depth test can be enabled or not by the user 00083 // - depth func can be choosen by the user 00084 // - render in the order: background, border, shadow, outline, text 00085 // Pass #2 - fill z-buffer 00086 // - enable depth write mask 00087 // - disable color mask 00088 // - disable stencil 00089 // - drawing background and border 00090 00091 // Pass #1 00092 00093 // disable z-writing 00094 GLboolean depth_mask=0; 00095 glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask); 00096 glDepthMask(GL_FALSE); 00097 00098 // background 00099 if (backgroundEnabled()) 00100 renderBackground( actor, camera ); 00101 00102 // border 00103 if (borderEnabled()) 00104 renderBorder( actor, camera ); 00105 00106 // to have the most correct results we should render the text twice one for color and stencil, the other for the z-buffer 00107 00108 // shadow render 00109 if (shadowEnabled()) 00110 renderText( actor, camera, shadowColor(), shadowVector() ); 00111 // outline render 00112 if (outlineEnabled()) 00113 { 00114 renderText( actor, camera, outlineColor(), fvec2(-1,0) ); 00115 renderText( actor, camera, outlineColor(), fvec2(+1,0) ); 00116 renderText( actor, camera, outlineColor(), fvec2(0,-1) ); 00117 renderText( actor, camera, outlineColor(), fvec2(0,+1) ); 00118 } 00119 // text render 00120 renderText( actor, camera, color(), fvec2(0,0) ); 00121 00122 // Pass #2 00123 // fills the z-buffer (not the stencil buffer): approximated to the text bbox 00124 00125 // restores depth mask 00126 glDepthMask(depth_mask); 00127 00128 if (depth_mask) 00129 { 00130 // disables writing to the color buffer 00131 GLboolean color_mask[4]; 00132 glGetBooleanv(GL_COLOR_WRITEMASK, color_mask); 00133 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 00134 00135 // disable writing to the stencil buffer 00136 int stencil_front_mask=0; 00137 glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_front_mask); 00138 int stencil_back_mask=0; 00139 if (Has_GL_Version_2_0) 00140 glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &stencil_back_mask); 00141 glStencilMask(0); 00142 00143 // background 00144 renderBackground( actor, camera ); 00145 00146 // border 00147 renderBorder( actor, camera ); 00148 00149 // restores color writing 00150 glColorMask(color_mask[0],color_mask[1],color_mask[2],color_mask[3]); 00151 00152 // restore the stencil masks 00153 glStencilMask(stencil_front_mask); 00154 if (Has_GL_Version_2_0) 00155 glStencilMaskSeparate(GL_BACK, stencil_back_mask); 00156 } 00157 } 00158 //----------------------------------------------------------------------------- 00159 void CoreText::renderText(const Actor*, const Camera*, const fvec4& color, const fvec2& offset) const 00160 { 00161 if(!mFont) 00162 { 00163 Log::error("CoreText::renderText() error: no Font assigned to the CoreText object.\n"); 00164 VL_TRAP() 00165 return; 00166 } 00167 00168 if (!font()->mFT_Face) 00169 { 00170 Log::error("CoreText::renderText() error: invalid FT_Face: probably you tried to load an unsupported font format.\n"); 00171 VL_TRAP() 00172 return; 00173 } 00174 00175 // mic fixme: these should be pre-computed when the text is set!!! 00176 00177 AABB rbbox = rawboundingRect( text() ); // for text alignment 00178 VL_CHECK(rbbox.maxCorner().z() == 0) 00179 VL_CHECK(rbbox.minCorner().z() == 0) 00180 AABB bbox = rbbox; 00181 bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*margin(), 2.0f*margin(), 0) ); 00182 VL_CHECK(bbox.maxCorner().z() == 0) 00183 VL_CHECK(bbox.minCorner().z() == 0) 00184 00185 // basic render states 00186 00187 // mic fixme: detect GLSLProgram and use VertexAttribPointer if present. 00188 00189 float texc[] = { 0,0,0,0,0,0,0,0 }; 00190 VL_glActiveTexture( GL_TEXTURE0 ); 00191 VL_glClientActiveTexture( GL_TEXTURE0 ); 00192 glEnable(GL_TEXTURE_2D); 00193 glEnableClientState( GL_TEXTURE_COORD_ARRAY ); 00194 glTexCoordPointer(2, GL_FLOAT, 0, texc); 00195 00196 // color 00197 glColor4fv(color.ptr()); 00198 00199 // Constant normal 00200 glNormal3fv( fvec3(0,0,1).ptr() ); 00201 00202 fvec3 vect[4]; 00203 glEnableClientState( GL_VERTEX_ARRAY ); 00204 glVertexPointer(3, GL_FLOAT, 0, vect[0].ptr()); 00205 00206 FT_Long use_kerning = FT_HAS_KERNING( font()->mFT_Face ); 00207 FT_UInt previous = 0; 00208 00209 fvec2 pen(0,0); 00210 00211 // mic fixme: 00212 // - lines split and lines dimensions (linebox) should be precomputed on text set! 00213 // - or convert this function to generate a pre-computed rendering-list! 00214 00215 // split the text in different lines 00216 00217 VL_CHECK(text().length()) 00218 00219 std::vector< String > lines; 00220 lines.push_back( String() ); 00221 for(int i=0; i<text().length(); ++i) 00222 { 00223 if (text()[i] == '\n') 00224 { 00225 // start new line 00226 lines.push_back( String() ); 00227 } 00228 else 00229 lines.back() += text()[i]; 00230 } 00231 00232 for(unsigned iline=0; iline<lines.size(); iline++) 00233 { 00234 // strip spaces at the beginning and at the end of the line 00235 if (textAlignment() == TextAlignJustify) 00236 lines[iline].trim(); 00237 00238 AABB linebox = rawboundingRect( lines[iline] ); 00239 int horz_text_align = 0; 00240 int just_space = 0; 00241 int just_remained_space = 0; 00242 int space_count = 0; 00243 for(int c=0; c<(int)lines[iline].length(); c++) 00244 if ( lines[iline][c] == ' ' ) 00245 space_count++; 00246 00247 if (space_count && textAlignment() == TextAlignJustify) 00248 { 00249 just_space = int(rbbox.width() - linebox.width()) / space_count; 00250 just_remained_space = int(rbbox.width() - linebox.width()) % space_count; 00251 } 00252 00253 if (layout() == RightToLeftText) 00254 { 00255 if (textAlignment() == TextAlignRight) 00256 horz_text_align = 0; 00257 else 00258 if (textAlignment() == TextAlignLeft) 00259 horz_text_align = - int(rbbox.width() - linebox.width()); 00260 else 00261 if (textAlignment() == TextAlignCenter) 00262 horz_text_align = - int((rbbox.width() - linebox.width()) / 2.0f); 00263 } 00264 if (layout() == LeftToRightText) 00265 { 00266 if (textAlignment() == TextAlignRight) 00267 horz_text_align = int(rbbox.width() - linebox.width()); 00268 else 00269 if (textAlignment() == TextAlignLeft) 00270 horz_text_align = 0; 00271 else 00272 if (textAlignment() == TextAlignCenter) 00273 horz_text_align = + int((rbbox.width() - linebox.width()) / 2.0f); 00274 } 00275 00276 // this is needed so that empty strings generate empty lines 00277 // note that puttig '\n\n\n\n' at the beginning of a text generates 00278 // a wrong rendering (see it with background box activated). 00279 if (iline != 0 && !lines[iline].length()) 00280 { 00281 pen.y() -= mFont->mHeight; 00282 pen.x() = 0; 00283 } 00284 else 00285 for(int c=0; c<(int)lines[iline].length(); c++) 00286 { 00287 if (c == 0 && iline != 0) 00288 { 00289 pen.y() -= mFont->mHeight; 00290 pen.x() = 0; 00291 } 00292 00293 const Glyph* glyph = mFont->glyph( lines[iline][c] ); 00294 00295 if (!glyph) 00296 continue; 00297 00298 if ( kerningEnabled() && use_kerning && previous && glyph->glyphIndex() ) 00299 { 00300 FT_Vector delta; delta.y = 0; 00301 if (layout() == LeftToRightText) 00302 { 00303 FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta ); 00304 pen.x() += delta.x / 64.0f; 00305 } 00306 else 00307 if (layout() == RightToLeftText) 00308 { 00309 FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta ); 00310 pen.x() -= delta.x / 64.0f; 00311 } 00312 pen.y() += delta.y / 64.0f; 00313 } 00314 previous = glyph->glyphIndex(); 00315 00316 if (glyph->textureHandle()) 00317 { 00318 glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() ); 00319 00320 texc[0] = glyph->s0(); 00321 texc[1] = glyph->t1(); 00322 texc[2] = glyph->s1(); 00323 texc[3] = glyph->t1(); 00324 texc[4] = glyph->s1(); 00325 texc[5] = glyph->t0(); 00326 texc[6] = glyph->s0(); 00327 texc[7] = glyph->t0(); 00328 00329 int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left(); 00330 00331 vect[0].x() = pen.x() + glyph->width()*0 + left -1; 00332 vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00333 00334 vect[1].x() = pen.x() + glyph->width()*1 + left +1; 00335 vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00336 00337 vect[2].x() = pen.x() + glyph->width()*1 + left +1; 00338 vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00339 00340 vect[3].x() = pen.x() + glyph->width()*0 + left -1; 00341 vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00342 00343 if (layout() == RightToLeftText) 00344 { 00345 vect[0].x() -= glyph->width()-1 +2; 00346 vect[1].x() -= glyph->width()-1 +2; 00347 vect[2].x() -= glyph->width()-1 +2; 00348 vect[3].x() -= glyph->width()-1 +2; 00349 } 00350 00351 vect[0].y() -= mFont->mHeight; 00352 vect[1].y() -= mFont->mHeight; 00353 vect[2].y() -= mFont->mHeight; 00354 vect[3].y() -= mFont->mHeight; 00355 00356 // normalize coordinate orgin to the bottom/left corner 00357 vect[0] -= (fvec3)bbox.minCorner(); 00358 vect[1] -= (fvec3)bbox.minCorner(); 00359 vect[2] -= (fvec3)bbox.minCorner(); 00360 vect[3] -= (fvec3)bbox.minCorner(); 00361 00362 // margin & horz_text_align 00363 vect[0].x() += margin() + horz_text_align; 00364 vect[0].y() += margin(); 00365 vect[1].x() += margin() + horz_text_align; 00366 vect[1].y() += margin(); 00367 vect[2].x() += margin() + horz_text_align; 00368 vect[2].y() += margin(); 00369 vect[3].x() += margin() + horz_text_align; 00370 vect[3].y() += margin(); 00371 00372 // apply offset for outline rendering 00373 vect[0].x() += offset.x(); 00374 vect[0].y() += offset.y(); 00375 vect[1].x() += offset.x(); 00376 vect[1].y() += offset.y(); 00377 vect[2].x() += offset.x(); 00378 vect[2].y() += offset.y(); 00379 vect[3].x() += offset.x(); 00380 vect[3].y() += offset.y(); 00381 00382 // text pivot 00383 for(int i=0; i<4; ++i) 00384 { 00385 if (textOrigin() & AlignHCenter) 00386 { 00387 VL_CHECK( !(textOrigin() & AlignRight) ) 00388 VL_CHECK( !(textOrigin() & AlignLeft) ) 00389 vect[i].x() -= (int)(bbox.width() / 2.0f); 00390 } 00391 00392 if (textOrigin() & AlignRight) 00393 { 00394 VL_CHECK( !(textOrigin() & AlignHCenter) ) 00395 VL_CHECK( !(textOrigin() & AlignLeft) ) 00396 vect[i].x() -= (int)bbox.width(); 00397 } 00398 00399 if (textOrigin() & AlignTop) 00400 { 00401 VL_CHECK( !(textOrigin() & AlignBottom) ) 00402 VL_CHECK( !(textOrigin() & AlignVCenter) ) 00403 vect[i].y() -= (int)bbox.height(); 00404 } 00405 00406 if (textOrigin() & AlignVCenter) 00407 { 00408 VL_CHECK( !(textOrigin() & AlignTop) ) 00409 VL_CHECK( !(textOrigin() & AlignBottom) ) 00410 vect[i].y() -= int(bbox.height() / 2.0); 00411 } 00412 } 00413 00414 glDrawArrays(GL_QUADS, 0, 4); 00415 00416 #if (0) 00417 glDisable(GL_TEXTURE_2D); 00418 glColor3fv(vec3(1,0,0).ptr()); 00419 glDrawArrays(GL_LINE_LOOP, 0, 4); 00420 glColor4fv(color.ptr()); 00421 glEnable(GL_TEXTURE_2D); 00422 #endif 00423 } 00424 00425 if (just_space && lines[iline][c] == ' ' && iline != lines.size()-1) 00426 { 00427 if (layout() == LeftToRightText) 00428 { 00429 pen.x() += just_space + (just_remained_space?1:0); 00430 // pen.y() += glyph->advance().y(); 00431 } 00432 else 00433 if (layout() == RightToLeftText) 00434 { 00435 pen.x() -= just_space + (just_remained_space?1:0); 00436 // pen.y() -= glyph->advance().y(); 00437 } 00438 if(just_remained_space) 00439 just_remained_space--; 00440 } 00441 00442 if (layout() == LeftToRightText) 00443 { 00444 pen.x() += glyph->advance().x(); 00445 // pen.y() += glyph->advance().y(); 00446 } 00447 else 00448 if (layout() == RightToLeftText) 00449 { 00450 pen.x() -= glyph->advance().x(); 00451 // pen.y() -= glyph->advance().y(); 00452 } 00453 00454 } 00455 } 00456 00457 glDisableClientState( GL_VERTEX_ARRAY ); 00458 glDisableClientState( GL_TEXTURE_COORD_ARRAY ); 00459 00460 VL_CHECK_OGL(); 00461 00462 glDisable(GL_TEXTURE_2D); 00463 glBindTexture(GL_TEXTURE_2D, 0); 00464 } 00465 //----------------------------------------------------------------------------- 00466 // returns the raw bounding box of the string, i.e. without alignment, margin and matrix transform. 00467 AABB CoreText::rawboundingRect(const String& text) const 00468 { 00469 AABB aabb; 00470 00471 if(!font()) 00472 { 00473 Log::error("CoreText::rawboundingRect() error: no Font assigned to the CoreText object.\n"); 00474 VL_TRAP() 00475 return aabb; 00476 } 00477 00478 if (!font()->mFT_Face) 00479 { 00480 Log::error("CoreText::rawboundingRect() error: invalid FT_Face: probably you tried to load an unsupported font format.\n"); 00481 VL_TRAP() 00482 return aabb; 00483 } 00484 00485 fvec2 pen(0,0); 00486 fvec3 vect[4]; 00487 00488 FT_Long use_kerning = FT_HAS_KERNING( font()->mFT_Face ); 00489 FT_UInt previous = 0; 00490 00491 for(int c=0; c<(int)text.length(); c++) 00492 { 00493 if (text[c] == '\n') 00494 { 00495 pen.y() -= mFont->mHeight ? mFont->mHeight : mFont->mSize; 00496 pen.x() = 0; 00497 continue; 00498 } 00499 00500 const ref<Glyph>& glyph = mFont->glyph(text[c]); 00501 00502 // if glyph == NULL there was an error during its creation... 00503 if (glyph.get() == NULL) 00504 continue; 00505 00506 if ( kerningEnabled() && use_kerning && previous && glyph->glyphIndex()) 00507 { 00508 FT_Vector delta; delta.y = 0; 00509 if (layout() == LeftToRightText) 00510 { 00511 FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta ); 00512 pen.x() += delta.x / 64.0f; 00513 } 00514 else 00515 if (layout() == RightToLeftText) 00516 { 00517 FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta ); 00518 pen.x() -= delta.x / 64.0f; 00519 } 00520 pen.y() += delta.y / 64.0f; 00521 } 00522 previous = glyph->glyphIndex(); 00523 00524 if ( glyph->textureHandle() ) 00525 { 00526 int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left(); 00527 00528 vect[0].x() = pen.x() + glyph->width()*0 + left -1; 00529 vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00530 00531 vect[1].x() = pen.x() + glyph->width()*1 + left +1; 00532 vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1; 00533 00534 vect[2].x() = pen.x() + glyph->width()*1 + left +1; 00535 vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00536 00537 vect[3].x() = pen.x() + glyph->width()*0 + left -1; 00538 vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1; 00539 00540 if (layout() == RightToLeftText) 00541 { 00542 vect[0].x() -= glyph->width()-1 +2; 00543 vect[1].x() -= glyph->width()-1 +2; 00544 vect[2].x() -= glyph->width()-1 +2; 00545 vect[3].x() -= glyph->width()-1 +2; 00546 } 00547 00548 vect[0].y() -= mFont->mHeight; 00549 vect[1].y() -= mFont->mHeight; 00550 vect[2].y() -= mFont->mHeight; 00551 vect[3].y() -= mFont->mHeight; 00552 } 00553 00554 aabb.addPoint( (vec3)vect[0] ); 00555 aabb.addPoint( (vec3)vect[1] ); 00556 aabb.addPoint( (vec3)vect[2] ); 00557 aabb.addPoint( (vec3)vect[3] ); 00558 00559 if (layout() == LeftToRightText) 00560 pen += glyph->advance(); 00561 else 00562 if (layout() == RightToLeftText) 00563 pen -= glyph->advance(); 00564 } 00565 00566 return aabb; 00567 } 00568 //----------------------------------------------------------------------------- 00569 void CoreText::renderBackground(const Actor*, const Camera*) const 00570 { 00571 // mic fixme: 00572 // rendering of border and background follows different rules in 3D compared from 2D: lines and polygons follow different rasterization rules! 00573 00574 // Background color 00575 glColor4fv(mBackgroundColor.ptr()); 00576 00577 // Constant normal 00578 glNormal3fv( fvec3(0,0,1).ptr() ); 00579 00580 vec3 a,b,c,d; 00581 AABB bbox = boundingRect(); // mic fixme: this guy recomputes the bounds again instead of using the precomputed one!! 00582 a = bbox.minCorner(); 00583 b.x() = (float)bbox.maxCorner().x(); 00584 b.y() = (float)bbox.minCorner().y(); 00585 c = bbox.maxCorner(); 00586 d.x() = (float)bbox.minCorner().x(); 00587 d.y() = (float)bbox.maxCorner().y(); 00588 // set z to 0 00589 a.z() = b.z() = c.z() = d.z() = 0; 00590 00591 fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d }; 00592 glEnableClientState( GL_VERTEX_ARRAY ); 00593 glVertexPointer(3, GL_FLOAT, 0, vect); 00594 00595 glDrawArrays(GL_QUADS,0,4); 00596 00597 glDisableClientState( GL_VERTEX_ARRAY ); 00598 } 00599 //----------------------------------------------------------------------------- 00600 void CoreText::renderBorder(const Actor*, const Camera*) const 00601 { 00602 // mic fixme: 00603 // rendering of border and background follows different rules in 3D compared from 2D: lines and polygons follow different rasterization rules! 00604 00605 // Border color 00606 glColor4fv(mBorderColor.ptr()); 00607 00608 // Constant normal 00609 glNormal3fv( fvec3(0,0,1).ptr() ); 00610 00611 vec3 a,b,c,d; 00612 AABB bbox = boundingRect(); // mic fixme: this guy recomputes the bounds again instead of using the precomputed one!! 00613 a = bbox.minCorner(); 00614 b.x() = (float)bbox.maxCorner().x(); 00615 b.y() = (float)bbox.minCorner().y(); 00616 c = bbox.maxCorner(); 00617 d.x() = (float)bbox.minCorner().x(); 00618 d.y() = (float)bbox.maxCorner().y(); 00619 // set z to 0 00620 a.z() = b.z() = c.z() = d.z() = 0; 00621 00622 fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d }; 00623 glEnableClientState( GL_VERTEX_ARRAY ); 00624 glVertexPointer(3, GL_FLOAT, 0, vect); 00625 00626 glDrawArrays(GL_LINE_LOOP,0,4); 00627 00628 glDisableClientState( GL_VERTEX_ARRAY ); 00629 } 00630 //----------------------------------------------------------------------------- 00631 AABB CoreText::boundingRect() const 00632 { 00633 return boundingRect(text()); 00634 } 00635 //----------------------------------------------------------------------------- 00636 AABB CoreText::boundingRect(const String& text) const 00637 { 00638 AABB bbox = rawboundingRect( text ); 00639 bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*margin(), 2.0f*margin(), 0) ); 00640 00641 // normalize coordinate orgin to the bottom/left corner 00642 vec3 min = bbox.minCorner() - bbox.minCorner(); 00643 vec3 max = bbox.maxCorner() - bbox.minCorner(); 00644 00645 // text pivot 00646 00647 if (textOrigin() & AlignHCenter) 00648 { 00649 VL_CHECK( !(textOrigin() & AlignRight) ) 00650 VL_CHECK( !(textOrigin() & AlignLeft) ) 00651 min.x() -= int(bbox.width() / 2.0); 00652 max.x() -= int(bbox.width() / 2.0); 00653 } 00654 00655 if (textOrigin() & AlignRight) 00656 { 00657 VL_CHECK( !(textOrigin() & AlignHCenter) ) 00658 VL_CHECK( !(textOrigin() & AlignLeft) ) 00659 min.x() -= (int)bbox.width(); 00660 max.x() -= (int)bbox.width(); 00661 } 00662 00663 if (textOrigin() & AlignTop) 00664 { 00665 VL_CHECK( !(textOrigin() & AlignBottom) ) 00666 VL_CHECK( !(textOrigin() & AlignVCenter) ) 00667 min.y() -= (int)bbox.height(); 00668 max.y() -= (int)bbox.height(); 00669 } 00670 00671 if (textOrigin() & AlignVCenter) 00672 { 00673 VL_CHECK( !(textOrigin() & AlignTop) ) 00674 VL_CHECK( !(textOrigin() & AlignBottom) ) 00675 min.y() -= int(bbox.height() / 2.0); 00676 max.y() -= int(bbox.height() / 2.0); 00677 } 00678 00679 AABB aabb; 00680 aabb.setMinCorner(min); 00681 aabb.setMaxCorner(max); 00682 return aabb; 00683 } 00684 //-----------------------------------------------------------------------------