| 1 | /* |
|---|
| 2 | ================================================================================= |
|---|
| 3 | This file is part of Cafu, the open-source game engine and graphics engine |
|---|
| 4 | for multiplayer, cross-platform, real-time 3D action. |
|---|
| 5 | Copyright (C) 2002-2012 Carsten Fuchs Software. |
|---|
| 6 | |
|---|
| 7 | Cafu is free software: you can redistribute it and/or modify it under the terms |
|---|
| 8 | of the GNU General Public License as published by the Free Software Foundation, |
|---|
| 9 | either version 3 of the License, or (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| 12 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|---|
| 13 | PURPOSE. See the GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Cafu. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | For support and more information about Cafu, visit us at <http://www.cafu.de>. |
|---|
| 19 | ================================================================================= |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | #include <iomanip> |
|---|
| 23 | #include <iostream> |
|---|
| 24 | #include <fstream> |
|---|
| 25 | #include <string> |
|---|
| 26 | #include <limits> |
|---|
| 27 | |
|---|
| 28 | #include <ft2build.h> |
|---|
| 29 | #include FT_FREETYPE_H |
|---|
| 30 | |
|---|
| 31 | #include "Bitmap/Bitmap.hpp" |
|---|
| 32 | #include "ConsoleCommands/Console.hpp" |
|---|
| 33 | #include "ConsoleCommands/ConsoleStdout.hpp" |
|---|
| 34 | #include "FileSys/FileManImpl.hpp" |
|---|
| 35 | #include "Templates/Array.hpp" |
|---|
| 36 | |
|---|
| 37 | #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER<1300) |
|---|
| 38 | #define vsnprintf _vsnprintf |
|---|
| 39 | #endif |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | static cf::ConsoleStdoutT ConsoleStdout; |
|---|
| 43 | cf::ConsoleI* Console=&ConsoleStdout; |
|---|
| 44 | |
|---|
| 45 | static cf::FileSys::FileManImplT FileManImpl; |
|---|
| 46 | cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl; |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | bool DebugPNGs=false; |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | struct GlyphInfoT |
|---|
| 53 | { |
|---|
| 54 | unsigned long GlyphIndex; |
|---|
| 55 | |
|---|
| 56 | float BearingX; ///< The horizontal offset of the bitmap relative to the cursor position. |
|---|
| 57 | float BearingY; ///< The vertical offset of the bitmap relative to the cursor position (y-axis points up!). |
|---|
| 58 | float AdvanceX; ///< How much the cursor position should be advanced horizontally for rendering the next character. |
|---|
| 59 | |
|---|
| 60 | int Width; ///< The width in pixels of the bitmap of this glyph. |
|---|
| 61 | int Height; ///< The height in pixels of the bitmap of this glyph. |
|---|
| 62 | |
|---|
| 63 | int BitmapNr; ///< The index of the larger bitmap this glyphs bitmap is embedded in. |
|---|
| 64 | float s1; ///< The s1 tex-coord into the larger bitmap. |
|---|
| 65 | float t1; ///< The t1 tex-coord into the larger bitmap. |
|---|
| 66 | float s2; ///< The s2 tex-coord into the larger bitmap. |
|---|
| 67 | float t2; ///< The t2 tex-coord into the larger bitmap. |
|---|
| 68 | }; |
|---|
| 69 | |
|---|
| 70 | |
|---|
| 71 | std::string va(const char* FormatString, ...) |
|---|
| 72 | { |
|---|
| 73 | va_list ArgList; |
|---|
| 74 | char Buffer[1024]; |
|---|
| 75 | |
|---|
| 76 | if (!FormatString) return ""; |
|---|
| 77 | |
|---|
| 78 | va_start(ArgList, FormatString); |
|---|
| 79 | vsnprintf(Buffer, 1024-1, FormatString, ArgList); |
|---|
| 80 | Buffer[1024-1]=0; |
|---|
| 81 | va_end(ArgList); |
|---|
| 82 | |
|---|
| 83 | return Buffer; |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | |
|---|
| 87 | /// This class "allocates" rectangular areas in larger bitmaps. |
|---|
| 88 | class RectBitmapAllocatorT |
|---|
| 89 | { |
|---|
| 90 | public: |
|---|
| 91 | |
|---|
| 92 | RectBitmapAllocatorT(uint32_t BitmapInitColor, unsigned long BitmapSizeS=256, unsigned long BitmapSizeT=256) |
|---|
| 93 | : BITMAP_SIZE_S(BitmapSizeS), |
|---|
| 94 | BITMAP_SIZE_T(BitmapSizeT), |
|---|
| 95 | BITMAP_INIT_COLOR(BitmapInitColor) |
|---|
| 96 | { |
|---|
| 97 | for (unsigned long s=0; s<BITMAP_SIZE_S; s++) |
|---|
| 98 | BitmapAllocated.PushBack(BITMAP_SIZE_T); |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | ~RectBitmapAllocatorT() |
|---|
| 102 | { |
|---|
| 103 | for (unsigned long BitmapNr=0; BitmapNr<Bitmaps.Size(); BitmapNr++) |
|---|
| 104 | delete Bitmaps[BitmapNr]; |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | bool Allocate(unsigned long SizeS, unsigned long SizeT, unsigned long& BitmapNr, unsigned long& PosS, unsigned long& PosT) |
|---|
| 108 | { |
|---|
| 109 | if (SizeS>BITMAP_SIZE_S) return false; |
|---|
| 110 | if (SizeT>BITMAP_SIZE_T) return false; |
|---|
| 111 | |
|---|
| 112 | BitmapNr=Bitmaps.Size()-1; |
|---|
| 113 | if (AllocateHelper(SizeS, SizeT, PosS, PosT)) return true; |
|---|
| 114 | |
|---|
| 115 | BitmapT* Bitmap=new BitmapT(BITMAP_SIZE_S, BITMAP_SIZE_T); |
|---|
| 116 | for (unsigned long i=0; i<Bitmap->Data.Size(); i++) |
|---|
| 117 | Bitmap->Data[i]=BITMAP_INIT_COLOR; |
|---|
| 118 | Bitmaps.PushBack(Bitmap); |
|---|
| 119 | |
|---|
| 120 | for (unsigned long s=0; s<BITMAP_SIZE_S; s++) |
|---|
| 121 | BitmapAllocated[s]=0; |
|---|
| 122 | |
|---|
| 123 | BitmapNr=Bitmaps.Size()-1; |
|---|
| 124 | return AllocateHelper(SizeS, SizeT, PosS, PosT); |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | |
|---|
| 128 | const unsigned long BITMAP_SIZE_S; |
|---|
| 129 | const unsigned long BITMAP_SIZE_T; |
|---|
| 130 | const uint32_t BITMAP_INIT_COLOR; |
|---|
| 131 | ArrayT<BitmapT*> Bitmaps; |
|---|
| 132 | |
|---|
| 133 | |
|---|
| 134 | private: |
|---|
| 135 | |
|---|
| 136 | RectBitmapAllocatorT(const RectBitmapAllocatorT&); ///< Use of the Copy Constructor is not allowed. |
|---|
| 137 | void operator = (const RectBitmapAllocatorT&); ///< Use of the Assignment Operator is not allowed. |
|---|
| 138 | |
|---|
| 139 | bool AllocateHelper(unsigned long SizeS, unsigned long SizeT, unsigned long& PosS, unsigned long& PosT) |
|---|
| 140 | { |
|---|
| 141 | unsigned long Best=BITMAP_SIZE_T; |
|---|
| 142 | |
|---|
| 143 | for (unsigned long s=0; s<=BITMAP_SIZE_S-SizeS; s++) |
|---|
| 144 | { |
|---|
| 145 | unsigned long Best2=0; |
|---|
| 146 | unsigned long s2; |
|---|
| 147 | |
|---|
| 148 | for (s2=0; s2<SizeS; s2++) |
|---|
| 149 | { |
|---|
| 150 | if (BitmapAllocated[s+s2]>=Best) break; |
|---|
| 151 | if (BitmapAllocated[s+s2]>Best2) Best2=BitmapAllocated[s+s2]; |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | if (s2==SizeS) |
|---|
| 155 | { |
|---|
| 156 | // Gültige Position gefunden |
|---|
| 157 | PosS=s; |
|---|
| 158 | PosT=Best=Best2; |
|---|
| 159 | } |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | if (Best+SizeT>BITMAP_SIZE_T) return false; |
|---|
| 163 | |
|---|
| 164 | for (unsigned long s=0; s<SizeS; s++) BitmapAllocated[PosS+s]=Best+SizeT; |
|---|
| 165 | return true; |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | ArrayT<unsigned long> BitmapAllocated; |
|---|
| 169 | }; |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | void ProcessNewGlyph(FT_Face& Face, GlyphInfoT& Glyph, RectBitmapAllocatorT& RBA) |
|---|
| 173 | { |
|---|
| 174 | unsigned long PosS=0; |
|---|
| 175 | unsigned long PosT=0; |
|---|
| 176 | |
|---|
| 177 | // IMPORTANT: |
|---|
| 178 | // Our bitmaps are rendered by the Cafu engine using bilinear filtering. |
|---|
| 179 | // Therefore, we add a safety margin (a frame) of 1 pixel thickness around each glyph bitmap, |
|---|
| 180 | // i.e. we increase its width and height by two pixels. |
|---|
| 181 | // This safety margin is transparently added here in this function -- no user code is supposed to ever become aware of it! |
|---|
| 182 | unsigned long BitmapNr; |
|---|
| 183 | if (!RBA.Allocate(Face->glyph->bitmap.width+2, Face->glyph->bitmap.rows+2, BitmapNr, PosS, PosT)) |
|---|
| 184 | { |
|---|
| 185 | std::cout << "WARNING: Unable to process glyph!\n"; |
|---|
| 186 | |
|---|
| 187 | if (RBA.Bitmaps.Size()==0) return; // That should never happen (but *could*, e.g. if the glpyh bitmap is larger than the bitmaps of the RBA). |
|---|
| 188 | BitmapNr=0; |
|---|
| 189 | PosS=0; |
|---|
| 190 | PosT=0; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | BitmapT* Bitmap=RBA.Bitmaps[BitmapNr]; |
|---|
| 194 | |
|---|
| 195 | // Copy the glyphs bitmap into the larger bitmap. |
|---|
| 196 | for (int y=0; y<Face->glyph->bitmap.rows; y++) |
|---|
| 197 | for (int x=0; x<Face->glyph->bitmap.width; x++) |
|---|
| 198 | { |
|---|
| 199 | const unsigned char GrayValue=Face->glyph->bitmap.buffer[x+y*Face->glyph->bitmap.pitch]; |
|---|
| 200 | |
|---|
| 201 | if (DebugPNGs) Bitmap->SetPixel(PosS+x+1, PosT+y+1, GrayValue, GrayValue, GrayValue, 255); |
|---|
| 202 | else Bitmap->SetPixel(PosS+x+1, PosT+y+1, 255, 255, 255, GrayValue); |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | |
|---|
| 206 | // Fill out the Glyph structure. |
|---|
| 207 | Glyph.BearingX=float(Face->glyph->bitmap_left-1); // The -1 is required in order to compensate for our "safety frame". |
|---|
| 208 | Glyph.BearingY=float(Face->glyph->bitmap_top+1); // The +1 is required in order to compensate for our "safety frame". (Note that the bearing is relative to the *UPPER* left corner of the bitmap, not the lower left.) |
|---|
| 209 | Glyph.AdvanceX=float(Face->glyph->advance.x)/64.0f; |
|---|
| 210 | // Glyph.AdvanceX=float(Face->glyph->linearHoriAdvance)/65536.0f; |
|---|
| 211 | |
|---|
| 212 | Glyph.Width =Face->glyph->bitmap.width+2; |
|---|
| 213 | Glyph.Height=Face->glyph->bitmap.rows +2; |
|---|
| 214 | |
|---|
| 215 | Glyph.BitmapNr=BitmapNr; |
|---|
| 216 | Glyph.s1=float(PosS)/float(RBA.BITMAP_SIZE_S); |
|---|
| 217 | Glyph.t1=float(PosT)/float(RBA.BITMAP_SIZE_T); |
|---|
| 218 | Glyph.s2=float(PosS+Face->glyph->bitmap.width+2)/float(RBA.BITMAP_SIZE_S); |
|---|
| 219 | Glyph.t2=float(PosT+Face->glyph->bitmap.rows +2)/float(RBA.BITMAP_SIZE_T); |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | |
|---|
| 223 | int Usage() |
|---|
| 224 | { |
|---|
| 225 | std::cout << "\nUSAGE: MakeFont FontFileName [OPTIONS]\n"; |
|---|
| 226 | std::cout << "\n"; |
|---|
| 227 | std::cout << "OPTIONS:\n"; |
|---|
| 228 | std::cout << "-m=MaterialBaseName\n"; |
|---|
| 229 | std::cout << " If this is given, a Font.cmat material definition file will be created\n"; |
|---|
| 230 | std::cout << " automatically, using MaterialBaseName as a basis for creating the material\n"; |
|---|
| 231 | std::cout << " names.\n"; |
|---|
| 232 | std::cout << "\n"; |
|---|
| 233 | std::cout << "-debug\n"; |
|---|
| 234 | std::cout << " Creates the FontImage_X_Y.png files with false colors.\n"; |
|---|
| 235 | std::cout << "\n"; |
|---|
| 236 | std::cout << "Example:\n"; |
|---|
| 237 | std::cout << " MakeFont c:\\WINNT\\Fonts\\arial.ttf -m=Arial\n"; |
|---|
| 238 | return 1; |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | |
|---|
| 242 | int main(int ArgC, const char* ArgV[]) |
|---|
| 243 | { |
|---|
| 244 | // Output header. |
|---|
| 245 | std::cout << "The Cafu Font Maker, version " << __DATE__ << ".\n\n"; |
|---|
| 246 | std::cout << "Portions of this software are copyright (c) 2006 The FreeType Project\n(www.freetype.org). All rights reserved.\n\n"; |
|---|
| 247 | |
|---|
| 248 | // Initialize the FileMan by mounting the default file system. |
|---|
| 249 | // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of |
|---|
| 250 | // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application. |
|---|
| 251 | cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", ""); |
|---|
| 252 | cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE"); |
|---|
| 253 | cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE"); |
|---|
| 254 | |
|---|
| 255 | // Parse the command-line arguments. |
|---|
| 256 | std::string FontFileName=""; |
|---|
| 257 | std::string MaterialBaseName=""; |
|---|
| 258 | |
|---|
| 259 | for (int ArgNr=1; ArgNr<ArgC; ArgNr++) |
|---|
| 260 | { |
|---|
| 261 | if (strncmp(ArgV[ArgNr], "-m=" , 3)==0) MaterialBaseName=ArgV[ArgNr]+3; |
|---|
| 262 | else if (strcmp (ArgV[ArgNr], "-debug" )==0) DebugPNGs=true; |
|---|
| 263 | else if (ArgV[ArgNr][0]=='-') return Usage(); |
|---|
| 264 | else if (FontFileName=="") FontFileName=ArgV[ArgNr]; |
|---|
| 265 | else return Usage(); |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | if (FontFileName=="") return Usage(); |
|---|
| 269 | |
|---|
| 270 | |
|---|
| 271 | // Init the FreeType library. |
|---|
| 272 | FT_Library ftLib; |
|---|
| 273 | if (FT_Init_FreeType(&ftLib)!=0) |
|---|
| 274 | { |
|---|
| 275 | std::cout << "Error: Could not init the FreeType library.\n"; |
|---|
| 276 | return 1; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | |
|---|
| 280 | // Load the font face. |
|---|
| 281 | FT_Face ftFontFace; |
|---|
| 282 | if (FT_New_Face(ftLib, FontFileName.c_str(), 0, &ftFontFace)!=0) |
|---|
| 283 | { |
|---|
| 284 | std::cout << "Error: Could not load the font face from \"" << FontFileName << "\".\n"; |
|---|
| 285 | return 1; |
|---|
| 286 | } |
|---|
| 287 | |
|---|
| 288 | |
|---|
| 289 | const int PointSizes[]= { 12, 24, 48 }; // The sizes in points (1/72th of an inch). |
|---|
| 290 | |
|---|
| 291 | for (unsigned long SizeNr=0; SizeNr<3; SizeNr++) |
|---|
| 292 | { |
|---|
| 293 | const int SizeInPoints=PointSizes[SizeNr]; |
|---|
| 294 | |
|---|
| 295 | // Set the character size to SizeInPoints points at 72 DPI, which implies that the size in pixels is the same as SizeInPoints. |
|---|
| 296 | if (FT_Set_Char_Size(ftFontFace, SizeInPoints*64, 0, 72, 72)!=0) |
|---|
| 297 | { |
|---|
| 298 | std::cout << "Error: Could not set the character size to " << SizeInPoints << "pt.\n"; |
|---|
| 299 | continue; |
|---|
| 300 | } |
|---|
| 301 | |
|---|
| 302 | RectBitmapAllocatorT RBA(DebugPNGs ? 0xFFFF00FF : 0x00FFFFFF); // The default color is "invisible white", that is, RGB=1 and A=0. |
|---|
| 303 | ArrayT<unsigned long> CharToGlyphInfoNr; |
|---|
| 304 | ArrayT<GlyphInfoT> GlyphInfos; |
|---|
| 305 | |
|---|
| 306 | for (unsigned long CharNr=0; CharNr<256; CharNr++) |
|---|
| 307 | { |
|---|
| 308 | // const unsigned long GlyphIndex=(CharNr<128) ? FT_Get_Char_Index(ftFontFace, CharNr) : 0; |
|---|
| 309 | const unsigned long GlyphIndex=FT_Get_Char_Index(ftFontFace, CharNr); |
|---|
| 310 | |
|---|
| 311 | // Determine if we already have a GlyphInfoT object with that GlyphIndex, that is, determine whether GlyphIndex has occurred before. |
|---|
| 312 | unsigned long GlyphInfoNr; |
|---|
| 313 | |
|---|
| 314 | for (GlyphInfoNr=0; GlyphInfoNr<GlyphInfos.Size(); GlyphInfoNr++) |
|---|
| 315 | if (GlyphInfos[GlyphInfoNr].GlyphIndex==GlyphIndex) |
|---|
| 316 | break; |
|---|
| 317 | |
|---|
| 318 | CharToGlyphInfoNr.PushBack(GlyphInfoNr); |
|---|
| 319 | |
|---|
| 320 | // If we already have such a GlyphInfoT, we're done with this character. |
|---|
| 321 | if (GlyphInfoNr<GlyphInfos.Size()) continue; |
|---|
| 322 | |
|---|
| 323 | // Load the glyph at GlyphIndex into the glyph slot of ftFontFace (i.e. ftFontFace->glyph). |
|---|
| 324 | if (FT_Load_Glyph(ftFontFace, GlyphIndex, FT_LOAD_RENDER)!=0) |
|---|
| 325 | { |
|---|
| 326 | std::cout << "Error: Could not obtain the glyph at index " << GlyphIndex << ".\n"; |
|---|
| 327 | continue; // TODO: Is this proper error handling??? |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | |
|---|
| 331 | // Insert and store the new glyph info. |
|---|
| 332 | GlyphInfos.PushBackEmpty(); |
|---|
| 333 | GlyphInfos[GlyphInfos.Size()-1].GlyphIndex=GlyphIndex; |
|---|
| 334 | |
|---|
| 335 | ProcessNewGlyph(ftFontFace, GlyphInfos[GlyphInfos.Size()-1], RBA); |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | |
|---|
| 339 | // Save all resulting texture maps to disk. |
|---|
| 340 | for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++) |
|---|
| 341 | RBA.Bitmaps[TexMapNr]->SaveToDisk(va("FontImage_%i_%lu.png", SizeInPoints, TexMapNr).c_str()); |
|---|
| 342 | |
|---|
| 343 | |
|---|
| 344 | // Save the resulting font description to disk. |
|---|
| 345 | std::ofstream FontFile(va("FontDescr_%i.cfont", SizeInPoints).c_str(), std::ios::out); |
|---|
| 346 | // if (FontFile.bad()) ... |
|---|
| 347 | |
|---|
| 348 | const int sigdigits=std::numeric_limits<float>::digits10; |
|---|
| 349 | FontFile << std::setprecision(sigdigits); |
|---|
| 350 | |
|---|
| 351 | FontFile << "// Values that are not per-glyph, but global to the font face at the current scale (" << SizeInPoints << " pixels).\n"; |
|---|
| 352 | FontFile << "// The values are: ascender, descender and height.\n"; |
|---|
| 353 | FontFile << "global " << float(ftFontFace->size->metrics.ascender )/64.0f << " " |
|---|
| 354 | << float(ftFontFace->size->metrics.descender)/64.0f << " " |
|---|
| 355 | << float(ftFontFace->size->metrics.height )/64.0f << "\n"; |
|---|
| 356 | FontFile << "\n"; |
|---|
| 357 | |
|---|
| 358 | FontFile << "// For each of the " << CharToGlyphInfoNr.Size() << " characters, record the index into the glyph infos below.\n"; |
|---|
| 359 | for (unsigned long CharNr=0; CharNr<CharToGlyphInfoNr.Size(); CharNr++) |
|---|
| 360 | { |
|---|
| 361 | if (CharNr>0) |
|---|
| 362 | { |
|---|
| 363 | if ((CharNr % 16)==0) FontFile << "\n"; else FontFile << " "; |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | FontFile << CharToGlyphInfoNr[CharNr]; |
|---|
| 367 | } |
|---|
| 368 | FontFile << "\n\n"; |
|---|
| 369 | |
|---|
| 370 | FontFile << "// The glyphs below refer to larger bitmaps, represented by the following materials.\n"; |
|---|
| 371 | for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++) |
|---|
| 372 | FontFile << "matname Fonts/" << MaterialBaseName << "_" << SizeInPoints << "_" << TexMapNr << "\n"; |
|---|
| 373 | FontFile << "\n"; |
|---|
| 374 | |
|---|
| 375 | FontFile << "// The glyphs (num, bearing x, bearing y, advance x, width, height, bitmap num, x1, y1, x2, y2).\n"; |
|---|
| 376 | for (unsigned long GINr=0; GINr<GlyphInfos.Size(); GINr++) |
|---|
| 377 | { |
|---|
| 378 | const GlyphInfoT& Glyph=GlyphInfos[GINr]; |
|---|
| 379 | |
|---|
| 380 | FontFile << "glyph " << GINr |
|---|
| 381 | << " " << Glyph.BearingX |
|---|
| 382 | << " " << Glyph.BearingY |
|---|
| 383 | << " " << Glyph.AdvanceX |
|---|
| 384 | << " " << Glyph.Width |
|---|
| 385 | << " " << Glyph.Height |
|---|
| 386 | << " " << Glyph.BitmapNr |
|---|
| 387 | << " " << Glyph.s1 |
|---|
| 388 | << " " << Glyph.t1 |
|---|
| 389 | << " " << Glyph.s2 |
|---|
| 390 | << " " << Glyph.t2 |
|---|
| 391 | << "\n"; |
|---|
| 392 | } |
|---|
| 393 | FontFile << "\n"; |
|---|
| 394 | |
|---|
| 395 | FontFile << "// The kerning table.\n"; |
|---|
| 396 | for (unsigned long GI1Nr=0; GI1Nr<GlyphInfos.Size(); GI1Nr++) |
|---|
| 397 | for (unsigned long GI2Nr=0; GI2Nr<GlyphInfos.Size(); GI2Nr++) |
|---|
| 398 | { |
|---|
| 399 | const GlyphInfoT& gi1=GlyphInfos[GI1Nr]; |
|---|
| 400 | const GlyphInfoT& gi2=GlyphInfos[GI2Nr]; |
|---|
| 401 | |
|---|
| 402 | FT_Vector KerningVec; |
|---|
| 403 | |
|---|
| 404 | if (FT_Get_Kerning(ftFontFace, gi1.GlyphIndex, gi2.GlyphIndex, FT_KERNING_DEFAULT /*FT_KERNING_UNFITTED*/, &KerningVec)!=0) continue; |
|---|
| 405 | if (KerningVec.x==0) continue; |
|---|
| 406 | |
|---|
| 407 | const float HorKerning=float(KerningVec.x)/64.0f; |
|---|
| 408 | |
|---|
| 409 | FontFile << "k " << GI1Nr << " " << GI2Nr << " " << HorKerning << "\n"; |
|---|
| 410 | } |
|---|
| 411 | |
|---|
| 412 | |
|---|
| 413 | // Auto-create a materials definition file if MaterialBaseName was given. |
|---|
| 414 | if (MaterialBaseName!="") |
|---|
| 415 | { |
|---|
| 416 | std::ofstream CMatFile("Font.cmat", SizeNr==0 ? std::ios::out : std::ios::out | std::ios::app); |
|---|
| 417 | |
|---|
| 418 | for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++) |
|---|
| 419 | { |
|---|
| 420 | CMatFile << "Fonts/" << MaterialBaseName << "_" << SizeInPoints << "_" << TexMapNr << "\n"; // Fonts/Arial_24_0 |
|---|
| 421 | CMatFile << "{\n"; |
|---|
| 422 | CMatFile << " diffusemap " << "FontImage" /*MaterialBaseName*/ << "_" << SizeInPoints << "_" << TexMapNr << ".png, minFilter linear, noScaleDown\n"; |
|---|
| 423 | CMatFile << "\n"; |
|---|
| 424 | CMatFile << " noDynLight // Should be replaced e.g. by \"LightShader none\"...\n"; |
|---|
| 425 | CMatFile << "\n"; |
|---|
| 426 | CMatFile << " blendFunc src_alpha one_minus_src_alpha\n"; |
|---|
| 427 | CMatFile << " red ambientLightRed // Hmmm. Maybe we should rather use fParam0...fParam2 here.\n"; |
|---|
| 428 | CMatFile << " green ambientLightGreen\n"; |
|---|
| 429 | CMatFile << " blue ambientLightBlue\n"; |
|---|
| 430 | CMatFile << " ambientMask d // Don't write into the depth buffer!\n"; |
|---|
| 431 | CMatFile << "}\n"; |
|---|
| 432 | CMatFile << "\n"; |
|---|
| 433 | } |
|---|
| 434 | } |
|---|
| 435 | } |
|---|
| 436 | |
|---|
| 437 | |
|---|
| 438 | // Clean up and quit. |
|---|
| 439 | FT_Done_Face(ftFontFace); |
|---|
| 440 | FT_Done_FreeType(ftLib); |
|---|
| 441 | return 0; |
|---|
| 442 | } |
|---|