Last active
May 12, 2016 01:41
-
-
Save sorrge/7056735 to your computer and use it in GitHub Desktop.
Загрузка растеризованного шрифта, сделанного в BMFont ( http://www.angelcode.com/products/bmfont/ ) и его отрисовка с помощью OpenGL 3.3. Дополнительно необходима библиотека glm ( http://glm.g-truc.net/0.9.4/index.html ) и процедура загрузки картинок.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 330 core | |
// Input vertex data, different for all executions of this shader. | |
layout(location = 0) in vec2 position2D; | |
layout(location = 1) in vec4 color; | |
layout(location = 2) in vec2 textureCoord; | |
// Output data | |
out vec4 pixColor; | |
out vec2 pixTextureCoord; | |
void main() | |
{ | |
gl_Position = vec4(position2D, 0, 1); | |
pixColor = color; | |
pixTextureCoord = textureCoord; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
BASIC BMFont example implementation with Kerning, for C++ and OpenGL 2.0 | |
Modified for OpenGL 3.3 | |
This is free and unencumbered software released into the public domain. | |
Anyone is free to copy, modify, publish, use, compile, sell, or | |
distribute this software, either in source code form or as a compiled | |
binary, for any purpose, commercial or non-commercial, and by any | |
means. | |
In jurisdictions that recognize copyright laws, the author or authors | |
of this software dedicate any and all copyright interest in the | |
software to the public domain. We make this dedication for the benefit | |
of the public at large and to the detriment of our heirs and | |
successors. We intend this dedication to be an overt act of | |
relinquishment in perpetuity of all present and future rights to this | |
software under copyright law. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
For more information, please refer to <http://unlicense.org/> | |
-------------------------------------------------------------------------------- | |
These editors can be used to generate BMFonts: | |
• http://www.angelcode.com/products/bmfont/ (free, windows) | |
• http://glyphdesigner.71squared.com/ (commercial, mac os x) | |
• http://www.n4te.com/hiero/hiero.jnlp (free, java, multiplatform) | |
• http://slick.cokeandcode.com/demos/hiero.jnlp (free, java, multiplatform) | |
Some code below based on code snippets from this gamedev posting: | |
http://www.gamedev.net/topic/330742-quick-tutorial-variable-width-bitmap-fonts/ | |
Although I'm giving this away, I'd appreciate an email with fixes or better code! | |
aaedev@gmail.com 2012 | |
*/ | |
#include "stdafx.h" | |
#include "bmfont.h" | |
#include "FileStuff.h" | |
//Todo: Add buffer overflow checking. | |
#define MAX_BUFFER 256 | |
bool BMFont::ParseFont(const string &fontfile ) | |
{ | |
std::ifstream Stream(fontfile); | |
std::string Line; | |
std::string Read, Key, Value; | |
std::size_t i; | |
int first,second,amount; | |
KearningInfo K; | |
CharDescriptor C; | |
while( !Stream.eof() ) | |
{ | |
std::stringstream LineStream; | |
std::getline( Stream, Line ); | |
LineStream << Line; | |
//read the line's type | |
LineStream >> Read; | |
if( Read == "common" ) | |
{ | |
//this holds common data | |
while( !LineStream.eof() ) | |
{ | |
std::stringstream Converter; | |
LineStream >> Read; | |
i = Read.find( '=' ); | |
Key = Read.substr( 0, i ); | |
Value = Read.substr( i + 1 ); | |
//assign the correct value | |
Converter << Value; | |
if( Key == "lineHeight" ) | |
{Converter >> LineHeight;} | |
else if( Key == "base" ) | |
{Converter >> Base;} | |
else if( Key == "scaleW" ) | |
{Converter >> Width;} | |
else if( Key == "scaleH" ) | |
{Converter >> Height;} | |
else if( Key == "pages" ) | |
{Converter >> Pages;} | |
else if( Key == "outline" ) | |
{Converter >> Outline;} | |
} | |
} | |
else if( Read == "char" ) | |
{ | |
//This is data for each specific character. | |
int CharID = 0; | |
while( !LineStream.eof() ) | |
{ | |
std::stringstream Converter; | |
LineStream >> Read; | |
i = Read.find( '=' ); | |
Key = Read.substr( 0, i ); | |
Value = Read.substr( i + 1 ); | |
//Assign the correct value | |
Converter << Value; | |
if( Key == "id" ) | |
{Converter >> CharID;} | |
else if( Key == "x" ) | |
{ Converter >> C.x;} | |
else if( Key == "y" ) | |
{ Converter >> C.y;} | |
else if( Key == "width" ) | |
{ Converter >> C.Width;} | |
else if( Key == "height" ) | |
{ Converter >> C.Height;} | |
else if( Key == "xoffset" ) | |
{ Converter >> C.XOffset;} | |
else if( Key == "yoffset" ) | |
{ Converter >> C.YOffset;} | |
else if( Key == "xadvance" ) | |
{ Converter >> C.XAdvance;} | |
else if( Key == "page" ) | |
{ Converter >> C.Page;} | |
} | |
Chars.insert(std::map<int,CharDescriptor>::value_type( CharID,C )); | |
} | |
else if( Read == "kernings" ) | |
{ | |
while( !LineStream.eof() ) | |
{ | |
std::stringstream Converter; | |
LineStream >> Read; | |
i = Read.find( '=' ); | |
Key = Read.substr( 0, i ); | |
Value = Read.substr( i + 1 ); | |
//assign the correct value | |
Converter << Value; | |
if( Key == "count" ) | |
{Converter >> KernCount; } | |
} | |
} | |
else if( Read == "kerning" ) | |
{ | |
while( !LineStream.eof() ) | |
{ | |
std::stringstream Converter; | |
LineStream >> Read; | |
i = Read.find( '=' ); | |
Key = Read.substr( 0, i ); | |
Value = Read.substr( i + 1 ); | |
//assign the correct value | |
Converter << Value; | |
if( Key == "first" ) | |
{Converter >> K.First; Converter >> first; } | |
else if( Key == "second" ) | |
{Converter >> K.Second; Converter >> second; } | |
else if( Key == "amount" ) | |
{Converter >> K.Amount; Converter >> amount;} | |
} | |
//LOG_DEBUG("Done with this pass"); | |
Kearn.push_back(K); | |
} | |
else if(Read == "page") | |
{ | |
int id; | |
string file; | |
while( !LineStream.eof() ) | |
{ | |
std::stringstream Converter; | |
LineStream >> Read; | |
i = Read.find( '=' ); | |
Key = Read.substr( 0, i ); | |
Value = Read.substr( i + 1 ); | |
//assign the correct value | |
Converter << Value; | |
if( Key == "id" ) | |
{ | |
Converter >> id; | |
if(id != 0) | |
throw "Only single page fonts are supported at the moment"; | |
} | |
else if( Key == "file" ) | |
{ | |
Converter >> file; | |
boost::algorithm::erase_all(file, "\""); | |
size_t found; | |
found=fontfile.find_last_of("/\\"); | |
auto dir = fontfile.substr(0,found); | |
string png = dir.empty() ? file : dir + "/" + file; | |
ftexid = pngTextureLoad(png, 0, 0, true); | |
} | |
} | |
} | |
} | |
Stream.close(); | |
return true; | |
} | |
int BMFont::GetKerningPair(int first, int second) | |
{ | |
if ( KernCount && UseKern ) //Only process if there actually is kerning information | |
{ | |
//Kearning is checked for every character processed. This is expensive in terms of processing time. | |
for (int j = 0; j < KernCount; j++ ) | |
{ | |
if (Kearn[j].First == first && Kearn[j].Second == second) | |
{ | |
//LOG_DEBUG("Kerning Pair Found!!!");kerning._Left | |
// LOG_DEBUG("FIRST: %d SECOND: %d offset %d",first,second,Kearn[j].Amount); | |
return Kearn[j].Amount; | |
} | |
} | |
} | |
return 0; | |
} | |
float BMFont::GetStringWidth(const char *string) | |
{ | |
float total=0; | |
CharDescriptor *f; | |
for (int i = 0; i != strlen(string); i++) | |
{ | |
f=&Chars[string[i]]; | |
total+=f->XAdvance; | |
} | |
return total * fscale; | |
} | |
bool BMFont::LoadFont(const string &fontfile) | |
{ | |
std::ifstream Stream(fontfile); | |
if ( !Stream.is_open() ) | |
{ | |
throw "Cannot Find Font File " + fontfile; | |
} | |
Stream.close(); | |
//LOG_DEBUG("Starting to Parse Font %s",fontfile); | |
ParseFont(fontfile); | |
//LOG_DEBUG("Finished Parsing Font %s",fontfile); | |
KernCount = (int) Kearn.size(); | |
return true; | |
} | |
#define BUFFER_OFFSET(i) ((char *)NULL + (i)) | |
void BMFont::Render_String(size_t len) | |
{ | |
glDisable(GL_DEPTH_TEST); | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
//Enable Client States | |
glUseProgram(shader); | |
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); | |
glBufferData(GL_ARRAY_BUFFER, txlist.size() * sizeof(txdata), &txlist[0].x, GL_STATIC_DRAW); | |
glEnableVertexAttribArray(0); | |
glVertexAttribPointer(0, 2, GL_FLOAT, GL_TRUE, sizeof(txdata), 0); | |
glEnableVertexAttribArray(1); | |
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(txdata), BUFFER_OFFSET(sizeof(float) * 4)); | |
glEnableVertexAttribArray(2); | |
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(txdata), BUFFER_OFFSET(sizeof(float) * 2)); | |
glBindTexture(GL_TEXTURE_2D, ftexid); | |
GLint uniform_mytexture = glGetUniformLocation(shader, "textureSampler"); | |
glUniform1i(uniform_mytexture, /*GL_TEXTURE*/0); | |
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)len * 6); | |
glDisableVertexAttribArray(0); | |
glDisableVertexAttribArray(1); | |
glDisableVertexAttribArray(2); | |
} | |
void BMFont::Print(float x, float y, const char *fmt, ...) | |
{ | |
float CurX = (float) x; | |
float CurY = (float) y; | |
float DstX = 0.0; | |
float DstY = 0.0; | |
size_t Flen; | |
float adv = (float) 1.0/Width; // Font texture atlas spacing. | |
char text[512] = ""; // Holds Our String | |
CharDescriptor *f; // Pointer to font. | |
va_list ap; // Pointer To List Of Arguments | |
if (fmt == NULL) // If There's No Text | |
return; // Do Nothing | |
va_start(ap, fmt); // Parses The String For Variables | |
vsprintf_s(text, fmt, ap); // And Converts Symbols To Actual Numbers | |
va_end(ap); | |
//y= y + LineHeight; //This can be used to flip rendering | |
Flen = strlen(text); | |
//Todo: Add caching to make this much faster | |
txlist.clear(); | |
for (size_t i = 0; i != Flen; ++i) | |
{ | |
f=&Chars[text[i]]; | |
CurX = x + f->XOffset; | |
CurY = y + f->YOffset; | |
DstX = CurX + f->Width; | |
DstY = CurY + f->Height; | |
vec2 fCur = IntCoordToFloat(vec2(CurX, CurY)) * fscale; | |
vec2 fDst = IntCoordToFloat(vec2(DstX, DstY)) * fscale; | |
txlist.push_back(txdata(fCur.x, fDst.y, adv*f->x, 1.0f -(adv*(f->y+f->Height)), fcolor)); | |
txlist.push_back(txdata(fCur.x, fCur.y, adv*f->x, 1.0f -(adv*f->y), fcolor)); | |
txlist.push_back(txdata(fDst.x, fCur.y, adv*(f->x+f->Width),1.0f -(adv*f->y), fcolor)); | |
txlist.push_back(txdata(fCur.x, fDst.y, adv*f->x, 1.0f -(adv*(f->y+f->Height)), fcolor)); | |
txlist.push_back(txdata(fDst.x, fCur.y, adv*(f->x+f->Width),1.0f -(adv*f->y), fcolor)); | |
txlist.push_back(txdata(fDst.x, fDst.y, adv*(f->x+f->Width),1.0f-(adv*(f->y+f->Height)), fcolor)); | |
//Only check kerning if there is greater then 1 character and | |
//if the check character is 1 less then the end of the string. | |
if (Flen > 1 && i < Flen) | |
x += GetKerningPair(text[i],text[i+1]); | |
x += f->XAdvance; | |
} | |
Render_String(strlen(text)); | |
} | |
void BMFont::PrintCenter(float y, const char *string) | |
{ | |
int x = 0; | |
CharDescriptor *f; | |
size_t len = strlen(string); | |
for (size_t i = 0; i != len; ++i) | |
{ | |
f=&Chars[string[i]]; | |
if (len > 1 && i < len) | |
x += GetKerningPair(string[i],string[i+1]); | |
x += f->XAdvance; | |
} | |
Print( (float)(windowDimensions.x/2) - (x/2) , y, string); | |
} | |
vec2 BMFont::IntCoordToFloat(const vec2& iPos) | |
{ | |
return vec2(iPos.x / windowDimensions.x * 2 - 1, -iPos.y / windowDimensions.y * 2 + 1); | |
} | |
BMFont::~BMFont() | |
{ | |
Chars.clear(); | |
Kearn.clear(); | |
txlist.clear(); | |
glDeleteProgram(shader); | |
glDeleteTextures(1, &ftexid); | |
glDeleteBuffers(1, &vertexBuffer); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
BMFont example implementation with Kerning, for C++ and OpenGL 2.0 | |
Modified for OpenGL 3.3 | |
This is free and unencumbered software released into the public domain. | |
Anyone is free to copy, modify, publish, use, compile, sell, or | |
distribute this software, either in source code form or as a compiled | |
binary, for any purpose, commercial or non-commercial, and by any | |
means. | |
In jurisdictions that recognize copyright laws, the author or authors | |
of this software dedicate any and all copyright interest in the | |
software to the public domain. We make this dedication for the benefit | |
of the public at large and to the detriment of our heirs and | |
successors. We intend this dedication to be an overt act of | |
relinquishment in perpetuity of all present and future rights to this | |
software under copyright law. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
For more information, please refer to <http://unlicense.org/> | |
*/ | |
#ifndef __BMFONT__ | |
#define __BMFONT__ | |
#include "FileStuff.h" | |
#ifndef MAKE_RGBA | |
#define MAKE_RGBA(r,g,b,a) (r | (g << 8) | (b << 16) | (a << 24)) | |
#endif | |
#ifndef GET_BLUE | |
#define GET_BLUE(rgba) (( (rgba)>>16 ) & 0xff ) | |
#endif | |
#ifndef GET_GREEN | |
#define GET_GREEN(rgba) (( (rgba)>>8 ) & 0xff ) | |
#endif | |
#ifndef GET_RED | |
#define GET_RED(rgba) ( rgba & 0xff ) | |
#endif | |
#ifndef GET_ALPHA | |
#define GET_ALPHA(rgba) (( (rgba)>>24 ) & 0xff) | |
#endif | |
struct txdata { | |
float x, y; | |
float tx,ty; | |
vec4 color; | |
txdata(float x, float y, float tx, float ty, const vec4 &_color) | |
{ | |
this->x = x; | |
this->y = y; | |
this->tx = tx; | |
this->ty = ty; | |
color = _color; | |
} | |
}; | |
class KearningInfo | |
{ | |
public: | |
short First; | |
short Second; | |
short Amount; | |
KearningInfo() : First( 0 ), Second( 0 ), Amount( 0 ) { } | |
}; | |
class CharDescriptor | |
{ | |
public: | |
short x, y; | |
short Width; | |
short Height; | |
short XOffset; | |
short YOffset; | |
short XAdvance; | |
short Page; | |
CharDescriptor() : x( 0 ), y( 0 ), Width( 0 ), Height( 0 ), XOffset( 0 ), YOffset( 0 ), | |
XAdvance( 0 ), Page( 0 ) | |
{ } | |
}; | |
class BMFont | |
{ | |
public: | |
bool LoadFont(const string& fontFile); | |
void SetColor(const vec4& color) {fcolor = color;} | |
void SetBlend(int b) {fblend = b;} | |
void SetScale(float scale){fscale = scale;} | |
float GetHeight(){return LineHeight * fscale;} | |
void UseKerning(bool b) {UseKern = b;} | |
void Print(float, float, const char *,...); | |
void PrintCenter( float, const char *); | |
float GetStringWidth(const char *); | |
BMFont(const uvec2& _windowDimensions) | |
{ | |
glGenBuffers(1, &vertexBuffer); | |
shader = LoadShaders("shaders/2dtexcol-vert.glsl", "shaders/texcol-frag.glsl"); | |
SetColor(vec4(1, 1, 1, 1)); | |
windowDimensions = _windowDimensions; | |
KernCount = 0; | |
ftexid = -1; | |
fblend = 0; | |
fscale = 1; | |
UseKern = true; | |
}; | |
virtual ~BMFont(); | |
private: | |
short LineHeight; | |
short Base; | |
short Width; | |
short Height; | |
short Pages; | |
short Outline; | |
short KernCount; | |
bool UseKern; | |
std::map<int,CharDescriptor> Chars; | |
std::vector<KearningInfo> Kearn; | |
vec4 fcolor; | |
GLuint ftexid; | |
float fscale; | |
int fblend; | |
void BMFont::ReplaceExtension(std::string &str, std::string rep); | |
char* BMFont::replace_str(char *str, char *orig, char *rep); | |
void Render_String(size_t len); | |
bool ParseFont(const string &fontFile); | |
int GetKerningPair(int, int); | |
vec2 IntCoordToFloat(const vec2& iPos); | |
std::vector<txdata> txlist; | |
GLuint vertexBuffer; | |
GLuint shader; | |
uvec2 windowDimensions; | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
uvec2 windowSize = ... | |
BMFont font(windowSize); | |
if(!font.LoadFont("fonts/arial32.fnt")) | |
throw "Can't load font"; | |
font.Print(0, 30, "Hello"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 330 core | |
in vec4 pixColor; | |
in vec2 pixTextureCoord; | |
uniform sampler2D textureSampler; | |
out vec4 color; | |
void main() | |
{ | |
color = pixColor * texture(textureSampler, pixTextureCoord).rgba; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment