Sign in to follow this  

Calculating UVs for Textured Font (need help)

This topic is 3667 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been trying to calculate the UVs for my textured font (which has variable width) without much success... Given the known position (X, Y) of the current character, the dimensions of the source image (iW, iH) and the size of the char (W, H) how can I calculate the UVs for all 4 verts? All the examples I've seen out there are either just the loader (ie the "renderer" generates the UVs so theres no code to learn from) or they are using constant sizes for each character! Any examples?, Any suggestions? (other than "download this lib and use it") Thanks!

Share this post


Link to post
Share on other sites
If it is any help, this is my Direct3D TextureFont class in total. You can see the UVs being computed in the Add() method:


class TextureFont
{
private:
struct Info { int X,Y,W,H; };
Texture Tex; VertexBuffer V; char Index[256]; std::vector<Info> Pos; int Space,TexSize;

public:
TextureFont(){ std::fill(Index,Index+256,-1); }

bool Acquire(const std::string &Path,int TextureSize);
bool Release();

float Width(const std::string &S) const;
float Height() const;

void Add(const std::string &S,float X,float Y,DWORD Color=ColorWhite);
void Render(GraphicsDevice &G);

bool SupportsChar(char C);
};




bool TextureFont::Acquire(const std::string &Path,int TextureSize)
{
if(!Tex.Acquire(TextureSize,TextureSize)) return false;
std::ifstream Is(Path.c_str(),std::ios::binary); if(Is.fail()){ Tex.Release(); return false; }

TexSize=TextureSize;

int No,Tx=0,Ty=0;

Is.read(reinterpret_cast<char*>(&No),sizeof(int));
Is.read(reinterpret_cast<char*>(&Space),sizeof(int));
Is.read(reinterpret_cast<char*>(Index),256);

Pos.resize(No);

for(int I=0;I<No;++I)
{
int W,H;
Is.read(reinterpret_cast<char*>(&W),sizeof(int));
Is.read(reinterpret_cast<char*>(&H),sizeof(int));

if(Ty+H>=TextureSize){ Tex.Release(); return false; }
if(Tx+W>=TextureSize){ Tx=0; Ty+=H; }

unsigned char *Data=new unsigned char[(W*4)*H];
Is.read(reinterpret_cast<char*>(Data),(W*4)*H);

Tex.Fill(Tx,Ty,W,H,(const void*)Data);

Pos[I].X=Tx; Pos[I].Y=Ty; Pos[I].W=W; Pos[I].H=H;

Tx+=W;

delete [] Data;
}

return true;
}

bool TextureFont::Release()
{
Tex.Release();
return true;
}

float TextureFont::Width(const std::string &S) const
{
int W=0;

for(size_t I=0;I<S.size();++I)
{
if(S[I]==' ') W+=Space;
else if(Index[S[I]]!=-1) W+=Pos[Index[S[I]]].W;
}

return float(W);
}

float TextureFont::Height() const
{
if(Pos.size()==0) return 0; return float(Pos[0].H);
}

void TextureFont::Add(const std::string &S,float X,float Y,DWORD Color)
{
if(Pos.size()==0) return;

float Cx=X;
for(size_t I=0;I<S.size();++I)
{
if(S[I]==' ') Cx+=float(Space);
else
{
int Ind=Index[S[I]]; if(Ind==-1) Ind=0;

float Tx=float(Pos[Ind].X);
float Ty=float(Pos[Ind].Y);
float W=float(Pos[Ind].W);
float H=float(Pos[Ind].H);

V.Add(Vertex(Cx-0.5f,Y-0.5f,Tx/float(TexSize),Ty/float(TexSize),Color));
V.Add(Vertex(Cx+W-0.5f,Y-0.5f,(Tx+W)/float(TexSize),Ty/float(TexSize),Color));
V.Add(Vertex(Cx+W-0.5f,Y+H-0.5f,(Tx+W)/float(TexSize),(Ty+H)/float(TexSize),Color));

V.Add(Vertex(Cx-0.5f,Y-0.5f,Tx/float(TexSize),Ty/float(TexSize),Color));
V.Add(Vertex(Cx+W-0.5f,Y+H-0.5f,(Tx+W)/float(TexSize),(Ty+H)/float(TexSize),Color));
V.Add(Vertex(Cx-0.5f,Y+H-0.5f,Tx/float(TexSize),(Ty+H)/float(TexSize),Color));

Cx+=W;
}
}
}

void TextureFont::Render(GraphicsDevice &G)
{
G.SetTexture(Tex);
G.RenderTriangleList(V);
V.Clear();
}

bool TextureFont::SupportsChar(char C)
{
return(C==' ' || Index[C]!=-1);
}



Should all be self-explanatory I hope. I use a custom font file format that basically has a 256 byte array at the start of the file with an index to each letter, or -1 if not in the font file, then just a sequence of each character as the width and height followed by the ARGB data.

Each character texture is then atlased onto a single texture at load time and its location stored in the Info array.

HTH

Share this post


Link to post
Share on other sites
This is what I have right now:
(excuse the obscure syntax)



TexSizeX = ImageWidth;
TexSizeY = ImageHeight;

Tx = x; // Current Position X
Ty = y; // Current Position Y
W = Size\cx; // Glyph Width
H = Size\cy; // Glyph Height

*font\Strips[m]\tex[0]\x = (Tx)/TexSizeX; // btw: m = i (iteration)
*font\Strips[m]\tex[0]\y = (Ty)/TexSizeY;

*font\Strips[m]\tex[1]\x = (Tx+W)/TexSizeX;
*font\Strips[m]\tex[1]\y = (Ty)/TexSizeY;

*font\Strips[m]\tex[2]\x = (Tx+W)/TexSizeX;
*font\Strips[m]\tex[2]\y = (Ty+H)/TexSizeY;

*font\Strips[m]\tex[3]\x = (Tx)/TexSizeX;
*font\Strips[m]\tex[3]\y = (Ty+H)/TexSizeY;




As you can see, I'm pre-calculating everything to my structure, this structure is saved to a file, later on the engine loads this file and simply assigns the uvs / verts.

The verts part are ok, they always were .. I'm using pixel-space for them.
However, the UVs are all wrong.

string: "123 ABC", output:
[image]http://i15.tinypic.com/869agw7.png[/image]

Any ideas?


PS: OpenGL



Share this post


Link to post
Share on other sites
Ok I figured they were off-scale so I tried this:



*font\Strips[m]\tex[3]\x = (Tx)/TexSizeX;
*font\Strips[m]\tex[3]\y = 1.0-(Ty)/TexSizeY;

*font\Strips[m]\tex[2]\x = (Tx+W)/TexSizeX;
*font\Strips[m]\tex[2]\y = 1.0-(Ty)/TexSizeY;

*font\Strips[m]\tex[1]\x = (Tx+W)/TexSizeX;
*font\Strips[m]\tex[1]\y = 1.0-(Ty+H)/TexSizeY;

*font\Strips[m]\tex[0]\x = (Tx)/TexSizeX;
*font\Strips[m]\tex[0]\y = 1.0-(Ty+H)/TexSizeY;




That worked, the uvs and verts are 100% ok now, but my string is not being displayed correctly (obviously this is another issue, so I'll move away from the coordinates now that they are fine)

Thanks :)

Share this post


Link to post
Share on other sites
Quote:
Original post by esteban nielsen
I don't understand why 6 vertex calls instead of just 4?, Can you explain me?


Glad you've got this working now.

Just to answer the question, I've not bothered using an index buffer in this particular bit of code, so I need six vertices to make up the two triangles that form a quad. I believe that OpenGL supports quads, but Direct3D does not directly.

Share this post


Link to post
Share on other sites
That makes sense!

What doesn't make sense is that my code is displaying +1 char on anything I type, for instance "123" would show up as "234". I tried stepping 1 down on the display list, but it doesn't work properly. I assume it's a bug in my UVs, still? - Perhaps I should down-step the coordinates I'm using to calculate them?

Edit: indeed, that was it!

Share this post


Link to post
Share on other sites
Thanks again :)

This has taught me not to rely on "tutorials" but rather use a little of thinking to solve this kind of issues. Before I even got to download a NEHE tutorial regarding textured fonts, I was doing something similar to what you've shown me, my problem though was that I was calculating the UVs the other way around, that with the displacement issue I just had, rendered the output useless. So I thought I was doing something way too wrong to even work, thats when I decided to download a tutorial... Most of those tutorials use tricks and dirty hacks to do everything.

In reality, I don't see how those are helping out people, they might end up having an output but it's far from good.

I might get a book on OpenGL + gamedev, any good ones worth a look?

Thanks.

Share this post


Link to post
Share on other sites

This topic is 3667 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this