创建三维文本网格模型
在Direct3D中,三维物体的显示是通过网格模型来实现的,显示三维物体的关键在于生成该网格模型。三维文本也不例外,显示三维文本同样需要该文本所对应的网格模型。
Direct3D为此提供了功能库函数D3DXCreateText(),通过它可以方便地创建一个包含具体文本的网格模型,该函数的声明如下:
Creates a mesh containing the specified text, using the
font associated with the device context.
HRESULT D3DXCreateText(
LPDIRECT3DDEVICE9 pDevice,
HDC hDC,
LPCTSTR pText,
FLOAT Deviation,
FLOAT Extrusion,
LPD3DXMESH * ppMesh,
LPD3DXBUFFER * ppAdjacency,
LPGLYPHMETRICSFLOAT pGlyphMetrics
);
Parameters
- pDevice
- [in] Pointer to the device that created the mesh.
- hDC
- [in] Device context, containing the font for
output. The font selected by the device context must be a TrueType font.
- pText
- [in] Pointer to a string that specifies the text
to generate. If the compiler settings require Unicode, the data type LPCTSTR
resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See
Remarks.
- Deviation
- [in] Maximum chordal deviation from TrueType font
outlines.
- Extrusion
- [in] Amount to extrude text in the negative
z-direction.
- ppMesh
- [out] Pointer to the returned mesh.
- ppAdjacency
- [out] Pointer to a buffer containing adjacency
information. May be NULL.
- pGlyphMetrics
- [out] Pointer to an array of GLYPHMETRICSFLOAT
structures that contain the glyph metric data. Each element contains
information about the position and orientation of the corresponding glyph in
the string. The number of elements in the array should be equal to the
number of characters in the string. Note that the origin in each structure
is not relative to the entire string, but rather is relative to that
character cell. To compute the entire bounding box, add the increment for
each glyph while traversing the string. If you are not concerned with the
glyph sizes, set this parameter to NULL.
Return Values
If the function succeeds, the return value is D3D_OK.
If the function fails, the return value can be one of the following:
D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.
Remarks
The compiler setting also determines the function
version. If Unicode is defined, the function call resolves to D3DXCreateTextW.
Otherwise, the function call resolves to D3DXCreateTextA because ANSI strings
are being used.
This function creates a mesh with the D3DXMESH_MANAGED
creation option and D3DFVF_XYZ | D3DFVF_NORMAL flexible vertex format (FVF).
Deviaton指定弦偏差的最大值。
Extrusion指定文本在z轴负方向突出的总量。
GLYPHMETRICSFLOAT
The GLYPHMETRICSFLOAT
structure contains information about the placement and orientation of a glyph in
a character cell.
typedef struct _GLYPHMETRICSFLOAT { // gmf
FLOAT gmfBlackBoxX;
FLOAT gmfBlackBoxY;
POINTFLOAT gmfptGlyphOrigin;
FLOAT gmfCellIncX;
FLOAT gmfCellIncY;
} GLYPHMETRICSFLOAT;
Members
- gmfBlackBoxX
- Specifies the width of the smallest rectangle (the
glyph's black box) that completely encloses the glyph.
- gmfBlackBoxY
- Specifies the height of the smallest rectangle
(the glyph's black box) that completely encloses the glyph.
- gmfptGlyphOrigin
- Specifies the x and y coordinates of the
upper-left corner of the smallest rectangle that completely encloses the
glyph.
- gmfCellIncX
- Specifies the horizontal distance from the origin
of the current character cell to the origin of the next character cell.
- gmfCellIncY
- Specifies the vertical distance from the origin of
the current character cell to the origin of the next character cell.
Remarks
The values of GLYPHMETRICSFLOAT are specified as
notional units.
示例代码如下:
HDC hdc = CreateCompatibleDC(NULL);
if(hdc == NULL)
return false;
HFONT hfont = CreateFont(0, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
SelectObject(hdc, hfont);
D3DXCreateText(g_device, hdc, L"三维字体", 0.001f, 0.4f, &g_text_mesh, NULL, NULL);
DeleteObject(hfont);
DeleteDC(hdc);
The CreateCompatibleDC function creates a memory
device context (DC) compatible with the specified device.
HDC CreateCompatibleDC(
HDC hdc // handle to DC
);
Parameters
- hdc
- [in] Handle to an existing DC. If this handle is
NULL, the function creates a memory DC compatible with the application's
current screen.
Return Values
If the function succeeds, the return value is the
handle to a memory DC.
If the function fails, the return value is NULL.
Windows NT/2000/XP: To get extended error
information, call
GetLastError.
Remarks
A memory DC exists only in memory. When the memory DC
is created, its display surface is exactly one monochrome pixel wide and one
monochrome pixel high. Before an application can use a memory DC for drawing
operations, it must select a bitmap of the correct width and height into the DC.
To select a bitmap into a DC, use the CreateCompatibleBitmap function,
specifying the height, width, and color organization required.
When a memory DC is created, all attributes are set to
normal default values. The memory DC can be used as a normal DC. You can set the
attributes; obtain the current settings of its attributes; and select pens,
brushes, and regions.
The CreateCompatibleDC function can only be used
with devices that support raster operations. An application can determine
whether a device supports these operations by calling the GetDeviceCaps
function.
When you no longer need the memory DC, call the
DeleteDC function.
Windows 2000 and later: If hdc is NULL,
the thread that calls CreateCompatibleDC owns the HDC that is created.
When this thread is destroyed, the HDC is no longer valid. Thus, if you create
the HDC andpass it to another thread, then exit the first thread, the second
thread will not be able to use the HDC.
绘制三维文本网格模型
创建好文本的网格模型之后,就可以使用ID3DXMesh的接口函数DrawSubset()将其绘制出来,在绘制之前,需要注意设置合适的世界矩阵,这时虽然是绘制三维文本,但实质上就是绘制一个三维物体,所以为三维文本设置世界矩阵是必不可少的。示例代码如下:
void setup_world_matrix()
{
D3DXMATRIX mat_translation;
D3DXMatrixTranslation(&mat_translation, -1.75f, 0.0f, 0.0f);
D3DXMATRIX mat_world;
D3DXMatrixRotationY(&mat_world, timeGetTime()/1000.0f);
mat_world = mat_translation * mat_world;
g_device->SetTransform(D3DTS_WORLD, &mat_world);
}
void render()
{
g_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_X#050505, 1.0f, 0);
g_device->BeginScene();
setup_world_matrix();
g_text_mesh->DrawSubset(0);
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
}
使用D3DXCreateText()函数为文本创建网格模型时,网格模型的原点在左下方,所以需要对文本网格模型进行平移,使其基本上显示在窗口的中央。
源程序:
#include <D3DX9.h>
#pragma warning(disable : 4127)
#define CLASS_NAME "GameApp"
#define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0)
IDirect3D9* g_d3d;
IDirect3DDevice9* g_device;
ID3DXMesh* g_text_mesh;
void setup_world_matrix()
{
D3DXMATRIX mat_translation;
D3DXMatrixTranslation(&mat_translation, -1.75f, 0.0f, 0.0f);
D3DXMATRIX mat_world;
D3DXMatrixRotationY(&mat_world, timeGetTime()/1000.0f);
mat_world = mat_translation * mat_world;
g_device->SetTransform(D3DTS_WORLD, &mat_world);
}
void setup_view_proj_matrices()
{
D3DXVECTOR3 eye(0.0f, 0.0f,-8.0f);
D3DXVECTOR3 at(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX mat_view;
D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
g_device->SetTransform(D3DTS_VIEW, &mat_view);
D3DXMATRIX mat_proj;
D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}
void setup_material_light()
{
D3DMATERIAL9 material;
ZeroMemory(&material, sizeof(D3DMATERIAL9));
material.Diffuse.r = material.Ambient.r = 1.0f;
material.Diffuse.g = material.Ambient.g = 1.0f;
material.Diffuse.b = material.Ambient.b = 1.0f;
material.Diffuse.a = material.Ambient.a = 1.0f;
g_device->SetMaterial(&material);
D3DLIGHT9 light;
ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_DIRECTIONAL;
light.Diffuse.r = 0.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
light.Range = 1000.0f;
D3DXVECTOR3 light_dir(1, 1, 1);
D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &light_dir);
g_device->SetLight(0, &light);
g_device->LightEnable(0, TRUE);
g_device->SetRenderState(D3DRS_LIGHTING, TRUE);
g_device->SetRenderState(D3DRS_AMBIENT, 0xffFF50FF);
}
bool init_d3d(HWND hwnd)
{
g_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(g_d3d == NULL)
return false;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}
HDC hdc = CreateCompatibleDC(NULL);
if(hdc == NULL)
return false;
HFONT hfont = CreateFont(0, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
SelectObject(hdc, hfont);
D3DXCreateText(g_device, hdc, L"三维字体", 0.001f, 0.4f, &g_text_mesh, NULL, NULL);
DeleteObject(hfont);
DeleteDC(hdc);
g_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_device->SetRenderState(D3DRS_LIGHTING, TRUE);
setup_view_proj_matrices();
setup_material_light();
return true;
}
void cleanup()
{
release_com(g_text_mesh);
release_com(g_device);
release_com(g_d3d);
}
void render()
{
g_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0);
g_device->BeginScene();
setup_world_matrix();
g_text_mesh->DrawSubset(0);
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
}
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_KEYDOWN:
if(wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = inst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"GameApp";
wc.hIconSm = NULL;
if(! RegisterClassEx(&wc))
return -1;
HWND hwnd = CreateWindow(L"GameApp", L"Direct3D App", WS_OVERLAPPEDWINDOW, 200, 100, 600, 500,
NULL, NULL, wc.hInstance, NULL);
if(hwnd == NULL)
return -1;
if(init_d3d(hwnd))
{
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
render();
Sleep(10);
}
}
cleanup();
UnregisterClass(L"GameApp", wc.hInstance);
return 0;
}