VBO & Fonts..

Started by
6 comments, last by TTK-Bandit 15 years, 4 months ago
Hey back over in this topic, I was told to use vbo's for everything, even fonts.. now after getting to know how vbo works, I dont get how this could be efficiently done.. To begin I'll tell you what my font system needs: The hud in the game dll can draw a lot of text, it can be colored differently with escape codes like in quake3 and it has its specified drawing order. Currently I have a fontclass with memberfunctions SetSize, DrawString, etc Now every frame the hud will draw quads over the screen using DrawString, while SetSize is only called on change. Now using VBO's, I thought I would need to have a new class VBO_TextObject, which will get called on text creation/change to init the vbo and on every frame, the hud calls the textobjects Draw function, so it's drawn in the correct order. Now to my problems: From what I can see there are 2 ways to solve the buffering & drawing: - Have a VBO for all texcoord of a font, and for the different sizes of the font a vbo that contains all verts for the glyphs, then doing something like: foreach char in string, translate to x-value & call drawrangelements for the char. - Or create 3 VBO's for every TextObject (vertex, color, texcoord), and call drawrangelements once on the textobjects vbo's both methods seem very ugly to me and cause a lot of memory to be lost for nothing. I'm not quite sure this will gain any performance at all. any other suggestions ?
Advertisement
Normally, text doesn't change every frame, so you could just have an entire sentence in a single texture and render.
It only requires 1 call to glBindBuffer or perhaps 0 if you have already bound it.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);
what do you mean by a single texture ?
like prerender the string into a texture which is then drawn as a single quad ?
some text items do update pretty frequently tho, like fps, teamoverlay, chat messages, console text, etc
What I do (may be terrible way of doing it but, for conversation sake) I use a font texture, that contains all the characters, this font texture has an associated data file describing all the offsets and tweaks that each character needs to be positioned. I allocate a buffer (vbo) that can be used to draw MaxChars, say 128 at a time. When drawing, I set the font texture for use, then modify this vertex buffer (interleaved), changing the vertices position (for translating placing in right place), texcoord (based on characters parameters, so it use the right part of the texture), and color (for blending the texture with the vertices to change its color). This buffer lives in a manager for hud stuff, hud elements call it when they want to draw their text, or i can call it right away for throwing up debug text. So if I need to draw say 152 chars, I make two calls, a 128 call, and the rest goes in another call. So it goes something like this.

Load Font Texture
Load Font Texture Data (build a list of chars, 1:1 char array indexing)
Allocate a vbo with some limit I think is best
Draw Text
Build Text String (straight index into array to get char info I need)
Loop text, tweak two tris to position coords on say 'R' part of texture
Set texture
Draw Vbo

God how I hate text.
How about using a round-robin of Streaming VBOs, use glMapBuffer and fill-in vtx-attribs of triangles. (2 triangles for each drawn character)

Here's an excerpt from a header of my implementation of such stuff:

//------------[ vertex-buffer-object procs ]--------------------------------------[ILVBO ilCreateStaticVBO(const void* vtxData,int numVerts,const short* vtxDecl,const void* idxData=0,int idxDataSize=0,bool isDwordIndex=true);ILVBO ilCreateStreamVBO(int VtxBufSize,int IdxBufSize,bool isDwordIndex);void ilDeleteVBO(ILVBO vbo);void ilDrawVBO(ILVBO vbo);void ilDrawVBOs(ILVBO vboPrimary,ILVBO vboSecondary); // one of these VBOs has an index-buffer; vboPrimary must have ATTR0 (vertex-position). All attributes and data from the two VBOs are used to draw the primitivesint  ilGetVertexDeclSize(const short* vtxDecl); // get expected size of a vertex, by passing its vertex-declaration//--------------------------------------------------------------------------------///-----------[ immediate-mode drawing ]----------------------------------------------[void ilImmBegin(int NumVertsPerPrim1234,const short* vtxDecl); // start immediate mode, with expected type of primitives (1=points, 2=lines, 3=triangles, 4=quads and triangles). Each vertex has the same format, specified by vtxDecl. vtxDecl MUST NOT be NULL! vtxDecl must stay valid at least until ilImmEnd() - so don't put it on the thread's stack if the stack will be overwritten before ilImmEnd. void ilImmEnd(); // flush and end drawing this set of immediate-mode primitivesvoid ilImmFlush();// flush drawing, expecting to continue drawing imm-mode primitives. Useful if you want to change an uniform-value in-between a range of primitivesvoid ilImmPoint(const void* vtx1); // add one vertex, defining a pointvoid ilImmLine(const void* vtx12); // add two vertices, defining a line. vtx12 contains both vertices in a contiguous range of memoryvoid ilImmLine(const void* vtx1,const void* vtx2); // like above, but the two vertices can be from different ranges of memoryvoid ilImmTriangle(const void* vtx123); // add three vertices, defining a triangle. Contiguous range of memoryvoid ilImmTriangle(const void* vtx1,const void* vtx2, const void* vtx3); // like above, but different mem-rangesvoid ilImmQuad(const void* vtx1234); // add four vertices, defining a quad. They get internally split into two CW triangles. void ilImmQuad(const void* vtx1,const void* vtx2, const void* vtx3,const void* vtx4); // like above, but different mem-rangesvoid ilImmDrawArray(int NumPrimitives,int NumVertsPerPrim1234,const short* vtxDecl,const void* data);//----------------------------------------------------------------------------------/


I've uploaded the whole .h and .cpp implementation at:
http://pastebin.com/m1c01f0f
http://pastebin.com/m1a0bcf76
Quote:Original post by NumberXaero
So if I need to draw say 152 chars, I make two calls, a 128 call, and the rest goes in another call. So it goes something like this.

So you are uploading the string verts into the vbo every frame ?
wouldn't it be easier to draw a vertex array without allocating a buffer ?

@idinev:
I dont really get how this is working.. could you give an example of how you use the code ?
Here:
http://pastebin.com/m455cbfe6
Look at the immBegin() ... immEnd() part.
Thank you very much, I'll have a deep look into this.

This topic is closed to new replies.

Advertisement