Lazy Foo' Productions

Transforming Text

Last Updated 8/09/12
Text as far as we're concerned is just a set of textured quads. If we treat a rendered string as one big quad, we can transform it just like any quad.
From LFont.cpp
LFRect getStringArea( std::string text ); /* Pre Condition: -A loaded font Post Condition: -Returns area for given text Side Effects: -None */
The new LFont function getStringArea() gives us the rendering area for an entire string.
From LFont.cpp
LFRect LFont::getStringArea( std::string text ) { //Initialize area GLfloat subWidth = 0.f; LFRect area = { 0.f, 0.f, subWidth, mLineHeight }; //Go through string for( int i = 0; i < text.length(); ++i ) { //Space if( text[ i ] == ' ' ) { subWidth += mSpace; } //Newline else if( text[ i ] == '\n' ) { //Add another line area.h += mLineHeight; //Check for max width if( subWidth > area.w ) { area.w = subWidth; subWidth = 0.f; } } //Character else { //Get ASCII GLuint ascii = (unsigned char)text[ i ]; subWidth += mClips[ ascii ].w; } } //Check for max width if( subWidth > area.w ) { area.w = subWidth; } return area; }
The way this function calculates area is simple. For every newline, it adds on a line height to the area height. To calculate how wide the text area is, we need to find the line of text with the greatest width.

To do that, we go through the string adding the character/space width for each character to "subWidth". When we reach a newline or the end of the whole string, we check if this line of text has a greater width than the current text area width. If it is, it means we found the greatest line width for the string.
From LUtil.cpp
//Font LFont gFont; //Text areas LFRect gScaledArea = { 0.f, 0.f, 0.f, 0.f }; LFRect gPivotArea = { 0.f, 0.f, 0.f, 0.f }; LFRect gCirclingArea = { 0.f, 0.f, 0.f, 0.f }; //Transformation variables GLfloat gBigTextScale = 3.f; GLfloat gPivotAngle = 0.f; GLfloat gCirclingAngle = 0.f;
At the top of LUtil.cpp we declare the font, the text areas for the text we're going to render, and various transformation variables.
From LUtil.cpp
bool loadMedia() { //Load text if( !gFont.loadFreeType( "25_transforming_text/lazy.ttf", 60 ) ) { printf( "Unable to load ttf font!\n" ); return false; } //Calculate rendering areas gScaledArea = gFont.getStringArea( "Big Text!" ); gPivotArea = gFont.getStringArea( "Pivot" ); gCirclingArea = gFont.getStringArea( "Wheee!" ); return true; }
After we load the text, we precalculate the render areas for all of the strings so we don't calculate them every time we render.
From LUtil.cpp
void update() { //Update angles gPivotAngle += -1.f; gCirclingAngle += +2.f; //Scale gBigTextScale += 0.1f; if( gBigTextScale >= 3.f ) { gBigTextScale = 0.f; } }
In the update function() we update rotation angles and text scaling.
From LUtil.cpp
void render() { //Clear color buffer glClear( GL_COLOR_BUFFER_BIT ); //Big upper middle text glLoadIdentity(); glColor3f( 1.f, 0.f, 0.f ); //Move to render point glTranslatef( ( SCREEN_WIDTH - gScaledArea.w * gBigTextScale ) / 2.f, ( SCREEN_HEIGHT - gScaledArea.h * gBigTextScale ) / 4.f, 0.f ); //Scale and render glScalef( gBigTextScale, gBigTextScale, gBigTextScale ); gFont.renderText( 0.f, 0.f, "Big Text!" , &gScaledArea, LFONT_TEXT_ALIGN_CENTERED_H );
First we render some red text that pops out at the user. First we want to translate the text to the center of the top half of the screen. Since we're translating a scaled quad (we're treating the text area like a single quad) in unscaled coordinates, we scale the text area in the call to glTranslate().

Then we call glScale() and render the scaled text.
From LUtil.cpp
//Lower pivoting text glLoadIdentity(); glColor3f( 0.f, 1.f, 0.f ); //Move to render point glTranslatef( ( SCREEN_WIDTH - gPivotArea.w * 1.5f ) / 2.f, ( SCREEN_HEIGHT - gPivotArea.h * 1.5f ) * 3.f / 4.f, 0.f ); //Scale and move to pivot point glScalef( 1.5f, 1.5f, 1.5f ); glTranslatef( gPivotArea.w / 2.f, gPivotArea.h / 2.f, 0.f ); //Rotate around pivot glRotatef( gPivotAngle, 0.f, 0.f, 1.f ); //Move back to render point and render glTranslatef( -gPivotArea.w / 2.f, -gPivotArea.h / 2.f, 0.f ); gFont.renderText( 0.f, 0.f, "Pivot", &gPivotArea, LFONT_TEXT_ALIGN_CENTERED_H );
Here we have rotating green text that rotates around it's center at the bottom half of the screen.
From LUtil.cpp
//Circling text glLoadIdentity(); glColor3f( 0.f, 0.f, 1.f ); //Move to center of screen glTranslatef( SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 0.f ); //Rotate around center glRotatef( gCirclingAngle, 0.f, 0.f, 1.f ); //Move to arm position glTranslatef( 0.f, -SCREEN_HEIGHT / 2.f, 0.f ); //Center on arm glTranslatef( -gCirclingArea.w / 2.f, 0.f, 0.f ); //Render gFont.renderText( 0.f, 0.f, "Wheee!", &gCirclingArea, LFONT_TEXT_ALIGN_CENTERED_H ); //Update screen glutSwapBuffers(); }
Finally we render text that circles around the screen. It works by rotating around the center of the screen.
Download the media and source code for this tutorial here.
Back to OpenGL Tutorials