AaronCameron.net
Not Left, nor right. Just correct.
Not a Member? - Login or Create an Account
Tuesday the 22nd of May 2012 @ 03:58pm
Front Page Journal Projects Your Profile About
[]

LibN2L-4 Library Code Reference

Classes
Compounds
Files
Members
Method Index
Full Reference

cTextureFont.cpp

Go 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
©2012 Aaron Cameron