//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2003 // // This file is part of Scorched3D. // // Scorched3D is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Scorched3D is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include GLFont2d::GLFont2d() : textures_(0), list_base_(0), characters_(0) { } GLFont2d::~GLFont2d() { glDeleteLists(list_base_,128); glDeleteTextures(128, textures_); delete [] textures_; textures_ = 0; delete [] characters_; characters_ = 0; } bool GLFont2d::getInit() { return (textures_ != 0); } float GLFont2d::getWidth(float size, const char *text, int len) { float width = 0.0f; if (len == 0) len = (int) strlen(text); char *a=(char *) text; for (int i=0; i 0) { drawString(len - start, color, alpha, size, x + width, y, z, text + start, false); } } void GLFont2d::drawWidth(float len, Vector &color, float size, float x, float y, float z, const char *text) { int l = getChars(size, text, len); drawString(l, color, 1.0f, size, x, y, z, text, false); } void GLFont2d::drawWidthRhs(float len, Vector &color, float size, float x, float y, float z, const char *text) { int slen = strlen(text); if (slen > 0) { int l = 0; float width = 0.0f; char *a=& ((char *)text)[slen-1]; for (; a >= text; a--, l++) { width += float(characters_[*a].advances) * size / height_; if (width > len) break; } a++; drawString(l, color, 1.0f, size, x, y, z, a, false); } } void GLFont2d::drawBilboard(Vector &color, float alpha, float size, float x, float y, float z, const char *text) { drawString((GLsizei) strlen(text), color, alpha, size, x, y, z, text, true); } static void drawLetter(char ch, GLuint list_base, GLuint *tex_base, GLFont2d::CharEntry *characters) { Vector &bilX = GLCameraFrustum::instance()->getBilboardVectorX(); Vector &bilY = GLCameraFrustum::instance()->getBilboardVectorY(); glBindTexture(GL_TEXTURE_2D,tex_base[ch]); glPushMatrix(); Vector trans = bilX * (float) (characters+ch)->left + bilY * (float) (characters+ch)->rows; glTranslatef(trans[0], trans[1], trans[2]); glBegin(GL_QUADS); glTexCoord2f(0.0f,0.0f); glVertex3fv(bilY * (characters+ch)->height); glTexCoord2f(0.0f, (characters+ch)->y); glVertex3f(0.0f,0.0f,0.0f); glTexCoord2f((characters+ch)->x,(characters+ch)->y); glVertex3fv(bilX * (characters+ch)->width); glTexCoord2f((characters+ch)->x,0.0f); glVertex3fv(bilX * (characters+ch)->width + bilY * (characters+ch)->height); glEnd(); glPopMatrix(); bilX *= (float)((characters+ch)->advances); glTranslatef(bilX[0], bilX[1], bilX[2]); } bool GLFont2d::drawString(unsigned length, Vector &color, float alpha, float size, float x, float y, float z, const char *string, bool bilboard, float size2) { if (textures_) { GLTextureBase::setLastBind(0); // Clear so no texture is cached GLState currentState(GLState::BLEND_ON | GLState::TEXTURE_ON); glColor4f(color[0], color[1], color[2], alpha); if (bilboard) { glPushMatrix(); glTranslatef(x,y,z); glScalef(size / height_, size / height_, size / height_); for (const char *current = string; *current; current++) { drawLetter(*current, list_base_,textures_,characters_); } glPopMatrix(); } else if (size2 == 0.0f) { glPushAttrib(GL_LIST_BIT); glListBase(list_base_); glPushMatrix(); glTranslatef(x,y,z); glScalef(size / height_, size / height_, size / height_); glCallLists(length, GL_UNSIGNED_BYTE, string); glPopMatrix(); glPopAttrib(); } else { glPushMatrix(); glTranslatef(x,y,z); glScalef(size / height_, size / height_, size / height_); float scale = size / height_; float outlineScale = size2 / height_; for (;*string; string++) { float advances = (float)((characters_+*string)->advances); unsigned int list = *string; glCallList(list + list_base_); glTranslatef(-advances + advances * outlineScale / scale,0.0f ,0.0f); } glPopMatrix(); } return true; } return false; } // This Function Gets The First Power Of 2 >= The // Int That We Pass It. static inline int next_p2 (int a ) { int rval=1; // rval<<=1 Is A Prettier Way Of Writing rval*=2; while(rvalglyph, &glyph )) { dialogMessage("GLFont", "FT_Get_Glyph failed"); return false; } // Convert The Glyph To A Bitmap. FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 ); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; // This Reference Will Make Accessing The Bitmap Easier. FT_Bitmap& bitmap=bitmap_glyph->bitmap; // Use Our Helper Function To Get The Widths Of // The Bitmap Data That We Will Need In Order To Create // Our Texture. int width = next_p2( bitmap.width ); int height = next_p2( bitmap.rows ); // Allocate Memory For The Texture Data. GLubyte* expanded_data = new GLubyte[ 2 * width * height]; // Here We Fill In The Data For The Expanded Bitmap. // Notice That We Are Using A Two Channel Bitmap (One For // Channel Luminosity And One For Alpha), But We Assign // Both Luminosity And Alpha To The Value That We // Find In The FreeType Bitmap. // We Use The ?: Operator To Say That Value Which We Use // Will Be 0 If We Are In The Padding Zone, And Whatever // Is The FreeType Bitmap Otherwise. for(int j=0; j =bitmap.width || j>=bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j]; } } // Now We Just Setup Some Texture Parameters. glBindTexture( GL_TEXTURE_2D, tex_base[ch]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); // Here We Actually Create The Texture Itself, Notice // That We Are Using GL_LUMINANCE_ALPHA To Indicate That // We Are Using 2 Channel Data. glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data ); // With The Texture Created, We Don't Need The Expanded Data Anymore. delete [] expanded_data; // Now We Create The Display List glNewList(list_base+ch, GL_COMPILE); glBindTexture(GL_TEXTURE_2D,tex_base[ch]); glPushMatrix(); // First We Need To Move Over A Little So That // The Character Has The Right Amount Of Space // Between It And The One Before It. glTranslatef((float) bitmap_glyph->left, 0.0f, 0.0f); (characters+ch)->left = bitmap_glyph->left; // Now We Move Down A Little In The Case That The // Bitmap Extends Past The Bottom Of The Line // This Is Only True For Characters Like 'g' Or 'y'. glTranslatef(0.0f,(float) bitmap_glyph->top-bitmap.rows,0.0f); (characters+ch)->rows = bitmap_glyph->top-bitmap.rows; // Now We Need To Account For The Fact That Many Of // Our Textures Are Filled With Empty Padding Space. // We Figure What Portion Of The Texture Is Used By // The Actual Character And Store That Information In // The x And y Variables, Then When We Draw The // Quad, We Will Only Reference The Parts Of The Texture // That Contains The Character Itself. float x=(float)bitmap.width / (float)width; float y=(float)bitmap.rows / (float)height; (characters+ch)->x = x; (characters+ch)->y = y; // Here We Draw The Texturemapped Quads. // The Bitmap That We Got From FreeType Was Not // Oriented Quite Like We Would Like It To Be, // But We Link The Texture To The Quad // In Such A Way That The Result Will Be Properly Aligned. (characters+ch)->width = (float)bitmap.width; (characters+ch)->height = (float)bitmap.rows; glBegin(GL_QUADS); glTexCoord2f(0.0f,0.0f); glVertex2f(0.0f,(float)bitmap.rows); glTexCoord2f(0.0f,y); glVertex2f(0.0f,0.0f); glTexCoord2f(x,y); glVertex2f((float)bitmap.width,0.0f); glTexCoord2f(x,0.0f); glVertex2f((float)bitmap.width,(float)bitmap.rows); glEnd(); glPopMatrix(); glTranslatef((float)(face->glyph->advance.x >> 6) ,0.0f ,0.0f); (characters+ch)->advances = (face->glyph->advance.x >> 6); // Increment The Raster Position As If We Were A Bitmap Font. // (Only Needed If You Want To Calculate Text Length) // glBitmap(0,0,0,0,face->glyph->advance.x >> 6,0,NULL); // Finish The Display List glEndList(); return true; } bool GLFont2d::createFont(const char *typeFace, unsigned int h) { // Allocate Some Memory To Store The Texture Ids. textures_ = new GLuint[128]; characters_ = new CharEntry[128]; height_ = float(h); // Create And Initilize A FreeType Font Library. FT_Library library; if (FT_Init_FreeType( &library )) { dialogMessage("GLFont2d", "FT_Init_FreeType failed"); return false; } // The Object In Which FreeType Holds Information On A Given // Font Is Called A "face". FT_Face face; // This Is Where We Load In The Font Information From The File. // Of All The Places Where The Code Might Die, This Is The Most Likely, // As FT_New_Face Will Fail If The Font File Does Not Exist Or Is Somehow Broken. if (FT_New_Face( library, typeFace, 0, &face )) { dialogMessage("GLFont2d", formatString( "FT_New_Face failed (there is probably a problem with your font file \"%s\")", typeFace)); return false; } // For Some Twisted Reason, FreeType Measures Font Size // In Terms Of 1/64ths Of Pixels. Thus, To Make A Font // h Pixels High, We Need To Request A Size Of h*64. // (h << 6 Is Just A Prettier Way Of Writing h*64) FT_Set_Char_Size( face, h << 6, h << 6, 96, 96); // Here We Ask OpenGL To Allocate Resources For // All The Textures And Display Lists Which We // Are About To Create. list_base_=glGenLists(128); glGenTextures( 128, textures_ ); // This Is Where We Actually Create Each Of The Fonts Display Lists. for(unsigned char i=0;i<128;i++) make_dlist(face,i,list_base_,textures_,characters_); // We Don't Need The Face Information Now That The Display // Lists Have Been Created, So We Free The Assosiated Resources. FT_Done_Face(face); // Ditto For The Font Library. FT_Done_FreeType(library); return true; }