The
cTextWindow Class
To get things rolling, take a
look at the following cTextWindow class definition:
#include <stdlib.h>
#include "core_common.h"
#include "core_graphics.h"
typedef class cTextWindow
{
private:
typedef struct sVertex
{
float x, y, z; // coordinates in screen space
float rhw;
D3DCOLOR diffuse;
} *sVertexPtr;
#define WINDOW_FVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
ID3DXFont* m_font;
IDirect3DVertexBuffer9* m_vb; // vertex buffer for window text
char* m_text; // text to display
D3DCOLOR m_text_color; // color to draw text
long m_x_pos, m_y_pos; // window coordinates
long m_width, m_height; // window dimensions
bool m_draw_target; // flag to draw bubble pointer
public:
cTextWindow()
{
memset(this, 0, sizeof(*this));
m_text_color = 0xFFFFFFFF;
}
~cTextWindow()
{
free();
}
void create(ID3DXFont* font)
{
free();
m_font = font;
create_vertex_buffer(&m_vb, 11, sizeof(sVertex), WINDOW_FVF);
}
void free()
{
release_com(m_vb);
delete[] m_text;
}
void set_text(pcstr text, D3DCOLOR text_color)
{
delete[] m_text;
m_text = strdup(text);
m_text_color = text_color;
}
long get_height()
{
return m_height;
}
void move(long x_pos, long y_pos, long width, long height,
long target_x, long target_y,
D3DCOLOR back_color, D3DCOLOR outline_color);
void render(pcstr text, D3DCOLOR text_color);
} *cTextWindowPtr;
Remember that the text window uses
a vertex buffer to contain a
couple of rectangles (with two triangles defined per rectangle). The vertex
buffer uses
only transformed vertices that are assigned a diffuse color (white for the
larger rectangle
in the back and your color of choice for the smaller rectangle in the front).
Each
vertex is stored within the preceding sVertex structure
(and matching vertex descriptor).
cTextWindow::move
The biggest of the bunch, cTextWindow::Move has the job of constructing the
vertex buffer
used to render the window (and supporting pointer, if needed). The function
takes
as arguments the position to place the window (screen coordinates), the
dimensions
(in pixels), a pair of coordinates at which to point the text-bubble pointer,
and a
color to use for the smaller frontmost window.
void cTextWindow::move(long x_pos, long y_pos, long width, long height,
long target_x, long target_y,
D3DCOLOR back_color, D3DCOLOR outline_color)
{
m_x_pos = x_pos;
m_y_pos = y_pos;
m_width = width;
// calculate height if needed
if((m_height = height) == 0)
{
RECT rect;
rect.left = x_pos;
rect.top = 0;
rect.right = x_pos + width - 12;
rect.bottom = 1;
int text_height = m_font->DrawText(NULL, m_text, -1, &rect, DT_CALCRECT | DT_WORDBREAK, 0xFFFFFFFF);
m_height = text_height + 12;
}
sVertex verts[11];
// initialize vertex data
for(long i = 0; i < 11; i++)
{
verts[i].z = 0.0f;
verts[i].rhw = 1.0f;
verts[i].diffuse = outline_color;
}
// setup outline
verts[0].x = (float) m_x_pos;
verts[0].y = (float) m_y_pos;
verts[1].x = (float) (m_x_pos + m_width);
verts[1].y = (float) m_y_pos;
verts[2].x = (float) m_x_pos;
verts[2].y = (float) m_y_pos + m_height;
verts[3].x = (float) (m_x_pos + m_width);
verts[3].y = (float) (m_y_pos + m_height);
// setup text window
verts[4].x = (float) m_x_pos + 2.0f;
verts[4].y = (float) m_y_pos + 2.0f;
verts[4].diffuse = back_color;
verts[5].x = (float) m_x_pos + m_width - 2.0f;
verts[5].y = (float) m_y_pos + 2.0f;
verts[5].diffuse = back_color;
verts[6].x = (float) m_x_pos + 2.0f;
verts[6].y = (float) m_y_pos + m_height + 2.0f;
verts[6].diffuse = back_color;
verts[7].x = (float) m_x_pos + m_width - 2.0f;
verts[7].y = (float) m_y_pos + m_height - 2.0f;
verts[7].diffuse = back_color;
// setup the target position
if(target_x != -1 && target_y != -1)
{
m_draw_target = true;
if(target_y < m_y_pos)
{
verts[8].x = (float) target_x;
verts[8].y = (float) target_y;
verts[9].x = (float) (m_x_pos + m_width/2 + 10);
verts[9].y = (float) m_y_pos;
verts[10].x = (float) (m_x_pos + m_width/2 - 10);
verts[10].y = (float) m_y_pos;
}
else
{
verts[8].x = (float) (m_x_pos + m_width/2 - 10);
verts[8].y = (float) (m_y_pos + m_height);
verts[9].x = (float) (m_x_pos + m_width/2 + 10);
verts[9].y = (float) (m_y_pos + m_height);
verts[10].x = (float) target_x;
verts[10].y = (float) target_y;
}
}
else
m_draw_target = false;
fill_in_vertex_buffer(m_vb, 0, 11, &verts);
}
Using the previously stored
position and dimensions, the windows are constructed
as shown in Figure 16.7.
cTextWindow::render
Render merely sets the required rendering states and
draws the required polygons that form the pointer and text window. Then it draws
the text string (if the window height is greater than 12, which is the size of
the
border used to surround the smaller frontmost window).
void cTextWindow::render(pcstr text, D3DCOLOR text_color)
{
if(m_font == NULL)
return;
g_d3d_device->SetTexture(0, NULL);
disable_zbuffer();
render_vertex_buffer(m_vb, 0, 2, D3DPT_TRIANGLESTRIP);
render_vertex_buffer(m_vb, 4, 2, D3DPT_TRIANGLESTRIP);
if(m_draw_target)
render_vertex_buffer(m_vb, 8, 1, D3DPT_TRIANGLELIST);
if(m_height > 12)
{
if(text == NULL)
draw_font(m_font, m_text, m_x_pos+6, m_y_pos+6, m_width-12, m_height-12, m_text_color, DT_WORDBREAK);
else
draw_font(m_font, text, m_x_pos+6, m_y_pos+6, m_width-12, m_height-12, text_color, DT_WORDBREAK);
}
}
Render takes two optional
arguments. The first argument, Text, overrides the class’s
text that was already set using the SetText function. Overriding the text to
draw is
great for dynamically updating what needs to be shown. The second argument,
Color, specifies the color you want to use to draw the text to the display.