![[]](/images/special/trans.gif)
LibN2L-4 Library Code ReferenceClassesCompounds Files Members Method Index Full Reference cTextureFont.cppGo to the documentation of this file.00001 /************************************************************************ 00002 Nova-2 Library (libN2L, or simply n2l) Game development C++ Library 00003 Copyright (C) 2003 Aaron Cameron 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free Software 00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 00019 A copy of the GNU Lesser General Public License has been provided with 00020 this library in the file 'COPYING'. 00021 00022 Contact information for the author of this library has been provided 00023 with this library in the file 'AUTHOR'. 00024 ************************************************************************/ 00025 #include "cTextureFont.h" 00026 00027 #include "n2l/video.h" 00028 #include "n2l/materials.h" 00029 00030 #include <GL/gl.h> 00031 00032 #include <iostream> 00033 using namespace std; 00034 /******************************************************************************/ 00035 namespace n2l 00036 { 00037 00038 const tUint cTextureFont::MaxGlyphs = 255; 00039 const tGlyphID cTextureFont::UndefinedGlyphID = 0; 00040 00041 const tUint cTextureFont::GlyphPadding = 1; 00042 00043 const tUint cTextureFont::PreferredMaxGenTexXSize = 255; 00044 const tUint cTextureFont::PreferredMaxGenTexYSize = 255; 00045 00046 const tUint cTextureFont::MaxGenTexXSize = 1024; 00047 const tUint cTextureFont::MaxGenTexYSize = 1024; 00048 00049 // SDL_ttf dies if passed 0 as a character, and it's the terminate 00050 // character, so probably for the best that it doesn't show properly. 00051 // Manually mapped surfaces may define 0, however. So this 00052 // restriction is a sort of magical number. 00053 const tGlyphID cTextureFont::MinGenGlyphID = 1; 00054 00055 const tGlyphID cTextureFont::MaxGenGlyphID = cTextureFont::MaxGlyphs; 00056 00057 /**************************************************************************/ 00058 cTextureFont::cTextureFont( const cVfsNodeInterface &iFile, 00059 const tUint iPtSize ) 00060 { 00061 // Load and convert the file. 00062 cTTFSurfaceFactory factory( iFile, iPtSize ); 00063 // Get the memory for our glyph map 00064 mTexCoords = new tTexCoords[ MaxGlyphs ]; 00065 mGlyphWidths = new tFloat[ MaxGlyphs ]; 00066 buildGlyphs(factory); 00067 } 00068 00069 00070 /**************************************************************************/ 00071 cTextureFont::cTextureFont( const cSurfaceInterface &iSurface, 00072 const tCoordMap & iMap ) 00073 { 00074 // Get the memory for our glyph map 00075 mTexCoords = new tTexCoords[ MaxGlyphs ]; 00076 mGlyphWidths = new tFloat[ MaxGlyphs ]; 00077 buildGlyphs(iSurface,iMap); 00078 } 00079 00080 00081 /**************************************************************************/ 00082 cTextureFont::cTextureFont( const cTTFSurfaceFactory &iFactory ) 00083 { 00084 // Get the memory for our glyph map 00085 mTexCoords = new tTexCoords[ MaxGlyphs ]; 00086 mGlyphWidths = new tFloat[ MaxGlyphs ]; 00087 buildGlyphs(iFactory); 00088 } 00089 00090 00091 /**************************************************************************/ 00092 cTextureFont::~cTextureFont() 00093 { 00094 delete []mGlyphWidths; 00095 delete []mTexCoords; 00096 } 00097 00098 00099 /**************************************************************************/ 00100 void cTextureFont::buildGlyphs( const cSurfaceInterface &iSurface, 00101 const tCoordMap &iMap ) 00102 { 00103 if (iMap.empty()) 00104 throw cBadDataUseException( "cTextureFont::buildGlyphs", 00105 "No coordinates in the map" ); 00106 // Check to make sure there's mapping for an 'undefined' 00107 // character 00108 const tCoordMap::const_iterator UndefinedGlyphCoordsIt( 00109 iMap.find(UndefinedGlyphID) ); 00110 if (UndefinedGlyphCoordsIt==iMap.end()) 00111 throw cBadDataUseException( "cTextureFont::buildGlyphs", 00112 "No coordinates for required glyph \'undefined\'" ); 00113 00114 // Create the GL texture we'll use to hold the actual pixel data for 00115 // these glyphs 00116 mGlyphs = new cGLTexture( iSurface ); 00117 00118 // Get the new size of the glTexture so we can get the final glyph 00119 // coordinates as a percentage [0->1.0f] of its size. 00120 // Any dead space in the texture will have been created to 00121 // the right, and the bottom so we don't need to consider it when 00122 // modifying our surface coordinates to texture coordinates 00123 const tFloat XMax = mGlyphs->size().x(); 00124 const tFloat YMax = mGlyphs->size().y(); 00125 00126 // Step through our source coordinates and convert them to fractions of 00127 // one which will apply to the GLTexture we'll use to hold the pixel 00128 // data itself. 00129 00130 // Blank all of the tex coordinates by setting them to the 'undefined' 00131 // glyph. 00132 for (tGlyphID g=0; g<MaxGlyphs; ++g) { 00133 mTexCoords[g] = tTexCoords( 00134 UndefinedGlyphCoordsIt->second.x()/XMax, 00135 UndefinedGlyphCoordsIt->second.y()/YMax, 00136 UndefinedGlyphCoordsIt->second.x2()/XMax, 00137 UndefinedGlyphCoordsIt->second.y2()/YMax ); 00138 mGlyphWidths[g] = UndefinedGlyphCoordsIt->second.w()/ 00139 tFloat(UndefinedGlyphCoordsIt->second.h()); 00140 } 00141 00142 // Now set the rest of the glyphs that ARE defined. 00143 for(tCoordMap::const_iterator cSet = iMap.begin(); 00144 cSet!=iMap.end(); ++cSet) { 00145 mTexCoords[cSet->first] = tTexCoords( cSet->second.x()/XMax, 00146 cSet->second.y()/YMax, cSet->second.x2()/XMax, 00147 cSet->second.y2()/YMax ); 00148 // Store the percentage of the glyphs height to width from the 00149 // original coordinates. 00150 mGlyphWidths[cSet->first] = 00151 cSet->second.w()/tFloat(cSet->second.h()); 00152 00153 } // for 00154 00155 // Figure out how far apart to space individual glyphs. 00156 // There's a better way to do this, but for now we'll just 00157 // take a 1 pixel buffer and convert it with the same ratio 00158 // we've be using. 00159 mGlyphSeperation = 1.0f/(iMap.begin()->second.h()); 00160 // Same for the row count. 00161 mGlyphRowSeperation = 1.0f/(iMap.begin()->second.h()); 00162 00163 // That's it. We have a font. 00164 } 00165 00166 00167 /**************************************************************************/ 00168 void cTextureFont::buildGlyphs( const cTTFSurfaceFactory &iFactory ) 00169 { 00170 // For a factory based font, we have to build a surface and 00171 // mapping coordinates from the factory itself. During the 00172 // surface generation, we'll get get a map of coordinates and 00173 // then we can pass the whole thing into the cSurface/coordMap 00174 // buildGlyphs to do the rest. Why? Because I'm lazy. 00175 // Note: This will likely have to change eventually to speed 00176 // up loading of ttf fonts. 00177 00178 // We'll do this in two passes. First, we need to crawl over 00179 // the faces in the surface to get their sizes. That way we 00180 // can figure out the width and height we need for the temp 00181 // surface. It's also important that we restrict how wide the 00182 // texture becomes, rather than just going straight left to 00183 // right on one line, as some video cards have a cap on how 00184 // large the dimention of a texture can be. And we might end 00185 // up with a software texture, which achieves nothing. 00186 // When I said 2, I meant three. First we try to fit within 00187 // ideal dimentions, something more likely to fit into memory, 00188 // then 00189 00190 // WE need to make sure that the glyphs from this font all 00191 // have the same y size. If not, our system breaks down. 00192 tUint GlyphHeight = iFactory.glyphSize(MinGenGlyphID).y(); 00193 for (tUint g=MinGenGlyphID; g<MaxGenGlyphID; ++g) 00194 if (GlyphHeight!=iFactory.glyphSize(g).y()) 00195 throw cBadDataUseException( "cTextureFont::buildGlyphs", 00196 "All glyphs from this factory dont\'t have the " 00197 "same y-size" ); 00198 tUint x=0; 00199 tUint y=0; 00200 00201 tUint finalXSize, finalYSize; 00202 00203 // We use a tUint instead of tGlyphID because MaxGenGlyphID 00204 // is usually MaxGlyphs, which is usually 255. Which means 00205 // this loop would never end since the tGlyphID usually only 00206 // has 8-bit precision. 00207 if (!willFitIn( iFactory, PreferredMaxGenTexXSize, 00208 PreferredMaxGenTexYSize, finalXSize, finalYSize) && 00209 !willFitIn( iFactory, MaxGenTexXSize, MaxGenTexYSize, finalXSize, 00210 finalYSize) ) 00211 { 00212 throw cOutOfBoundsException( "cTextureFont::buildGlyphs", 00213 "Can't fit glyphs into an acceptable texture size " 00214 "try lowering the texture factory font point size"); 00215 } 00216 00217 // Create a surface to hold this thing. 00218 // Get a glyph from the factory to let us know what format 00219 // we're going to need for the final surface. 00220 cAutoPtr<cSurface> glyphSurface = iFactory.generate(MinGenGlyphID); 00221 cSurface tempSurface; 00222 tempSurface.create( finalXSize,finalYSize, 00223 glyphSurface->format().bitsPerPixel(), 00224 glyphSurface->format().channelMask(cPixelFormat::Channel_Red), 00225 glyphSurface->format().channelMask(cPixelFormat::Channel_Green), 00226 glyphSurface->format().channelMask(cPixelFormat::Channel_Blue), 00227 glyphSurface->format().channelMask(cPixelFormat::Channel_Alpha) ); 00228 if (glyphSurface->format().channelMask(cPixelFormat::Channel_Alpha)) 00229 tempSurface.clearSurface( 00230 tempSurface.mapRGBA(0x00,0x00,0x00,0x00) ); 00231 00232 else tempSurface.clearSurface( tempSurface.mapRGB(0x00,0x00,0x00) ); 00233 // Now step through our glyphs one last time to generate the 00234 // coordinate map and tempSurface face 00235 x = 0; 00236 y = 0; 00237 00238 tCoordMap tempMap; 00239 for (tUint g=MinGenGlyphID; g<MaxGenGlyphID; ++g) { 00240 //cout << __FILE__ << ": " << __LINE__ << endl; 00241 glyphSurface = iFactory.generate(g); 00242 //cout << __FILE__ << ": " << __LINE__ << endl; 00243 if ( (x+glyphSurface->properties().size().x()) >= finalXSize ) { 00244 x = 0; 00245 y += GlyphHeight+GlyphPadding; 00246 } 00247 //tempSurface.blit( tVector2u(x,y), *glyphSurface ); 00248 tempSurface.literalCopyBlit( tVector2u(x,y), *glyphSurface ); 00249 tempMap[g] = tCoordSet( x,y, 00250 x+glyphSurface->properties().size().x(), 00251 y+glyphSurface->properties().size().y() ); 00252 // Move our x 00253 x += glyphSurface->properties().size().x() + GlyphPadding; 00254 } // Pasting for 00255 00256 // Finally, since the font factory won't generate a font for 00257 // 'undefined', we need to pick the closest other glyphID 00258 // possible instead, and use those coordinates 00259 tempMap[UndefinedGlyphID] = tempMap[MinGenGlyphID]; 00260 00261 // Wheuph... now we can call the OTHER buildGlyphs to actually 00262 // do the texturing work. 00263 buildGlyphs( tempSurface, tempMap ); 00264 } 00265 00266 00267 /**************************************************************************/ 00268 void cTextureFont::drawGlyph( const tGlyphID iGlyphID ) const 00269 { 00270 if (iGlyphID>=MaxGlyphs) 00271 throw cOutOfBoundsException( "cTextureFont::drawGlyph", 00272 "No such glyphID" ); 00273 /* glPushAttrib( GL_ALL_ATTRIB_BITS ); 00274 00275 glEnable(GL_CULL_FACE); 00276 glCullFace(GL_FRONT); 00277 00278 glEnable( GL_TEXTURE_2D ); 00279 if (mGlyphs->hasAlpha()) { 00280 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 00281 glEnable( GL_BLEND ); 00282 } 00283 */ 00284 mGlyphs->bind(); 00285 glBegin( GL_QUADS ); 00286 glTexCoord2f( mTexCoords[iGlyphID].x(), 00287 mTexCoords[iGlyphID].y2() ); 00288 00289 glVertex3f(0,0,0); 00290 glTexCoord2f( mTexCoords[iGlyphID].x(), 00291 mTexCoords[iGlyphID].y() ); 00292 00293 glVertex3f(0,1,0); 00294 glTexCoord2f( mTexCoords[iGlyphID].x2(), 00295 mTexCoords[iGlyphID].y() ); 00296 00297 glVertex3f(1,1,0); 00298 glTexCoord2f( mTexCoords[iGlyphID].x2(), 00299 mTexCoords[iGlyphID].y2() ); 00300 00301 glVertex3f(1,0,0); 00302 glPopAttrib(); 00303 } 00304 00305 /**************************************************************************/ 00306 void cTextureFont::draw( const tString &iText ) const 00307 { 00308 const tString::size_type SLen(iText.size()); 00309 tFloat xPos = 0.0f; 00310 /* glEnable( GL_TEXTURE_2D ); 00311 if (mGlyphs->hasAlpha()) { 00312 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 00313 glEnable( GL_BLEND ); 00314 } 00315 */ 00316 mGlyphs->bind(); 00317 glBegin( GL_QUADS ); 00318 for (tString::size_type i=0; i<SLen; ++i) { 00319 const tFloat gWidth = mGlyphWidths[iText[i]]; 00320 glTexCoord2f( mTexCoords[iText[i]].x(), 00321 mTexCoords[iText[i]].y() ); 00322 00323 glVertex3f(xPos,0,0); 00324 glTexCoord2f( mTexCoords[iText[i]].x(), 00325 mTexCoords[iText[i]].y2() ); 00326 00327 glVertex3f(xPos,1,0); 00328 glTexCoord2f( mTexCoords[iText[i]].x2(), 00329 mTexCoords[iText[i]].y2() ); 00330 00331 glVertex3f(xPos+gWidth,1,0); 00332 glTexCoord2f( mTexCoords[iText[i]].x2(), 00333 mTexCoords[iText[i]].y() ); 00334 00335 glVertex3f(xPos+gWidth,0,0); 00336 xPos += gWidth+mGlyphSeperation; 00337 } // for. 00338 glEnd(); 00339 /* if (mGlyphs->hasAlpha()) 00340 glDisable( GL_BLEND ); 00341 glDisable( GL_TEXTURE_2D ); 00342 */ 00343 } 00344 00345 /**************************************************************************/ 00346 void cTextureFont::draw( const tVector2f &iPos, const tString &iText, 00347 const tFloat &iGlyphHeight, const tString::size_type iStartPos, 00348 const tString::size_type iLength ) const 00349 { 00350 const tString::size_type SLen(iText.size()); 00351 if (iStartPos>=SLen) return; 00352 const tString::size_type EPos = n2l_min(SLen,iStartPos+iLength); 00353 tFloat xPos = iPos.x(); 00354 tFloat yPos = iPos.y(); 00355 tFloat yPos2 = iPos.y()+iGlyphHeight; 00356 00357 /* 00358 glEnable( GL_TEXTURE_2D ); 00359 if (mGlyphs->hasAlpha()) { 00360 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 00361 glEnable( GL_BLEND ); 00362 } 00363 */ 00364 mGlyphs->bind(); 00365 glBegin( GL_QUADS ); 00366 for (tString::size_type i=iStartPos; i<EPos; ++i) { 00367 const tFloat gWidth = mGlyphWidths[iText[i]]*iGlyphHeight; 00368 const tFloat xPos2 = xPos+gWidth; 00369 glTexCoord2f( mTexCoords[iText[i]].x(), 00370 mTexCoords[iText[i]].y() ); 00371 glVertex3f(xPos,yPos,0); 00372 00373 glTexCoord2f( mTexCoords[iText[i]].x(), 00374 mTexCoords[iText[i]].y2() ); 00375 glVertex3f(xPos,yPos2,0); 00376 00377 glTexCoord2f( mTexCoords[iText[i]].x2(), 00378 mTexCoords[iText[i]].y2() ); 00379 glVertex3f(xPos2,yPos2,0); 00380 00381 glTexCoord2f( mTexCoords[iText[i]].x2(), 00382 mTexCoords[iText[i]].y() ); 00383 glVertex3f(xPos2,yPos,0); 00384 00385 xPos += gWidth + (mGlyphSeperation*iGlyphHeight); 00386 } // for. 00387 glEnd(); 00388 /* if (mGlyphs->hasAlpha()) 00389 glDisable( GL_BLEND ); 00390 glDisable( GL_TEXTURE_2D ); 00391 */ 00392 } 00393 00394 /**************************************************************************/ 00395 const tVector2f cTextureFont::calcSize( const tString &iText, 00396 const tFloat &iGlyphHeight, const tString::size_type iStartPos, 00397 const tString::size_type iLength ) const 00398 { 00399 const tString::size_type SLen(iText.size()); 00400 if (iStartPos>=SLen) return tVector2f(0,0); 00401 const tString::size_type EPos = n2lMin(SLen,iStartPos+iLength); 00402 tFloat width = 0.0f; 00403 for (tString::size_type i=iStartPos; i<EPos; ++i) 00404 width += (mGlyphWidths[iText[i]]+mGlyphSeperation)*iGlyphHeight; 00405 return tVector2f(width,iGlyphHeight); 00406 } 00407 00408 00409 /**************************************************************************/ 00410 const tFloat cTextureFont::glyphWidth( const tGlyphID iGlyphID, 00411 const tFloat &iGlyphHeight ) const 00412 { 00413 return mGlyphWidths[iGlyphID]*iGlyphHeight; 00414 } 00415 00416 /**************************************************************************/ 00417 const tFloat cTextureFont::glyphSeperation( 00418 const tFloat &iGlyphHeight ) const 00419 { 00420 return mGlyphSeperation*iGlyphHeight; 00421 } 00422 00423 /**************************************************************************/ 00424 const tFloat cTextureFont::glyphRowSeperation( 00425 const tFloat &iGlyphHeight ) const 00426 { 00427 return mGlyphRowSeperation*iGlyphHeight; 00428 } 00429 00430 /**************************************************************************/ 00431 const tBool cTextureFont::willFitIn( const cTTFSurfaceFactory &iFactory, 00432 const tUint iMaxX, const tUint iMaxY, tUint &oActualXSize, 00433 tUint &oActualYSize ) const 00434 { 00435 tUint x=0; 00436 tUint y=0; 00437 for (tUint g=MinGenGlyphID; g<MaxGenGlyphID; ++g) { 00438 tVector2u gSize = iFactory.glyphSize(g); 00439 // If this glyph is too big to fit on this line, 00440 // bump it down 00441 if ( (x+gSize.x()) >= iMaxX ) { 00442 x = 0; 00443 y += gSize.y()+GlyphPadding; 00444 // Check to see if we've blown our cap 00445 if ( (y+gSize.y()) >= iMaxY ) return false; 00446 } 00447 x+= gSize.x()+GlyphPadding; 00448 } // for 00449 oActualXSize = iMaxX; 00450 oActualYSize = y+iFactory.glyphSize(MinGenGlyphID).y(); 00451 return true; 00452 } 00453 00454 } // namespace |