   What would be faster?
  Hi, i wondered how I should implement my texture mapped font to work as fast as possible. I use quads which use the same texture and those quads can be associated with a specified character width, so that an "I" would not take the same space as a "W".So how would you store and render the quads?1) A display list for each quad. Then go through the text, render the corresponding quad for each character, translate the text by the last character's width.2) Store all quads in a vertex buffer, and create an index buffer for the text. Then bind the vbo once and render each character as described above, using an offset into the index buffer (1st char -> buffer, 2nd char -> buffer + 4, 3rd char -> buffer + 8 etc.).3) Create a texture for the text, render the quads with either method 1) or 2) to the texture. Then render a single quad for the text.Method 1) would be the easiest to implement, but would need to call a display list for each character. 2) would need to do a render call for each character, too, but would need to bind the buffer only once a frame. 3) would require some extra storage for the texture and extra work for creating it, but as long as the text isn't changed, these are done only once.Or just a 4th method, that came into my mind: don't store the quads in the font but store them in the text class. Then create a display list / vertexbuffer + indexbuffer for the text and render. Would use only one render call but much more memory and more work to change the text.Another question: Would it be possible/practical to use a shader so that you could use glCallLists(text) (1) or render the whole index buffer (2) and do the character translation in the shader? Would it further be possible/practical to provide some char width information to the shader?   
  Or you could do:5) store only 1 quad as a display list, this display list has no texture coordinates. Bind the font texture, do glCallList(quadID); as there are characters with the correct offsets (so a probably glPushMatrix()/glPopMatrix() for each character) and send the texture coordinates manually per character...I think 5 is pretty decent, if you do it right (meaning along side the font texture, create a character array with the texture coordinates), it should be a treat...Regards,DP   ;  I am using list for each character:base = glGenLists(256);for (int i=0; i<256; i++){    glNewList(base + i, GL_COMPILE);    // render i character as two triangles    glEndList();}// text rendering:glListBase(base);glBindTexture(GL_TEXTURE_2D, font_tex);glCallLists(text_length, GL_UNSIGNED_BYTE, text);;Quote:Original post by darkprophet5) store only 1 quad as a display list, this display list has no texture coordinates. Bind the font texture, do glCallList(quadID); as there are characters with the correct offsets (so a probably glPushMatrix()/glPopMatrix() for each character) and send the texture coordinates manually per character...I think 5 is pretty decent, if you do it right (meaning along side the font texture, create a character array with the texture coordinates), it should be a treat...Well, that's calling a display list per character again, just that I would have to send the texture coords, too. Currently I have to possibilities of creating a texture mapped font: 1) Create the font with equal-size characters, which means each char has tex coords which are like (n/16,n/16) for top left and (n+1/16, n+1/16) for bottom right (the others as a mix of the two)  , 2) load an array which provides the exact tex coordinates. Quad size will then match the characters.With your method 5), darkprophet, I think the characters would sometimes be stretched inpropoertionally (think of an "I" using the same space - because it uses the same quad - as a "W")@bubu LVThat's what I meant with my additional question: I'd like to do it as you do, so only one render call (glCallLists()). But how do you do the character translation, to place them next to each other not above each other? glWindowPos / glRasterPos - Matrices - Shaders?What I'd like to do is beeing able to place the text on the screen (2D) as well as in the world (3D), so glWindowPos etc. won't work.;  Hihere's my cFont class.cFont.h#pragma once#include "cTexture.h"//////struct sGlyphMetric{// Infos sur le bitmapshortWidth;// Largeur en pixelshortHeight;// Hauteur en pixelchar*Data;// Pointeur vers les donnees de l'image brute// Info sur la textureintStartU;// Debut X dans la textureintStartV;// Debut Y dans la textureintEndU;// Fin X dans la textureintEndV;// Fin Y dans la textureintAdvance;// Largeur totale du caracter a l'ecranintBearingX;// Deplacement horizontal avant d'afficher le bitmapintBearingY;// Deplacement vertical avant d'afficher le bitmapintUnderline;// Position de soulignement};//////class cFont{public:cFont(void);~cFont(void);// Charge une font depuis une imageboolLoadimage(const char *filename, int width, int height);// Charge une font depuis un fichier TTFboolLoadTTF(const char *filename, int size);// Affiche du textevoidPrint(int x, int y, const char *text);// Retourne la largeur d'un texteintGetWidth(const char *txt);// Retourne la hauteur d'un texteintGetHeight(const char *txt);protected:voidClear(void);intListID;// Display listcTextureTexture;// TexturesGlyphMetricMetrics[256];// Metrics des caracteres};cFont.cpp#include "cFont.h"//// Constructeur//cFont::cFont(void){ListID= 0;}//// Destructeur//cFont::~cFont(void){Clear();}//// Charge une font au format TTF//bool cFont::LoadTTF(const char *filename, int size){FT_LibraryLibrary;// Library FFTstringstreamnum;intFirstGlyph = 0;// Premier caractere a chargerintEndGlyph = 256;// Dernier caractere a charger// NettoyageClear();ListID = glGenLists(256);// Init de freetypeif (FT_Init_FreeType(&Library)){cerr << "Failed to initialize Freetype2 library";return false;};////// On charge la fontFT_Faceface;if (FT_New_Face(Library, filename, 0, &face )) {cerr << "Unable to find font \"" << filename << "\"" << endl;Clear();FT_Done_FreeType(Library);return false;}// Resize de la font a la bonne tailleFT_Set_Char_Size(face,(FT_F26Dot6 )size*64, (FT_F26Dot6 )size*64, 96, 96);// On recupere les infos sur tous les glyphs un a un// et on calcul la hauteur que va prendre la textureinttexheight = 0;inttexwidth = 0;intmaxheight = 0;intnumline = 1;for (int id = FirstGlyph; id<EndGlyph; id++){// Le caractere qui nous interresseif (FT_Load_Glyph(face, FT_Get_Char_Index(face, id), FT_LOAD_RENDER)){FT_Done_Face(face);cerr << "Can't get glyph's infos for charactere \"" << id << "\" (font \"" << filename << "\") !" << endl;Clear();FT_Done_FreeType(Library);return false;};// Pointeur vers le bitmapFT_Bitmap *bitmap= &face->glyph->bitmap;Metrics[id].Width= bitmap->width;Metrics[id].Height= bitmap->rows;Metrics[id].Advance= face->glyph->metrics.horiAdvance/64;Metrics[id].BearingX= face->glyph->metrics.horiBearingX/64;Metrics[id].BearingY= face->glyph->metrics.horiBearingY/64;Metrics[id].Underline= face->underline_position /64;// On sauvegarde les donnees de l'imageMetrics[id].Data = new char[bitmap->width * bitmap->rows *2];memset(Metrics[id].Data, 0, sizeof(char) * bitmap->width * bitmap->rows *2);for(int j=0; j < bitmap->rows; j++){for(int i=0; i < bitmap->width; i++){// RGBMetrics[id].Data[2*(i+j*bitmap->width)]= bitmap->buffer ? <span class="cpp-number">255</span> : <span class="cpp-number">0</span>;<br><span class="cpp-comment">// Alpha</span><br>Metrics[id].Data[<span class="cpp-number">2</span>*(i+j*bitmap-&gt;width)+<span class="cpp-number">1</span>] = bitmap-&gt;buffer;<br>}<br>}<br><br><br><br><br><span class="cpp-comment">// Si on depasse la largeur on fait une nouvelle ligne</span><br><span class="cpp-keyword">if</span> (texwidth + Metrics[id].Width &gt; <span class="cpp-number">512</span>)<br>{<br>numline++;<br>texwidth = <span class="cpp-number">0</span>;<br>};<br><br><span class="cpp-comment">// Si on a trouve une hauteur plus grande que les autres</span><br><span class="cpp-keyword">if</span> (Metrics[id].Height &gt; maxheight)<br>{<br>maxheight = Metrics[id].Height;<br>}<br><br><br><span class="cpp-comment">// On se deplace sur la droite</span><br>texwidth += Metrics[id].Width;<br>}<br>FT_Done_Face(face);<br>FT_Done_FreeType(Library);<br><br>texheight = <span class="cpp-number">1</span>;<br><span class="cpp-keyword">while</span>(texheight &lt; (numline*maxheight))<br>texheight &lt;&lt;= <span class="cpp-number">1</span>;<br><br><br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// On recopie tout dans un seul buffer de bonne taille</span><br><span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> *final = <span class="cpp-keyword">new</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span>[<span class="cpp-number">512</span> * texheight * <span class="cpp-number">2</span>];<br><span class="cpp-keyword">if</span> (!final)<br>{<br>cerr &lt;&lt; <span class="cpp-literal">"Unable to allocate temporary texture buffer. Not enough memory !"</span> &lt;&lt; endl;<br>Clear();<br><span class="cpp-keyword">return</span> <span class="cpp-keyword">false</span>;<br>}<br>memset(final, <span class="cpp-number">0</span>, <span class="cpp-number">512</span> * texheight * <span class="cpp-number">2</span>);<br><br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// on recopie un a un les caracteres dans la texture finale</span><br><span class="cpp-keyword">int</span> posx = <span class="cpp-number">0</span>;<br><span class="cpp-keyword">int</span> posy = <span class="cpp-number">0</span>;<br><span class="cpp-keyword">for</span> (<span class="cpp-keyword">int</span> id = FirstGlyph; id &lt; EndGlyph; id++)<br>{<br><br><br><span class="cpp-comment">// On copie dans la texture</span><br><span class="cpp-keyword">for</span> (<span class="cpp-keyword">int</span> y = <span class="cpp-number">0</span>; y &lt; Metrics[id].Height; y++)<br>{<br>memcpy(final + (posy + y)*<span class="cpp-number">1024</span> + posx * <span class="cpp-number">2</span>,<br>Metrics[id].Data + y*Metrics[id].Width*<span class="cpp-number">2</span>,<br>Metrics[id].Width*<span class="cpp-number">2</span>);<br>}<br><span class="cpp-keyword">delete</span>[] Metrics[id].Data;<br>Metrics[id].Data = NULL;<br><br><br><span class="cpp-comment">// Sauvegarde de la position UV</span><br>Metrics[id].StartU= posx;<br>Metrics[id].StartV= posy;<br>Metrics[id].EndU= posx + Metrics[id].Width;<br>Metrics[id].EndV= posy + Metrics[id].Height;<br><br><span class="cpp-comment">// On avance</span><br>posx += Metrics[id].Width;<br><br><span class="cpp-comment">// On se positionne bien</span><br><span class="cpp-keyword">if</span> (posx + Metrics[id].Width &gt;= <span class="cpp-number">512</span>)<br>{<br>posy += (<span class="cpp-keyword">int</span> )maxheight;<br>posx = <span class="cpp-number">0</span>;<br>}<br>}<br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// On cree une nouvelle texture avec les bonnes dimension</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">if</span>(!Texture.Create(<span class="cpp-number">512</span>, texheight, <span class="cpp-number">2</span>, GL_LUMINANCE_ALPHA))<br>{<br><span class="cpp-keyword">delete</span>[] final;<br>cerr &lt;&lt; <span class="cpp-literal">"Unable to allocate final texture. Not enough memory !"</span> &lt;&lt; endl;<br>Clear();<br><span class="cpp-keyword">return</span> <span class="cpp-keyword">false</span>;<br>};<br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// On mets a jour les infos</span><br>Texture.Update(final);<br><span class="cpp-keyword">delete</span>[]final;<br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// Generation de la displaylist</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">for</span> (<span class="cpp-keyword">int</span> id = FirstGlyph; id &lt; EndGlyph; id++)<br>{<br><span class="cpp-comment">// Precalcul des positions</span><br><span class="cpp-keyword">int</span>startx, starty, endx, endy;<br>startx=Metrics[id].BearingX;<br>starty=-Metrics[id].BearingY;<br>endx=startx + Metrics[id].Width;<br>endy=starty + Metrics[id].Height;<br><br><span class="cpp-comment">// Construction de la displaylist</span><br>glNewList(ListID + id, GL_COMPILE);<br><br><span class="cpp-comment">// On affiche le caractere</span><br>glBegin(GL_QUADS);<br>glTexCoord2d(Metrics[id].StartU, Metrics[id].StartV); <br>glVertex2i(startx, starty);<br><br>glTexCoord2d(Metrics[id].StartU, Metrics[id].EndV);<br>glVertex2i(startx, endy);<br><br>glTexCoord2d(Metrics[id].EndU, Metrics[id].EndV);<br>glVertex2i(endx, endy);<br><br>glTexCoord2d(Metrics[id].EndU, Metrics[id].StartV); <br>glVertex2i(endx, starty);<br>glEnd();<br><br><span class="cpp-comment">// On se deplace comme il faut</span><br>glTranslated(Metrics[id].Advance, <span class="cpp-number">0</span>, <span class="cpp-number">0</span>);<br><br><span class="cpp-comment">// Fini la liste</span><br>glEndList();<br>}<br><br><br><span class="cpp-keyword">return</span> <span class="cpp-keyword">true</span>;<br><br>}<br><br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// Charge une font depuis une image</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">bool</span> cFont::Loadimage(<span class="cpp-keyword">const</span> <span class="cpp-keyword">char</span> *filename, <span class="cpp-keyword">int</span> width, <span class="cpp-keyword">int</span> height)<br>{<br><br><br><span class="cpp-keyword">return</span> <span class="cpp-keyword">false</span>;<br>}<br><br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// Affiche du  texte</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">void</span> cFont::Print(<span class="cpp-keyword">int</span> x, <span class="cpp-keyword">int</span> y, <span class="cpp-keyword">const</span> <span class="cpp-keyword">char</span> *text)<br>{<br><br>glPushMatrix();<br><br><span class="cpp-comment">//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);</span><br><br>Texture.Bind();<br>glTranslatef((<span class="cpp-keyword">float</span> )x, (<span class="cpp-keyword">float</span> )y , <span class="cpp-number">0</span>.0f);<br>glListBase(ListID);<br>glCallLists((GLsizei )strlen(text), GL_UNSIGNED_BYTE, text);<br><br>glPopMatrix();<br>}<br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// Efface tout</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">void</span> cFont::Clear(<span class="cpp-keyword">void</span>)<br>{<br>memset(&amp;Metrics, <span class="cpp-number">0</span>, <span class="cpp-keyword">sizeof</span>(Metrics));<br><span class="cpp-keyword">if</span> (ListID)glDeleteLists(ListID, <span class="cpp-number">256</span>);<br><br>Texture.Clear();<br>}<br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// Retourne la largeur d'un texte</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">int</span>cFont::GetWidth(<span class="cpp-keyword">const</span> <span class="cpp-keyword">char</span> *txt)<br>{<br><span class="cpp-keyword">int</span> i = <span class="cpp-number">0</span>;<br><span class="cpp-keyword">int</span>width = <span class="cpp-number">0</span>;<br><span class="cpp-keyword">while</span>(txt<span style="font-weight:bold;">)<br>{<br>width += Metrics[(<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> )txt<span style="font-weight:bold;">].Advance;<br>i++;<br>}<br><br><br><span class="cpp-keyword">return</span> width;<br>}<br><br><br><br><span class="cpp-comment">//</span><br><span class="cpp-comment">// Retourne la hauteur d'un texte</span><br><span class="cpp-comment">//</span><br><span class="cpp-keyword">int</span> cFont::GetHeight(<span class="cpp-keyword">const</span> <span class="cpp-keyword">char</span> *txt)<br>{<br><span class="cpp-keyword">int</span> i = <span class="cpp-number">0</span>;<br><span class="cpp-keyword">int</span>height = <span class="cpp-number">0</span>;<br><span class="cpp-keyword">while</span>(txt<span style="font-weight:bold;">)<br>{<br><span class="cpp-keyword">if</span> (Metrics[(<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> )txt<span style="font-weight:bold;">].Height &gt; height)<br>height = Metrics[(<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> )txt<span style="font-weight:bold;">].Height;<br>i++;<br>}<br><br><br><span class="cpp-keyword">return</span> height;<br>}<br><br><br><br><br></pre></div><!–ENDSCRIPT–><br><br><br>usage :<br><!–STARTSCRIPT–><!–source lang="cpp"–><div class="source"><pre><br>cFont font;<br>font.LoadTTF(<span class="cpp-literal">"data/arial.ttf"</span>, <span class="cpp-number">12</span>);<br><br><span class="cpp-comment">// Don't forget to go to ortho mode here !</span><br>font.Print(<span class="cpp-number">100</span>, <span class="cpp-number">200</span>, <span class="cpp-literal">"Here's some texte"</span>);<br><br><br><br></pre></div><!–ENDSCRIPT–><br><br>Right now it's &#111;nly loading black and white font, no color fonts.<br><br>PS: Don't forget to include & link with Freetype2 library !;  Ah thank you,I forgot the glTranslate in the list definition.   ;Quote:Well, that's calling a display list per character again, just that I would have to send the texture coords, too.Yes, but you get significantly less memory usage. With my method, you have 4 vertices, 4 normals, 4 texture coordinates and 7 indices, thats a total of 76bytes. Compare that with a 64x64 character texture which is 311296bytes per font....if you want to have a few fonts about, well....;  I don't get it. Where do those 7 indices come from? Displaylist id etc. ?Does'nt your method require the use of a texture, too? So what about that 64 x 64 character texture, did you mean a 64 x 64 texture or a texture containing 64 x 64 characters?I use 256 character textures. Storing those would require (without the texture itself):256 * (4 positions + 4 texcoords + 1 width [which you suggested to store, too])positions are 3D vectors so that the quads can be placed both on screen and into the world, texcoords are 2D vectors (u,v) and width is a float describing the width of the character (0.0f - 1.0f). So that's256 * (4 * 3 * 4 + 4 * 2 * 4 + 4) = 256 * (68) = 17408 bytes. That's not too much. And IMHO sacrificing some memory in order to speed things up is worth it, unless your memory requirements are that high, that speed drops again. But I don't think some 17K (quads without) will do that.Adding the a 256 x 256 8 bit texture (that's all you need since its used as alpha map) that would be 256 * 256 bytes = 64K uncompressed. A 256 character font would thus need 17K + 64K = 81K.   