nenuzhno-engine_iter1/src/renderer/font.cpp

331 lines
6.9 KiB
C++

#include <cstring>
#include <string>
#include <fstream>
#include <stdint.h>
using namespace std;
#include "log.h"
#include "system/FileSystem.h"
#include "graphics/platform_gl.h"
#include "graphics/texture.h"
#include "renderer/font.h"
#include "resource/ResourceManager.h"
struct bmfontInfoBlock_t
{
uint16_t fontSize;
uint8_t bitField;//bit 0: smooth, bit 1: unicode, bit 2: italic, bit 3: bold, bit 4: fixedHeigth, bits 5-7: reserved
uint8_t charSet;
uint16_t stretchH;
uint8_t aa;
uint8_t paddingUp;
uint8_t paddingRight;
uint8_t paddingDown;
uint8_t paddingLeft;
uint8_t spacingHoriz;
uint8_t spacingVert;
uint8_t outline;//added with version 2
//fontName n+1 string 14 null terminated string with length n
};
struct bmfontCommonBlock_t
{
uint16_t lineHeight;
uint16_t base;
uint16_t scaleW;
uint16_t scaleH;
uint16_t pages;
uint8_t bitField;//bits 0-6: reserved, bit 7: packed
uint8_t alphaChnl;
uint8_t redChnl;
uint8_t greenChnl;
uint8_t blueChn;
//padding
};
struct bmfontChar_t
{
uint32_t id;//These fields are repeated until all characters have been described
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
int16_t xoffset;
int16_t yoffset;
int16_t xadvance;
uint8_t page;
uint8_t chnl;
};
bool Font::LoadBMFont(const char *fileName,ResourceManager *resMan)
{
string filePath = "fonts/"+string(fileName)+".fnt";
IFile *file = g_fs.Open(filePath.c_str());
if(!file){
return false;
}
char ident[4];
file->Read(ident, 4);
if(ident[0]!='B'||ident[1]!='M'||ident[2]!='F'){
Log("LoadBMFont: unknown format\n");
g_fs.Close(file);
return false;
}
//Log("BMFont \"%s\" version %d\n", fileName, ident[3]);
char blockType = 0;
int blockSize = 0;
//info block
file->Read(&blockType,1);
if(blockType==1){
file->Read(&blockSize, 4);
char *block = new char[blockSize];
file->Read(block, blockSize);
//bmfontInfoBlock_t *infoBlock = (bmfontInfoBlock_t *)block;
//LOG("font size %d, charset %d\n",infoBlock->fontSize,infoBlock->charSet);
//LOG("name %s\n",block+14);
delete[] block;
}else{
Log("first block type is not 1\n");
g_fs.Close(file);
return false;
}
//common block
//int numPages=0;
int texW=1;
int texH=1;
file->Read(&blockType,1);
if(blockType==2){
file->Read(&blockSize, 4);
char *block = new char[blockSize];
file->Read(block, blockSize);
bmfontCommonBlock_t *commonBlock = (bmfontCommonBlock_t *)block;
//LOG("line height %d, base %d, scale %d %d, pages %d\n",commonBlock->lineHeight,commonBlock->base,commonBlock->scaleW,commonBlock->scaleH,commonBlock->pages);
//numPages = commonBlock->pages;
texW = commonBlock->scaleW;
texH = commonBlock->scaleH;
base = (float)commonBlock->base/texW;
delete[] block;
}
//pages block
//TODO: multiple pages
string pageName;
file->Read(&blockType,1);
if(blockType==3){
file->Read(&blockSize, 4);
char *block = new char[blockSize];
file->Read(block, blockSize);
//LOG("page 0: %s\n",block);
pageName = block;
delete[] block;
}
tex = resMan->GetTexture(("fonts/"+pageName).c_str());
//chars block
int numChars = 0;
file->Read(&blockType,1);
if(blockType==4){
file->Read(&blockSize, 4);
char *block = new char[blockSize];
file->Read(block, blockSize);
numChars = blockSize/20;//sizeof char
//LOG("numChars %d\n",numChars);
bmfontChar_t *curChar = (bmfontChar_t *)block;
for(int i=0; i<numChars; i++){
if(curChar->id>255){
Log("LoadBMFont: char %d id %d too big (%s)\n",i,curChar->id,fileName);
continue;
}
chars[curChar->id].x = (float)curChar->x/texW;
chars[curChar->id].y = (float)curChar->y/texH;
chars[curChar->id].width = (float)curChar->width/texW;
chars[curChar->id].height = (float)curChar->height/texH;
chars[curChar->id].xoffset = (float)curChar->xoffset/texW;
chars[curChar->id].yoffset = (float)curChar->yoffset/texH;
chars[curChar->id].xAdv = (float)curChar->xadvance/texW;
curChar++;
}
delete[] block;
}
bmfont = true;
g_fs.Close(file);
return true;
}
void Font::Print(const char *text, float x, float y, float size)
{
if(bmfont)
PrintBMFont(text,x,y,size);
else
PrintOLD(text,x,y,size);
}
void Font::PrintBMFont(const char *text, float x, float y, float size)
{
if(!tex)
return;
int l = strlen(text);
if(!l)
return;
GLfloat *verts = new GLfloat[l*6*4];
int cv = 0;
float cx = x;
float cy = y;
for(int i=0; i<l; i++)
{
int c = text[i];
if(c=='\n')
{
cx = x;
cy -= base*size*1.1f;
continue;
}
float tx = chars[c].x;
float tw = chars[c].width;
float ty = chars[c].y;
float th = chars[c].height;
float xo = chars[c].xoffset*size;
float yo = (base-th-chars[c].yoffset)*size;
verts[cv*4]=cx+xo;
verts[cv*4+1]=cy+yo;
verts[cv*4+2]=tx;
verts[cv*4+3]=ty+th;
cv++;
verts[cv*4]=cx+xo;
verts[cv*4+1]=cy+yo+size*th;
verts[cv*4+2]=tx;
verts[cv*4+3]=ty;
cv++;
verts[cv*4]=cx+xo+size*tw;
verts[cv*4+1]=cy+yo;
verts[cv*4+2]=tx+tw;
verts[cv*4+3]=ty+th;
cv++;
verts[cv*4]=cx+xo+size*tw;
verts[cv*4+1]=cy+yo;
verts[cv*4+2]=tx+tw;
verts[cv*4+3]=ty+th;
cv++;
verts[cv*4]=cx+xo;
verts[cv*4+1]=cy+yo+size*th;
verts[cv*4+2]=tx;
verts[cv*4+3]=ty;
cv++;
verts[cv*4]=cx+xo+size*tw;
verts[cv*4+1]=cy+yo+size*th;
verts[cv*4+2]=tx+tw;
verts[cv*4+3]=ty;
cv++;
cx += size * chars[c].xAdv;
}
tex->Bind();
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, verts);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 16, verts+2);
glDrawArrays(GL_TRIANGLES, 0, cv);
delete[] verts;
}
void Font::PrintOLD(const char *text, float x, float y, float size)
{
// static bool first = true;
if(!tex)
return;
int l = strlen(text);
GLfloat *verts=new GLfloat[l*6*4];
int cv=0;
float cx=x;
float cy=y;
size *= 0.125;
for(int i=0; i<l; i++)
{
int c = tolower(text[i]);
//if(first)
// LOG("c %d %c %d\n", i,text[i],c);
if(c=='\n')
{
cx = x;
cy -= size*1.1f;
continue;
}
if(c>=' '&&c<='?')
{
c -= ' ';
}
else if(c>='a'&&c<='x')
{
c -= 'a';
c += 32;
}
else
c = -1;
if(c>=0)
{
float tx = (c%8)/8.0f;
float tw = 1.0f/8.0f;
float ty = (c/8)/7.0f;
float th = 1.0f/7.0f;
verts[cv*4]=cx;
verts[cv*4+1]=cy;
verts[cv*4+2]=tx;
verts[cv*4+3]=ty+th;
cv++;
verts[cv*4]=cx;
verts[cv*4+1]=cy+size;
verts[cv*4+2]=tx;
verts[cv*4+3]=ty;
cv++;
verts[cv*4]=cx+size;
verts[cv*4+1]=cy;
verts[cv*4+2]=tx+tw;
verts[cv*4+3]=ty+th;
cv++;
verts[cv*4]=cx+size;
verts[cv*4+1]=cy;
verts[cv*4+2]=tx+tw;
verts[cv*4+3]=ty+th;
cv++;
verts[cv*4]=cx;
verts[cv*4+1]=cy+size;
verts[cv*4+2]=tx;
verts[cv*4+3]=ty;
cv++;
verts[cv*4]=cx+size;
verts[cv*4+1]=cy+size;
verts[cv*4+2]=tx+tw;
verts[cv*4+3]=ty;
cv++;
}
cx+=size*13.0f/12.0f;
}
tex->Bind();
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, verts);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 16, verts+2);
glDrawArrays(GL_TRIANGLES, 0, cv);
delete[] verts;
//first=false;
}