3D Text

Introduction

Creation and Destruction

Rendering Text

Lost Device

Lesson Downloads

Further Reading

MSDN Links For Functions/Concepts Discussed Here

Introduction

The D3DXCreateText function creates a 3D mesh representing the string you pass to it. This is not something you would want to use in general use, and since it's incredibly resource intensive it would need to operate on static text. It is however, rather cool. And that seems like a good enough reason to cover it.

In the example code a D3DXFont object is also created to be used as an FPS counter. There is no way D3DXCreateText could be used for something that dynamic. Also, rather than displaying the entire 2 paragraphs that the 2 previous lessons covered, only the first paragraph (4 lines) will be rendered.

If you run the example code, you'll notice that the text looks thicker near the edges of the display. Since the text has depth (it's a 3D model) you are seeing the effects of perspective. A later lesson will use less text and show how to make it look a lot nicer, as well as making the fact that it's 3D more apparent.

In the example code an array of meshes is created, 1 element per line of text. This was required because D3DXCreateText has no support for multi-line text. An array of world matrices is also created to position the meshes within the scene. Though the example code uses arrays, this lesson will only use an individual instance for the sake of clarity.

Creation and Destruction

D3DXCreateText creates a 3D mesh. An ID3DXMesh specifically. Because these lessons are tightly focused there won't be much discussion about ID3DXMesh beyond what we need to render one. There will be other lessons covering them in more detail.

HRESULT WINAPI D3DXCreateText(
   LPDIRECT3DDEVICE9 pDevice,
   HDC hDC,
   LPCTSTR pText,
   FLOAT Deviation,
   FLOAT Extrusion,
   LPD3DXMESH *ppMesh,
   LPD3DXBUFFER *ppAdjacency,
   LPGLYPHMETRICSFLOAT pGlyphMetrics
);
pDevice
A pointer to a valid Direct3D Device.
hDC
A handle to a valid GDI device context. More on this later.
pText
The text to be rendered.
Deviation
Maximum chordal deviation from TrueType font outlines. Allowed error threshold. Higher values make a chunky font with fewer vertices. Smaller values make a smoother but more complicated mesh.
Extrusion
Extrusion depth (along -Z axis).
ppMesh
Pointer to the returned mesh.
ppAdjacency
Adjacency information returned through this pointer. Can be NULL.
pGlyphMetrics
Pointer to an array of Glyph Metrics. Gives information on size and orientation of each glyph in the string. Can be NULL.

In this example we'll set both ppAdjacency and pGlyphMetrics to NULL since we don't need that information. The only parameter that needs more explanation is the hDC. The text rendering methods that have been covered in previous lessons all took information about the font. D3DXCreateText takes the information more indirectly.

To pass font information to D3DXCreateText, a font has to be created and selected into a Device Context (DC). A DC can contain a lot of information, and D3DXCreateText reads the DC for the information it needs. Theoretically by setting different options in the DC a lot of control is possible. Exactly what information is read by D3DXCreateText is undocumented. I would be surprised if more than glyph size and positioning was read.

So before the mesh can be created, a font has to be created and then selected into a DC. Typically a DC is connected to a window or some other kind of display device. In this case, that isn't needed. Any DC will do, so a call to CreateCompatibleDC will take care of that. Passing NULL creates a DC compatible with the current display.

The GDI CreateFont function takes quite a few parameters, but most of them were covered in the D3DXFont tutorial. See the Further Reading section for more information.

Finally, the font has to be selected in to the DC. After that it can be passed to D3DXCreateText.

HDC hdc;
HFONT font;

   hdc=CreateCompatibleDC(NULL);

   font=CreateFont(10,         //Height

                   0,          //Width

                   0,          //Escapement

                   0,          //Orientation

                   FW_NORMAL,  //Weight

                   false,      //Italic

                   false,      //Underline

                   false,      //Strikeout

                   DEFAULT_CHARSET,//Charset 

                   OUT_DEFAULT_PRECIS,  //Output Precision

                   CLIP_DEFAULT_PRECIS, //Clipping Precision

                   DEFAULT_QUALITY,     //Quality

                   DEFAULT_PITCH|FF_DONTCARE, //Pitch and Family

                   "Arial");

   SelectObject(hdc, font);

With that out of the way, here is a typical example of how you'd use D3DXCreateText.

ID3DXMesh *mesh;

   hr=D3DXCreateText(g_d3d_device,  //Device

                     p_dc,          //GDI Device Context

                     "Hello World", //Text 

                     0.001f,        //Maximum chordal deviation from true font outlines       

                     0.4f,          //Extrusion depth (along -Z axis)

                     &mesh,         //Mesh

                     NULL,          //Adjacency information

                     NULL);         //GlyphMetrics

D3DXCreateText is the most complex to set up of all the Direct3D text/font solutions. But it also has the most complex job to perform. It's worth noting that the other solutions created an object that could render arbitrary text. D3DXCreateText creates a mesh out of a given string. Each different piece of text would require its own mesh which makes it unsuitable for large volumes of text or text that is very dynamic (frames per second counter, for example).

The FVF for the mesh is not documented, but a little testing showed that it has Model Space coordinates (D3DFVF_XYZ) and a normal (D3DFVF_NORMAL, for lighting). It has no diffuse member so when it's rendered it will be bright white. There are a number of ways around this problem but they will be discussed in later lessons.

Also, this sample uses 2 other methods provided by D3DXMesh:GetNumVertices and GetNumFaces. These are used so that the total number of vertices and triangles being rendered can be displayed along with the frames per second.

Cleaning up is easy, all it takes is a call to the Release method.

   if(g_text_mesh){
      g_text_mesh->Release();
      g_text_mesh=NULL;
   }

Rendering Text

To render the mesh we call the DrawSubset method. DrawSubset takes a single integer parameter which specifies which subset should be drawn. A subset is a piece of a mesh that has different attributes than the other parts (different texture or material, for example). This way you can draw the separate pieces making any adjustments necessary yet still treat it as a single mesh. Arms on a robot could be subsets which would allow them to be animated (rotated) separately from the rest of the mesh.

Meshes created by D3DXCreateText only have a single subset which makes things easy.

D3DXMatrix world_matrix; //Pre-initialized


   g_d3d_device->SetTransform(D3DTS_WORLD,&world_matrix);

   g_text_mesh->DrawSubset(0);

Lost Device

What memory pool is the mesh allocated from? Although it's not documented, calling the GetOptions method shows that it is created in the Managed pool. This means that no handling is required when you lose and reset your device.

Lesson Downloads

Further Reading

Programming Windows, Fifth Edition by Charles Petzold
From Amazon:

MSDN Links For Functions/Concepts Discussed Here

Back