How to handle lots of text?

Hello there,

I was wondering, what might be the best practice to work with a big amount of text elements in openframewoks?
I have something similar to http://www.vimeo.com/1550281 in mind. But since this was made with processing, i thought with the ofxMSAPhysics library i might spicen things a bit up an migrate to of not only for the fingertracking…

I was trying ofTrueTypeFont for font rendering but found the performance drop was pretty big after i added about 600 letters to the screen.

Has anyone tried ftgl yet? This actually seems to be a bit old ( Current Stable Version 2004 ) but who knows? And if this was worth it, how can i add it to an openframeworks project?

Thank you guys (and ladies ?)
Matthias

a couple of tips to speed up text display:

  1. if you don’t need the full character set, then don’t use it. letters are rendered as textures so they have to be loaded in the graphics mem before they can be drawn. so graphics mem size is a limit.

  2. if you can restrict your program to a handful of font sizes then do it. it saves graphics mem. display all characters before drawing the real stuff the first time. that should load the textures into graphics mem.

  3. if you don’t change the strings very often it might be good to cache the rendered texts in a FBOTexture. that’s what i do for the output of the debug console and the GUI buttons in my system. it speeds it up dramatically (especially the console).

best
joerg

Thanks for your suggestions jroge.

I tried to get FBOTexture to work, but did not manage to. The examples i downloaded did not compile correctly. Somehow the extension of ofTexture didnt work, couldnt find the parents variables (?)

Is there a ofFBOTexture example around, that is working with the latest version of OF (006)?

Is this hosted by anyone? Zach_Gage maybe?

Thanks for any hint

i modified them to work with OF 0.06.
i also disabled GLEE_ARB_texture_rectangle because i use them with shaders.
that’s why i added the drawClipped method to clip the texture beyond the wanted size (and not the ofNextPow2 sized).

  
/*  
 *  ofFBOTexture.h  
 *  openFrameworks  
 *  
 *  Created by Zach Gage on 3/28/08.  
 *  Copyright 2008 STFJ.NET. All rights reserved.  
 *  
 */  
  
#ifndef _FBO_TEX  
#define _FBO_TEX  
  
  
#include "ofMain.h"  
#include <iostream>  
  
  
class ofFBOTexture : public ofTexture  
{  
  
public:  
  
	void allocate(int w, int h, bool autoC);  
  
	void swapIn();  
	void swapOut();  
  
	void setupScreenForMe();  
	void setupScreenForThem();  
  
  
	void clear();  
	  
	void drawClipped(float x, float y, float w, float h);  
  
  
protected:  
  
	GLuint fbo;					// Our handle to the FBO  
	GLuint depthBuffer;			// Our handle to the depth render buffer  
	bool autoClear;  
	void clean();  
};  
  
#endif  
  

  
  
/*  
 *  ofFBOTexture.cpp  
 *  openFrameworks  
 *  
 *  Created by Zach Gage on 3/28/08.  
 *  Copyright 2008 STFJ.NET. All rights reserved.  
 *  
 */  
  
#include "ofFBOTexture.h"  
  
#undef GLEE_ARB_texture_rectangle  
#define GLEE_ARB_texture_rectangle 0  
  
void ofFBOTexture::allocate(int w, int h, bool autoC)  
{  
    texData.width = w;  
    texData.height = h;  
  
	//-------------ofTexture-------------  
	// 	can pass in anything (320x240) (10x5)  
	// 	here we make it power of 2 for opengl (512x256), (16x8)  
  
    if (GLEE_ARB_texture_rectangle){  
        texData.tex_w = w;  
        texData.tex_h = h;  
        texData.textureTarget = GL_TEXTURE_RECTANGLE_ARB;   // zach  -- added to get texture target right  
    } else {  
        texData.tex_w = ofNextPow2(w);  
        texData.tex_h = ofNextPow2(h);  
		texData.textureTarget = GL_TEXTURE_2D;  
    }  
  
	if (GLEE_ARB_texture_rectangle){  
		texData.tex_t = w;  
		texData.tex_u = h;  
	} else {  
		texData.tex_t = 1.0f;  
		texData.tex_u = 1.0f;  
	}  
  
	// attempt to free the previous bound texture, if we can:  
	clean();  
  
	texData.width = w;  
	texData.height = h;  
	texData.bFlipTexture = true;  
  
	//--FBOTexture-------------------  
	autoClear = autoC;  
	// Setup our FBO  
	glGenFramebuffersEXT(1, &fbo);  
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);  
  
	// Create the render buffer for depth  
	glGenRenderbuffersEXT(1, &depthBuffer);  
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer);  
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, texData.tex_w, texData.tex_h);  
  
	// Now setup a texture to render to  
	glGenTextures(1, (GLuint *)texData.textureName);   // could be more then one, but for now, just one  
  
	glEnable(texData.textureTarget);  
  
	glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);  
		glTexImage2D(texData.textureTarget, 0, GL_RGBA, texData.tex_w, texData.tex_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);  // init to black...  
		glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  
		glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  
		glTexParameterf(texData.textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
		glTexParameterf(texData.textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  
  
	// attach it to the FBO so we can render to it  
  
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texData.textureTarget, (GLuint)texData.textureName[0], 0);  
  
  
	// Attach the depth render buffer to the FBO as it's depth attachment  
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthBuffer);  
  
  
	GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);  
	if(status != GL_FRAMEBUFFER_COMPLETE_EXT)  
	{  
		cout<<"glBufferTexture failed to initialize. Perhaps your graphics card doesnt support the framebuffer extension? If you are running osx prior to system 10.5, that could be the cause"<<endl;  
		std::exit(1);  
	}  
	clear();  
  
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);	// Unbind the FBO for now  
  
	glDisable(texData.textureTarget);  
}  
  
  
void ofFBOTexture::setupScreenForMe(){  
  
	int w = texData.width;  
	int h = texData.height;  
  
	float halfFov, theTan, screenFov, aspect;  
	screenFov 		= 60.0f;  
  
	float eyeX 		= (float)w / 2.0;  
	float eyeY 		= (float)h / 2.0;  
	halfFov 		= PI * screenFov / 360.0;  
	theTan 			= tanf(halfFov);  
	float dist 		= eyeY / theTan;  
	float nearDist 	= dist / 10.0;	// near / far clip plane  
	float farDist 	= dist * 10.0;  
	aspect 			= (float)w/(float)h;  
  
	glMatrixMode(GL_PROJECTION);  
	glLoadIdentity();  
	gluPerspective(screenFov, aspect, nearDist, farDist);  
	gluLookAt(eyeX, eyeY, dist, eyeX, eyeY, 0.0, 0.0, 1.0, 0.0);  
	glMatrixMode(GL_MODELVIEW);  
	glLoadIdentity();  
  
  
	glScalef(1, -1, 1);           // invert Y axis so increasing Y goes down.  
  	glTranslatef(0, -h, 0);       // shift origin up to upper-left corner.  
  
    glViewport(0,0,texData.width, texData.height);  
  
}  
  
void ofFBOTexture::setupScreenForThem()  
{  
    int w, h;  
  
	w = glutGet(GLUT_WINDOW_WIDTH);  
	h = glutGet(GLUT_WINDOW_HEIGHT);  
  
	float halfFov, theTan, screenFov, aspect;  
	screenFov 		= 60.0f;  
  
	float eyeX 		= (float)w / 2.0;  
	float eyeY 		= (float)h / 2.0;  
	halfFov 		= PI * screenFov / 360.0;  
	theTan 			= tanf(halfFov);  
	float dist 		= eyeY / theTan;  
	float nearDist 	= dist / 10.0;	// near / far clip plane  
	float farDist 	= dist * 10.0;  
	aspect 			= (float)w/(float)h;  
  
	glMatrixMode(GL_PROJECTION);  
	glLoadIdentity();  
	gluPerspective(screenFov, aspect, nearDist, farDist);  
	gluLookAt(eyeX, eyeY, dist, eyeX, eyeY, 0.0, 0.0, 1.0, 0.0);  
	glMatrixMode(GL_MODELVIEW);  
	glLoadIdentity();  
  
  
	glScalef(1, -1, 1);           // invert Y axis so increasing Y goes down.  
  	glTranslatef(0, -h, 0);       // shift origin up to upper-left corner.  
  
  
    glViewport(0,0,w, h);  
}  
  
void ofFBOTexture::swapIn()  
{  
  
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // bind the FBO to the screen so we can draw to it  
  
	if(autoClear)  
	{  
		clear();  
	}  
  
	// Save the view port and set it to the size of the texture  
}  
  
void ofFBOTexture::swapOut()  
{  
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); //unbind the FBO  
  
}  
  
void ofFBOTexture::clean()  
{  
	// try to free up the texture memory so we don't reallocate  
	// [http://www.opengl.org/documentation/specs/man-pages/hardcopy/GL/html/gl/deletetextures.html](http://www.opengl.org/documentation/specs/man-pages/hardcopy/GL/html/gl/deletetextures.html)  
	if (texData.textureName[0] != 0){  
		glDeleteTextures(1, (GLuint *)texData.textureName);  
	}  
	texData.width = 0;  
	texData.height = 0;  
	texData.bFlipTexture = false;  
}  
  
void ofFBOTexture::clear()  
{  
	glClearColor(1,1,1,0); //clear white  
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer  
}  
  
  
void ofFBOTexture::drawClipped(float x, float y, float w, float h)  
{  
	// clip x at maxx  
	GLdouble plane0[4] = {-1.0, 0.0, 0.0, x+w};  
	glEnable(GL_CLIP_PLANE0);  
	glClipPlane(GL_CLIP_PLANE0,plane0);  
	// clip y at 0  
	GLdouble plane1[4] = {1.0, 0.0, 0.0, -x};  
	glEnable(GL_CLIP_PLANE1);  
	glClipPlane(GL_CLIP_PLANE1,plane1);  
  
	// clip y at maxy	  
	GLdouble plane2[4] = {0.0, -1.0, 0.0, y+h};  
	glEnable(GL_CLIP_PLANE2);  
	glClipPlane(GL_CLIP_PLANE2,plane2);  
	// clip y at 0	  
	GLdouble plane3[4] = {0.0, 1.0, 0.0, -y};  
	glEnable(GL_CLIP_PLANE3);  
	glClipPlane(GL_CLIP_PLANE3,plane3);  
  
	draw(x,y);  
  
	glDisable(GL_CLIP_PLANE0);  
	glDisable(GL_CLIP_PLANE1);  
	glDisable(GL_CLIP_PLANE2);  
	glDisable(GL_CLIP_PLANE3);  
}