2D-Text
Text Renderer
2D-Text is a text renderer written in C++ that works with a 2D game engine. It is a sprite text renderer. It works with the DirectX API of Windows and uses the game engine's graphics class to load the sprite textures to the system memory and render it with the help of a texture manager.
Project Overview
2D-Text is a sprite text renderer written as a module to work with a 2D game engine for Windows. It is written to work with the DirectX API. DirectX contains a complete rendering engine that can perform all the computations required to display complex shapes on the computer screen. The shapes are defined by vertex and primitive data. The DirectX graphics pipeline can be diagrammed, as shown below.
Sprite Text
In a sprite text system, individual letters are contained in a larger texture image. The program determines which part of the larger texture image to display when rendering each character. The example font contains 94 characters. Each individual character is 48 pixels wide and 62 pixels high. It is important in a sprite-based font to start with large images. If the font is scaled down to produce smaller characters, the quality of each character will be high. If the font is scaled up to produce larger characters, the curved edges of the characters will not be as smooth.
The Graphics Class of the Game Engine
The Graphics class is a wrapper class. It wraps the DirectX functions inside custom functions for the game and will redefine
the DirectX types with the custom types. The custom functions and types will be used in place of
the DirectX equivalent everywhere else in the code. This will limit the use of DirectX
specific code to the wrapper classes, which will make it easier to upgrade the custom code to use
different APIs in the future.
To intialize the Text object, a pointer to the Graphics object is passed as a parameter.
Normally textures are loaded with Graphics::loadTexture function. The
loadTexture function loads textures into default D3D memory, which is usually video
memory. In order to examine a texture at the pixel level, the texture has to be loaded
into system memory. A new function is added to the Graphics class for this purpose.
It's named Graphics::loadTextureSystemMem. The system memory pool is specified with the parameter D3DPOOL_SYSTEMMEM.
The TextureManager Class of the Game Engine
The loadTexture function, of the Graphics class, loads the texture data into D3DPOOL_DEFAULT memory. This is normally video memory. If the graphics device is used by another application, it may become unavailable or “lost” to the game. If that happens, the texture data should be released and reloaded as part of the process of recovering the lost graphics device. By creating a separate texture manager class, it's possible to include the code for dealing with a lost device. When the texture is needed in the game, a TextureManager object is created to load and manage each texture. The TextureManager->initialize function saves a pointer to the graphics system and loads the specified texture file from disk. The graphics->loadTexture function saves the width, height, and pointer to the texture data.
The Image Class of the Game Engine
All of the code that is needed to draw and manage a sprite will be incorporated into the Image
class. The Image’s initialize function receives a pointer to the Graphics object, the
width and height of the image in pixels, the number of columns in the texture, and a pointer
to TextureManager that contains the texture. The initialize function saves the pointers to graphics and textureManager.
The spriteData structure texture, width, and height members are filled in. The
number of columns in the texture is saved. The last step in initializing is to configure
spriteData.rect. The structure spriteData.rect specifies a rectangle that may be
used to select part of a multi-image texture.
The Image::draw function will draw the image to the screen.
The Text Class
The Text class has all the rules that should be kept in mind while making the sprite text image. It also has the methods that initialize and print the sprite text into the game scene. Care must be taken to ensure that the font texture matches the settings in the Text class. The following are some of the constants defined in text.h and how they relate to the font sprite image/ texture:
- FONT_BORDER = 3;. The size of the border around each character. Recall from our earlier discussion about sprites that we need to provide a one-pixel-wide transparent border around our sprite textures. One pixel on each side accounts for a border size of 2. To that we add our visible grid on the right and bottom of each character for a total of 3. The visible grid is only drawn on the right and bottom edge of each character. When placed together, they form a complete grid.
- FONT_HEIGHT = 62;. The height of the characters in pixels.
- GRID_WIDTH = 48 + FONT_BORDER;. The width of each character grid in pixels, including the FONT_BORDER. Each grid in this example is 51 pixels wide.
- GRID_HEIGHT = FONT_HEIGHT + FONT_BORDER;. The height of each character grid in pixels, including the FONT_BORDER. Each grid is 65 pixels tall.
- COLUMNS = 16;. The number of columns in the font image.
- ROWS = 14;. The number of rows in the font image.
- UNDERLINE = '_';. The character that is used to display an underline. The ASCII value of '_' is 0x5F or 95 decimal. If the underline texture is moved to a different grid position, then the UNDERLINE constant must be changed to indicate the grid position. Underline printing is accomplished by printing the underline character followed by the displayed character.
- BOLD_SIZE = 4;. Bold printing is accomplished by printing each character twice with a horizontal offset between each print. This is the number of pixels of offset applied for a 1:1 scaled character.
Initializing the Sprite Text
The loadTextureSystemMem function is used to load the font texture into system memory in the Text::initialize function. The Graphics *g parameter points to the Graphics object and the char *file parameter is the name of the font texture file. The font texture should be locked before accessing the pixel data.
- bool Text::initialize(Graphics *g, const char *file)
- {
- HRESULT result = graphics->loadTextureSystemMem(file);
- }
Printing the Sprite Text
One print function does all of the actual printing. The function parameters are the string to print and the X,Y screen location. The width variable is set equal to the width of one character in the sprite texture. The scaledWidth variable contains the width of the scaled character, which is the width of the character after printing. The doAlign(str) function sets the X and Y locations so the string will be aligned, as specified by the align setting.
- void Text::print(const std::string &str, int x, int y)
- {
- UCHAR ch = 0, chN = 0;
- std::string str2;
- width = textNS::FONT_WIDTH;
- int scaledWidth = static_cast<int>(textNS::FONT_WIDTH*spriteData.scale);
- float saveY=0;
- int tabX=0, tabW=0;
- spriteData.x = (float)x;
- spriteData.y = (float)y;
- doAlign(str);
- }