tags:

views:

125

answers:

4

I have an artsy opengl app that I am working on. The final product will have no need to display text. But during developement I want to display odd bits of data for diagnostic purposes, like framerate, object count. I would like the rendering of the text not to dramatically decrease the performance of the code.

I realize this is a mildly subjective question, but all the same I would appreciate your thoughts

+3  A: 

I wrote an opengl text renderer using just the gl_line primitives if that suits your needs:

#ifndef SIMPLETEXT_H
#define SIMPLETEXT_H

#include <GL/gl.h>

namespace SimpleText  {

   // internal vertex
   union vertex  {
      float data[3];
   };

   // use 15 verts to hold all possible letters
   // 0 - 1 - 2
   // 3 - 4 - 5
   // 6 - 7 - 8
   // 9 - 10- 11
   // 12- 13- 14
   vertex verts[15] = {
      {0, 4, 0}, {1, 4, 0}, {2, 4, 0},
      {0, 3, 0}, {1, 3, 0}, {2, 3, 0},
      {0, 2, 0}, {1, 2, 0}, {2, 2, 0},
      {0, 1, 0}, {1, 1, 0}, {2, 1, 0},
      {0, 0, 0}, {1, 0, 0}, {2, 0, 0}
   };

   // start/end char values
   const int START = 33;
   const int END = 90;

   // size of the window (assumed square)
   // increase this to decrease the size of text
   const int WINDOW_EXTENT = 300;

   // use index arrays to create letters
   unsigned int letters[END - START + 1][15] = {
      {4, 1, 7, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},      // !
      {4, 1, 4, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},        // "
      {8, 0, 12, 2, 14, 3, 5, 9, 11, 0, 0, 0, 0, 0, 0},     // #
      {8, 2, 3, 3, 11, 11, 12, 1, 13, 0, 0, 0, 0, 0, 0},    // $
      {14, 2, 12, 0, 3, 3, 1, 1, 0, 11, 13, 13, 14, 14, 11}, // %
      {14, 14, 3, 3, 1, 1, 7, 7, 9, 9, 12, 12, 13, 13, 11}, // &
      {2, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},        // '
      {6, 1, 3, 3, 9, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0},       // (
      {6, 1, 5, 5, 11, 11, 13, 0, 0, 0, 0, 0, 0, 0, 0},     // )
      {6, 0, 8, 6, 2, 1, 7, 0, 0, 0, 0, 0, 0, 0, 0},        // *
      {4, 6, 8, 4, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},       // +
      {2, 10, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},      // ,
      {2, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},        // -
      {8, 12, 13, 13, 10, 10, 9, 9, 12, 0, 0, 0, 0, 0, 0},  // .
      {2, 2, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},       // /
      {10, 0, 2, 2, 14, 14, 12, 12, 0, 0, 14, 0, 0, 0, 0},  // 0
      {6, 3, 1, 1, 13, 12, 14, 0, 0, 0, 0, 0, 0, 0, 0},     // 1
      {10, 3, 1, 1, 5, 5, 8, 8, 12, 12, 14, 0, 0, 0, 0},    // 2
      {12, 0, 1, 1, 5, 5, 11, 11, 13, 13, 12, 8, 7, 0, 0},  // 3
      {6, 14, 2, 2, 6, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0},       // 4
      {12, 2, 0, 0, 6, 6, 7, 7, 11, 11, 13, 13, 12, 0, 0},  // 5
      {14, 2, 1, 1, 3, 3, 12, 12, 13, 13, 11, 11, 7, 7, 6}, // 6
      {4, 0, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},       // 7
      {12, 1, 3, 3, 11, 11, 13, 13, 9, 9, 5, 5, 1, 0, 0},   // 8
      {10, 2, 1, 1, 3, 3, 7, 7, 8, 2, 14, 0, 0, 0, 0},      // 9
      {4, 1, 4, 10, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},      // :
      {4, 1, 4, 10, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},      // ;
      {4, 5, 6, 6, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},       // <
      {4, 5, 3, 9, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},       // =
      {4, 3, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},        // >
      {8, 3, 1, 1, 5, 5, 7, 7, 10, 0, 0, 0, 0, 0, 0},       // ?
      {14, 4, 7, 7, 8, 8, 5, 5, 1, 1, 3, 3, 9, 9, 14},      // @
      {10, 1, 6, 1, 8, 6, 12, 8, 14, 6, 8, 0, 0, 0, 0},     // A
      {14, 0, 12, 0, 5, 5, 7, 7, 6, 7, 11, 11, 13, 13, 12}, // B
      {10, 2, 1, 1, 3, 3, 9, 9, 13, 13, 14, 0, 0, 0, 0},    // C
      {12, 0, 1, 1, 5, 5, 11, 11, 13, 13, 12, 12, 0, 0, 0}, // D
      {8, 0, 12, 0, 2, 6, 7, 12, 14, 0, 0, 0, 0, 0, 0},     // E
      {6, 0, 12, 0, 2, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0},       // F
      {14, 2, 1, 1, 3, 3, 9, 9, 13, 13, 11, 11, 8, 8, 7},   // G
      {6, 0, 12, 6, 8, 2, 14, 0, 0, 0, 0, 0, 0, 0, 0},      // H
      {6, 0, 2, 1, 13, 12, 14, 0, 0, 0, 0, 0, 0, 0, 0},     // I
      {8, 1, 2, 2, 11, 11, 13, 13, 9, 0, 0, 0, 0, 0, 0},    // J
      {6, 0, 12, 6, 2, 6, 14, 0, 0, 0, 0, 0, 0, 0, 0},      // K
      {4, 0, 12, 12, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},     // L
      {8, 0, 12, 0, 7, 7, 2, 2, 14, 0, 0, 0, 0, 0, 0},      // M
      {6, 0, 12, 0, 14, 14, 2, 0, 0, 0, 0, 0, 0, 0, 0},     // N
      {12, 1, 5, 5, 11, 11, 13, 13, 9, 9, 3, 3, 1, 0, 0},   // O
      {10, 0, 12, 0, 1, 1, 5, 5, 7, 7, 6, 0, 0, 0, 0},      // P
      {12, 0, 12, 12, 13, 13, 11, 11, 2, 2, 0, 10, 14, 0, 0},  // Q
      {12, 0, 12, 0, 1, 1, 5, 5, 7, 7, 6, 7, 14, 0, 0},     // R
      {14, 2, 1, 1, 3, 3, 6, 6, 8, 8, 11, 11, 13, 13, 12},  // S
      {4, 0, 2, 1, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},       // T
      {8, 0, 9, 9, 13, 13, 11, 11, 2, 0, 0, 0, 0, 0, 0},    // U
      {4, 0, 13, 13, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},      // V
      {8, 0, 12, 12, 1, 1, 14, 14, 2, 0, 0, 0, 0, 0, 0},    // W
      {4, 0, 14, 2, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},      // X
      {6, 0, 7, 7, 2, 7, 13, 0, 0, 0, 0, 0, 0, 0, 0},       // Y
      {6, 0, 2, 2, 12, 12, 14, 0, 0, 0, 0, 0, 0, 0, 0}      // Z
   };

}

// Draws a string of length <len> at an <x>/<y> position on the screen, optionally with a <shadow>
//  The screen is set up to be 0,0 at the lower left and 150,150 at the upper right for positioning
//  by default.
static void DrawText(const char * string, int len, float x, float y, bool shadow = false)  {
   if (len <= 0) return;

   glPushMatrix();
   glLoadIdentity();

   glMatrixMode(GL_PROJECTION);
   glPushMatrix();

   glLoadIdentity();
   glOrtho(0, SimpleText::WINDOW_EXTENT, 0, SimpleText::WINDOW_EXTENT, -10, 10);

   glMatrixMode(GL_MODELVIEW);

   glBindTexture(GL_TEXTURE_2D, 0);

   glVertexPointer(3, GL_FLOAT, 0, SimpleText::verts);
   glEnableClientState(GL_VERTEX_ARRAY);

   char temp = 0;

   // draw first copy if we need a shadow
   if (shadow)  {
      glTranslatef(x + 0.3, y - 0.5, 0);
      glColor4f(0.0f, 0.0f, 0.0f, 1.0f);

      for (int i = 0; i < len; i++)  {
         temp = string[i];
         temp = (temp < 97) ? temp : temp - 32;
         if (temp >= SimpleText::START && temp <= SimpleText::END)  {

            glDrawElements(
               GL_LINES, 
               SimpleText::letters[temp - SimpleText::START][0], 
               GL_UNSIGNED_INT, 
               &(SimpleText::letters[temp - SimpleText::START][1])
            );
         }

         glTranslatef(2.7f, 0, 0);
      }
   }

   glLoadIdentity();
   glTranslatef(x, y, 0);
   glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

   // draw regular text
   for (int i = 0; i < len; i++)  {
      temp = string[i];
      temp = (temp < 97) ? temp : temp - 32;
      if (temp >= SimpleText::START && temp <= SimpleText::END)  {

         glDrawElements(
            GL_LINES, 
            SimpleText::letters[temp - SimpleText::START][0], 
            GL_UNSIGNED_INT, 
            &(SimpleText::letters[temp - SimpleText::START][1])
         );
      }

      glTranslatef(2.7f, 0, 0);
   }

   glDisableClientState(GL_VERTEX_ARRAY);


   glMatrixMode(GL_PROJECTION);
   glPopMatrix();

   glMatrixMode(GL_MODELVIEW);
   glPopMatrix();
}

#endif
Ron Warholic
I should note that this has been tested working on Linux and Windows and should probably work on just about anything reasonably well. I haven't tested the performance but it should be reasonable under most environments.
Ron Warholic
The poster did mention that performance was an issue. Wouldn't it be easier to just sprintf the text needed into a char[] buffer and then use openGL's built-in text-displaying functions (GLRasterPos, glutBitmapCharacter)? This way is much more primitive, and probably less performance instensive.
SauceMaster
This was designed for systems that wanted quick debug text without worrying about external libraries or cross-platform OS code. There certainly are better ways to display text but when you just need to get something on the screen to see whats going on it does the job just fine. That said OpenGL has no 'built-in' text rendering. GLUT has a text rendering interface and the OS provides facilities in some cases (glXUseXFont or wglFontBitmaps) but this has the advantage of pure C++ and OpenGL calls.
Ron Warholic
A: 

Given that you're doing this on Windows, the easy way to handle things is to use wglUseFontBitmaps to load a set of characters into a display list, then glRasterPos2i to pick where to draw the text, and glCallLists to draw a string. If you wanted something fancier than diagnostics, you could use wglUseFontOutlines to get a little fancier, such as creating 2.5D (extruded) text.

Jerry Coffin
+1  A: 

If you need it only during development, you might want to check out the Win32 function OutputDebugString(). It sends the text to an attached debugger and requires no changes to your existing program at all. You could also build your program as a console application and simply write the text to stdout (printf, std::cout). This might not be as beautiful as a custom OpenGL text renderer, but it's as simple as it gets.

Malte Clasen
A: 

Grab the 8x8 CP437 glyphs and stick them (in ASCII order) in a 128x128 texture. Since they're tightly packed, make sure to turn of bilinear filtering or else you'll get bleeding into adjacent glyphs.

genpfault