Tutorial 4 for SDK 1.0
Text Messages Part 2
by Trevor Hogan

Debug Visualizations

Before I talk about fonts I'd just like to point out id's excellent debug visualizations. Debug visualizations are rendered in 3D coordinates so they aren't very useful for rendering to your two dimensional screen but they can be useful for displaying debug info inside your mod's levels. Doom 3 already makes good use of debug visualizations so if you want to see an example just open up any original Doom 3 map and type "g_showTargets 1" in the console. Doom 3 will draw a ton of lines and text to the screen showing you exactly how the level works. I can't think of very many examples where you'd want to write your own debug visualizations but the capability is there if you need it. For example, say you coded a System Shock 2 style security camera but you were having trouble with it seeing the player too easily. You could draw the security camera's view frustum using the debug visualizations and adjust it according to what feels right to you. Anyway, open renderer/RenderWorld.h and take a look at the idRenderWorld class.

//-------------- Debug Visualization  -----------------

// Line drawing for debug visualization
virtual void DebugClearLines( ... ) = 0;
virtual void DebugLine( ... ) = 0;
virtual void DebugArrow( ... ) = 0;
virtual void DebugWinding( ... ) = 0;
virtual void DebugCircle( ... ) = 0;
virtual void DebugSphere( ... ) = 0;
virtual void DebugBounds( ... ) = 0;
virtual void DebugBox( ... ) = 0;
virtual void DebugFrustum( ... ) = 0;
virtual void DebugCone( ... ) = 0;
virtual void DebugAxis( ... ) = 0;

// Polygon drawing for debug visualization.
virtual void DebugClearPolygons( ... ) = 0;
virtual void DebugPolygon( ... ) = 0;

// Text drawing for debug visualization.
virtual void DrawText( ... ) = 0;

image1

These functions are intended for debug usage only so you probably don't want to use them in the final version of your mod. As you can see idRenderWorld includes several functions for drawing lines, arrows, circles, boxes, and more (in case you're feeling artistic). The code is fairly straightforward and there are quite a few examples already in the SDK so I'll leave the rest to you.

HUD Messages Revisited

If you read my first tutorial on text messages you may remember that I said it was impossible to print messages to the HUD directly from the SDK. Well, I was wrong. Open renderer/RenderSystem.h and look for this code.

virtual void DrawSmallChar( int x, int y, int ch, const idMaterial *material ) = 0;
virtual void DrawSmallStringExt( int x, int y, const char *string, const idVec4 &setColor, bool forceColor, const idMaterial *material ) = 0;
virtual void DrawBigChar( int x, int y, int ch, const idMaterial *material ) = 0;
virtual void DrawBigStringExt( int x, int y, const char *string, const idVec4 &setColor, bool forceColor, const idMaterial *material ) = 0;

You can use these four functions to print text to the screen directly from the SDK. Let's take a look at DrawBigStringExt. The first two arguments specify the top left corner of the message in virtual 640x480 screen coordinates - this means that (639,479) represents the bottom right corner of the screen no matter what your actual screen resolution is (i.e. the engine will scale your text to the correct position). Remember that screen coordinates are different from regular coordinates; (0,0) is the top left corner of the screen and increasing Y moves down the screen. The third argument is the actual text to display and the fourth argument specifies the default colour (red, green, blue, alpha). The forceColor argument tells Doom 3 if it should respect colour change sequences and the last argument specifies the material (i.e. the font). The only font you can use with these functions is the "textures/bigchars" font unless you make your own font.

To demonstrate how to use these functions I'll print the player's current speed on the screen. Open game/Player.cpp and find the DrawHUD function. Add this code to the end of the function.

idStr strText;

sprintf( strText, "Speed: %f", GetPhysics( )->GetLinearVelocity( ).Length( ) );

// the string will be drawn at (100,200) on the virtual screen
// it will be coloured white and be partially transparent (alpha 0.6)
// colour change sequences will be respected
// and it will use the "textures/bigchars" font

renderSystem->DrawBigStringExt( 100, 200, strText.c_str( ), idVec4( 1, 1, 1, 0.6 ), false, declManager->FindMaterial( "textures/bigchars" ) );

image2

Fonts

There's one final way to print messages to the screen, this time using the game fonts "an", "bank", and "micro". I'd like to say thanks to Brian from id Software for showing me how to use the font structures in the SDK. If you open up renderer/RenderSystem.h you'll see a ton of stuff for working with fonts but strangely enough there aren't any functions. Let's fix that by writing our own text rendering function. Scroll to the bottom of the file and add this code (above the #endif of course).

extern fontInfoEx_t font_an;
extern fontInfoEx_t font_bank;
extern fontInfoEx_t font_micro;

void PrintMessage( int x, int y, const char *szMessage, idVec4 colour, fontInfoEx_t &font );

This tells the compiler that we're going to declare three fontInfoEx_t structures and a PrintMessage function in a .cpp file somewhere. We can do this anywhere so open game/Game_local.cpp since we'll need it later and add this code near the top of the file just above the GetGameAPI function.

fontInfoEx_t font_an;
fontInfoEx_t font_bank;
fontInfoEx_t font_micro;

void PrintMessage( int x, int y, const char *szMessage, idVec4 colour, fontInfoEx_t &font )
{
      renderSystem->SetColor( colour );

      for( const char *p = szMessage; *p; p++ )
      {
            glyphInfo_t &glyph = font.fontInfoSmall.glyphs[*p];

            renderSystem->DrawStretchPic( x, y - glyph.top,
                  glyph.imageWidth, glyph.imageHeight,
                  glyph.s, glyph.t, glyph.s2, glyph.t2,
                  glyph.glyph );

            x += glyph.xSkip;
      }
}

The PrintMessage function works by iterating through each character in the message. It looks up the current character in the glyph table for the font (a glyph is just a character) and draws the character's image to the screen. Then it shifts to the right to make room for the next character and moves on. Note that this function won't respect colour change sequences (it will just print "^x" directly to the screen) but you should be able to rewrite it so it does - give it a shot!

There's one thing left to do - we have to initialize those fontInfoEx_t structures. Scroll down a few pages to idGameLocal :: Init and add this code to the end of the function.

const char *szLang = cvarSystem->GetCVarString( "sys_lang" );

renderSystem->RegisterFont( va( "fonts/%s/%s", szLang, "an" ), font_an );
renderSystem->RegisterFont( va( "fonts/%s/%s", szLang, "bank" ), font_bank );
renderSystem->RegisterFont( va( "fonts/%s/%s", szLang, "micro" ), font_micro );

Now you can print messages to the screen using the builtin fonts. Open game/PlayerView.cpp, find the SingleView function and add this code right after the call to DrawHUD (as shown).

player->DrawHUD( hud );

PrintMessage( 100, 100, "Hey there! This text was drawn with the an font.", idVec4( 1, 1, 1, 1 ), font_an );
PrintMessage( 100, 120, "Hey there! This text was drawn with the bank font.", idVec4( 1, 1, 1, 1 ), font_bank );
PrintMessage( 100, 140, "Hey there! This text was drawn with the micro font.", idVec4( 1, 1, 1, 1 ), font_micro );

image3

November 1, 2004