www.pudn.com > sxdl.zip > d3dfont.cpp


//-----------------------------------------------------------------------------
// File: D3DFont.cpp
//
// Desc: Texture-based font class
//-----------------------------------------------------------------------------
#define STRICT
#include <stdio.h>
#include <tchar.h>
#include <D3DX9.h>
#include "D3DFont.h"
#include "DXUtil.h"

LPDIRECT3DSTATEBLOCK9 CD3DFont::m_pStateBlockSaved = NULL ;
LPDIRECT3DSTATEBLOCK9 CD3DFont::m_pStateBlockDrawText = NULL ;

//-----------------------------------------------------------------------------
// Name: CD3DFont()
// Desc: Font class constructor
//-----------------------------------------------------------------------------
CD3DFont::CD3DFont( const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags )
{
_tcsncpy( m_strFontName, strFontName, sizeof(m_strFontName) / sizeof(TCHAR) );
m_strFontName[sizeof(m_strFontName) / sizeof(TCHAR) - 1] = _T('\0');
m_dwFontHeight = dwHeight;
m_dwFontFlags = dwFlags;
m_dwSpacing = 0;

m_pd3dDevice = NULL;
m_pTexture = NULL;
m_pVB = NULL;
}

//-----------------------------------------------------------------------------
// Name: ~CD3DFont()
// Desc: Font class destructor
//-----------------------------------------------------------------------------
CD3DFont::~CD3DFont()
{
InvalidateDeviceObjects();
DeleteDeviceObjects();
}

//-----------------------------------------------------------------------------
// Name: InitDeviceObjects()
// Desc: Initializes device-dependent objects, including the vertex buffer used
// for rendering text and the texture map which stores the font image.
//-----------------------------------------------------------------------------
HRESULT CD3DFont::InitDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice )
{
HRESULT hr;

// Keep a local copy of the device
m_pd3dDevice = pd3dDevice;

// Establish the font and texture size
m_fTextScale = 1.0f; // Draw fonts into texture without scaling

if ( m_dwFontFlags &amt; D3DFONT_FULLSET )
{
// 256 chars fonts need larger textures
if( m_dwFontHeight > 60 )
m_dwTexWidth = m_dwTexHeight = 2048;
else if( m_dwFontHeight > 30 )
m_dwTexWidth = m_dwTexHeight = 2048;
else if( m_dwFontHeight > 15 )
m_dwTexWidth = m_dwTexHeight = 1024;
else
m_dwTexWidth = m_dwTexHeight = 512;
}
else
{
if( m_dwFontHeight > 60 )
m_dwTexWidth = m_dwTexHeight = 2048;
else if( m_dwFontHeight > 30 )
m_dwTexWidth = m_dwTexHeight = 1024;
else if( m_dwFontHeight > 15 )
m_dwTexWidth = m_dwTexHeight = 512;
else
m_dwTexWidth = m_dwTexHeight = 256;
}
// If requested texture is too big, use a smaller texture and smaller font,
// and scale up when rendering.
D3DCAPS9 d3dCaps;
m_pd3dDevice->GetDeviceCaps( &amt;d3dCaps );

if( m_dwTexWidth > d3dCaps.MaxTextureWidth )
{
m_fTextScale = (FLOAT)d3dCaps.MaxTextureWidth / (FLOAT)m_dwTexWidth;
m_dwTexWidth = m_dwTexHeight = d3dCaps.MaxTextureWidth;
}

// Create a new texture for the font
hr = m_pd3dDevice->CreateTexture( m_dwTexWidth, m_dwTexHeight, 1,
0, D3DFMT_A4R4G4B4,
D3DPOOL_MANAGED, &amt;m_pTexture, NULL );
if( FAILED(hr) )
return hr;

// Prepare to create a bitmap
DWORD* pBitmapBits;
BITMAPINFO bmi;
ZeroMemory( &amt;bmi.bmiHeader, sizeof(BITMAPINFOHEADER) );
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = (int)m_dwTexWidth;
bmi.bmiHeader.biHeight = -(int)m_dwTexHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biBitCount = 32;

// Create a DC and a bitmap for the font
HDC hDC = CreateCompatibleDC( NULL );
HBITMAP hbmBitmap = CreateDIBSection( hDC, &amt;bmi, DIB_RGB_COLORS,
(void**)&amt;pBitmapBits, NULL, 0 );
SetMapMode( hDC, MM_TEXT );

// Create a font. By specifying ANTIALIASED_QUALITY, we might get an
// antialiased font, but this is not guaranteed.
INT nHeight = -MulDiv( m_dwFontHeight,
(INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72 );
DWORD dwBold = (m_dwFontFlags&amt;D3DFONT_BOLD) ? FW_BOLD : FW_NORMAL;
DWORD dwItalic = (m_dwFontFlags&amt;D3DFONT_ITALIC) ? TRUE : FALSE;
HFONT hFont = CreateFont( nHeight, 0, 0, 0, dwBold, dwItalic,
FALSE, FALSE, DEFAULT_CHARSET , OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
VARIABLE_PITCH, m_strFontName );
if( NULL==hFont )
return E_FAIL;

HGDIOBJ hbmOld = SelectObject( hDC, hbmBitmap );
HGDIOBJ hFontOld = SelectObject( hDC, hFont );

// Set text properties
SetTextColor( hDC, RGB(255,255,255) );
SetBkColor( hDC, 0x00000000 );
SetTextAlign( hDC, TA_TOP );

// Loop through all printable character and output them to the bitmap..
// Meanwhile, keep track of the corresponding tex coords for each character.
DWORD x = 0;
DWORD y = 0;
TCHAR str[2] = _T("x");
SIZE size;

// Calculate the spacing between characters based on line height
GetTextExtentPoint32( hDC, TEXT(" "), 1, &amt;size );
x = m_dwSpacing = (DWORD) ceil(size.cy * 0.3f);

int Limit = ( m_dwFontFlags &amt; D3DFONT_FULLSET ) ? 256 : 128 ;
for( int c = 32; c < Limit ; c++ )
{
str[0] = ( TCHAR ) c;

GetTextExtentPoint32( hDC, str, 1, &amt;size );

if( (DWORD)(x + size.cx + m_dwSpacing) > m_dwTexWidth )
{
x = m_dwSpacing;
y += size.cy+1;
}

ExtTextOut( hDC, x+0, y+0, ETO_OPAQUE, NULL, str, 1, NULL );

m_fTexCoords[ c - 32 ][0] = ((FLOAT)(x + 0 - m_dwSpacing))/m_dwTexWidth;
m_fTexCoords[ c - 32 ][1] = ((FLOAT)(y + 0 + 0 ))/m_dwTexHeight;
m_fTexCoords[ c - 32 ][2] = ((FLOAT)(x + size.cx + m_dwSpacing))/m_dwTexWidth;
m_fTexCoords[ c - 32 ][3] = ((FLOAT)(y + size.cy + 0 ))/m_dwTexHeight;

x += size.cx + (2 * m_dwSpacing);
}

// Lock the surface and write the alpha values for the set pixels
D3DLOCKED_RECT d3dlr;
m_pTexture->LockRect( 0, &amt;d3dlr, 0, 0 );
BYTE* pDstRow = (BYTE*)d3dlr.pBits;
WORD* pDst16;
BYTE bAlpha; // 4-bit measure of pixel intensity

for( y=0; y < m_dwTexHeight; y++ )
{
pDst16 = (WORD*)pDstRow;
for( x=0; x < m_dwTexWidth; x++ )
{
bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth*y + x] &amt; 0xff) >> 4);
if (bAlpha > 0)
{
*pDst16++ = (WORD) ((bAlpha << 12) | 0x0fff);
}
else
{
*pDst16++ = 0x0000;
}
}
pDstRow += d3dlr.Pitch;
}

// Done updating texture, so clean up used objects
m_pTexture->UnlockRect(0);
SelectObject( hDC, hbmOld );
SelectObject( hDC, hFontOld );
DeleteObject( hbmBitmap );
DeleteObject( hFont );
DeleteDC( hDC );

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc:
//-----------------------------------------------------------------------------
HRESULT CD3DFont::RestoreDeviceObjects()
{
HRESULT hr;

// Create vertex buffer for the letters
int vertexSize = max( sizeof(FONT2DVERTEX), sizeof(FONT3DVERTEX ) );
if( FAILED( hr = m_pd3dDevice->CreateVertexBuffer( MAX_NUM_VERTICES * vertexSize,
D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
D3DPOOL_DEFAULT, &amt;m_pVB, NULL ) ) )
{
return hr;
}

// The state blocks are static and the same for all fonts
if ( ( m_pStateBlockSaved == NULL ) &amt;&amt; ( m_pStateBlockDrawText == NULL ) )
{
// Create the state blocks for rendering text
for( UINT which=0; which<2; which++ )
{
m_pd3dDevice->BeginStateBlock();

// We always disable the Z buffer
//m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
// m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE ) ;
//m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
//m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
//m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
//m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
//m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 );
//m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
//m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
//m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
//m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
//m_pd3dDevice->SetRenderState( D3DRS_CLIPPING, TRUE );
//m_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE, FALSE );
//m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE );
//m_pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
//m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
//m_pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE,
// D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN |
// D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
//m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
//m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
//m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//// no mips
// m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
//// Font are always filtered
//m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
//m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
if( which==0 )
m_pd3dDevice->EndStateBlock( &amt;m_pStateBlockSaved );
else
m_pd3dDevice->EndStateBlock( &amt;m_pStateBlockDrawText );
} ; // end for
} ; // end if

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InvalidateDeviceObjects()
// Desc: Destroys all device-dependent objects
//-----------------------------------------------------------------------------
HRESULT CD3DFont::InvalidateDeviceObjects()
{
SAFE_RELEASE( m_pVB );
SAFE_RELEASE( m_pStateBlockSaved );
SAFE_RELEASE( m_pStateBlockDrawText );

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DeleteDeviceObjects()
// Desc: Destroys all device-dependent objects
//-----------------------------------------------------------------------------
HRESULT CD3DFont::DeleteDeviceObjects()
{
SAFE_RELEASE( m_pTexture );
m_pd3dDevice = NULL;

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: GetTextExtent()
// Desc: Get the dimensions of a text string
//-----------------------------------------------------------------------------
HRESULT CD3DFont::GetTextExtent( const TCHAR* strText, SIZE* pSize )
{
if( NULL==strText || NULL==pSize )
return E_FAIL;

FLOAT fRowWidth = 0.0f;
FLOAT fRowHeight = (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
FLOAT fWidth = 0.0f;
FLOAT fHeight = fRowHeight;

while( *strText )
{
TCHAR c = *strText++;

if( c == _T('\n') )
{
fRowWidth = 0.0f;
fHeight += fRowHeight;
}

int index = ( int ) ( ( unsigned int ) ( ( unsigned char ) c ) ) - 32 ;
FLOAT tx1 = m_fTexCoords[index][0];
FLOAT tx2 = m_fTexCoords[index][2];

fRowWidth += (tx2-tx1)*m_dwTexWidth - 2*m_dwSpacing;

if( fRowWidth > fWidth )
fWidth = fRowWidth;
}

pSize->cx = (int)fWidth;
pSize->cy = (int)fHeight;

return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DrawTextScaled()
// Desc: Draws scaled 2D text. Note that x and y are in viewport coordinates
// (ranging from -1 to +1). fXScale and fYScale are the size fraction
// relative to the entire viewport. For example, a fXScale of 0.25 is
// 1/8th of the screen width. This allows you to output text at a fixed
// fraction of the viewport, even if the screen or window size changes.
//-----------------------------------------------------------------------------
void CD3DFont::DrawTextScaled( FLOAT x, FLOAT y, /* FLOAT z, */
FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
const TCHAR* strText , DWORD dwFlags )
{
FLOAT z = 0.0f ;
y = - y ;

D3DVIEWPORT9 vp;
m_pd3dDevice->GetViewport( &amt;vp );
FLOAT fLineHeight = ( m_fTexCoords[0][3] - m_fTexCoords[0][1] ) * m_dwTexHeight;

// Center the text block in the viewport
if( dwFlags &amt; D3DFONT_CENTERED_X )
{
const TCHAR* strTextTmp = strText;
float xFinal = 0.0f;

while( *strTextTmp )
{
TCHAR c = *strTextTmp++;

if( c == _T('\n') )
break; // Isn't supported.
int index = ( int ) ( ( unsigned int ) ( ( unsigned char ) c ) ) - 32 ;
FLOAT tx1 = m_fTexCoords[index][0];
FLOAT tx2 = m_fTexCoords[index][2];

FLOAT w = (tx2-tx1)*m_dwTexWidth;

w *= (fXScale*vp.Height)/fLineHeight;

xFinal += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight;
}

x = -xFinal/vp.Width;
}
if( dwFlags &amt; D3DFONT_CENTERED_Y )
{
y = -fLineHeight/vp.Height;
}

FLOAT sx = (x+1.0f)*vp.Width/2;
FLOAT sy = (y+1.0f)*vp.Height/2;
FLOAT sz = z;
FLOAT rhw = 1.0f;

// Adjust for character spacing
sx -= m_dwSpacing * (fXScale*vp.Height)/fLineHeight;
FLOAT fStartX = sx;

// Fill vertex buffer
FONT2DVERTEX* pVertices;
DWORD dwNumTriangles = 0L;
m_pVB->Lock( 0, 0, (void**)&amt;pVertices, D3DLOCK_DISCARD );

while( *strText )
{
TCHAR c = *strText++;

if( c == _T('\n') )
{
sx = fStartX;
sy += fYScale*vp.Height;
}

int index = ( int ) ( ( unsigned int ) ( ( unsigned char ) c ) ) - 32 ;
FLOAT tx1 = m_fTexCoords[index][0];
FLOAT ty1 = m_fTexCoords[index][1];
FLOAT tx2 = m_fTexCoords[index][2];
FLOAT ty2 = m_fTexCoords[index][3];

FLOAT w = (tx2-tx1)*m_dwTexWidth;
FLOAT h = (ty2-ty1)*m_dwTexHeight;

w *= (fXScale*vp.Height)/fLineHeight;
h *= (fYScale*vp.Height)/fLineHeight;

if( c != _T(' ') )
{
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx1, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx2, ty1 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
dwNumTriangles += 2;

if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
m_pVB->Lock( 0, 0, (void**)&amt;pVertices, D3DLOCK_DISCARD );
dwNumTriangles = 0L;
}
}

sx += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight;
}

// Unlock and render the vertex buffer
m_pVB->Unlock();
if( dwNumTriangles > 0 )
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );

return ;
}

//-----------------------------------------------------------------------------
// Name: DrawText()
// Desc: Draws 2D text. Note that sx and sy are in pixels
//-----------------------------------------------------------------------------
void CD3DFont::DrawText( FLOAT sx, FLOAT sy, DWORD dwColor,
const TCHAR* strText , DWORD dwFlags )
{
// Center the text block in the viewport
if( dwFlags &amt; D3DFONT_CENTERED_X )
{
D3DVIEWPORT9 vp;
m_pd3dDevice->GetViewport( &amt;vp );
const TCHAR* strTextTmp = strText;
float xFinal = 0.0f;

while( *strTextTmp )
{
TCHAR c = *strTextTmp++;

if( c == _T('\n') )
break; // Isn't supported.
//if( (c-32) < 0 || (c-32) >= 128-32 )
// continue;

int index = ( int ) ( ( unsigned int ) ( ( unsigned char ) c ) ) - 32 ;
FLOAT tx1 = m_fTexCoords[index][0];
FLOAT tx2 = m_fTexCoords[index][2];

FLOAT w = (tx2-tx1) * m_dwTexWidth / m_fTextScale;

xFinal += w - (2 * m_dwSpacing);
}

sx = (vp.Width-xFinal)/2.0f;
}
if( dwFlags &amt; D3DFONT_CENTERED_Y )
{
D3DVIEWPORT9 vp;
m_pd3dDevice->GetViewport( &amt;vp );
float fLineHeight = ((m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight);
sy = (vp.Height-fLineHeight)/2;
}

// Adjust for character spacing
sx -= m_dwSpacing;
FLOAT fStartX = sx;

// Fill vertex buffer
FONT2DVERTEX* pVertices = NULL;
DWORD dwNumTriangles = 0;
m_pVB->Lock( 0, 0, (void**)&amt;pVertices, D3DLOCK_DISCARD );

while( *strText )
{
TCHAR c = *strText++;

if( c == _T('\n') )
{
sx = fStartX;
sy += (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
}

int index = ( int ) ( ( unsigned int ) ( ( unsigned char ) c ) ) - 32 ;
FLOAT tx1 = m_fTexCoords[index][0];
FLOAT ty1 = m_fTexCoords[index][1];
FLOAT tx2 = m_fTexCoords[index][2];
FLOAT ty2 = m_fTexCoords[index][3];

FLOAT w = (tx2-tx1) * m_dwTexWidth / m_fTextScale;
FLOAT h = (ty2-ty1) * m_dwTexHeight / m_fTextScale;

if( c != _T(' ') )
{
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx1, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx2, ty1 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
dwNumTriangles += 2;

if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
pVertices = NULL;
m_pVB->Lock( 0, 0, (void**)&amt;pVertices, D3DLOCK_DISCARD );
dwNumTriangles = 0L;
}
}

sx += w - (2 * m_dwSpacing);
}

// Unlock and render the vertex buffer
m_pVB->Unlock();
if( dwNumTriangles > 0 )
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );

return ;
}

//void CD3DFont::Begin3D ( )
//{
// if( m_pd3dDevice == NULL )
// return ;
//
// // Setup renderstate
// m_pd3dDevice->SetTexture( 0, m_pTexture );
// m_pStateBlockSaved->Capture();
// m_pStateBlockDrawText->Apply();
// m_pd3dDevice->SetFVF( D3DFVF_FONT3DVERTEX );
// m_pd3dDevice->SetPixelShader( NULL );
// m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT3DVERTEX) );
//
// return ;
//}
//
//
//void CD3DFont::End3D ( )
//{
// // Restore the modified renderstates
// m_pStateBlockSaved->Apply();
// return ;
//}
//
//-----------------------------------------------------------------------------
// Name: Render3DText()
// Desc: Renders 3D text
//-----------------------------------------------------------------------------
void CD3DFont::Render3DText( const TCHAR* strText , DWORD dwFlags )
{
// Position for each text element
FLOAT x = 0.0f;
FLOAT y = 0.0f;

// Center the text block at the origin (not the viewport)
if( dwFlags &amt; D3DFONT_CENTERED_X )
{
SIZE sz;
GetTextExtent( strText, &amt;sz );
x = -(((FLOAT)sz.cx)/10.0f)/2.0f;
}
if( dwFlags &amt; D3DFONT_CENTERED_Y )
{
SIZE sz;
GetTextExtent( strText, &amt;sz );
y = -(((FLOAT)sz.cy)/10.0f)/2.0f;
}

// Turn off culling for two-sided text
if( dwFlags &amt; D3DFONT_TWOSIDED )
m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

// Adjust for character spacing
x -= m_dwSpacing / 10.0f;
FLOAT fStartX = x;
TCHAR c;

// Fill vertex buffer
FONT3DVERTEX* pVertices;
DWORD dwNumTriangles = 0L;
m_pVB->Lock( 0, 0, (void**)&amt;pVertices, D3DLOCK_DISCARD );

while( (c = *strText++) != 0 )
{
if( c == '\n' )
{
x = fStartX;
y -= (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight/10.0f;
}

int index = ( int ) ( ( unsigned int ) ( ( unsigned char ) c ) ) - 32 ;
FLOAT tx1 = m_fTexCoords[index][0];
FLOAT ty1 = m_fTexCoords[index][1];
FLOAT tx2 = m_fTexCoords[index][2];
FLOAT ty2 = m_fTexCoords[index][3];

FLOAT w = (tx2-tx1) * m_dwTexWidth / ( 10.0f * m_fTextScale );
FLOAT h = (ty2-ty1) * m_dwTexHeight / ( 10.0f * m_fTextScale );

if( c != _T(' ') )
{
*pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+0,0), D3DXVECTOR3(0,0,-1), tx1, ty2 );
*pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
*pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
*pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+h,0), D3DXVECTOR3(0,0,-1), tx2, ty1 );
*pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
*pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
dwNumTriangles += 2;

if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
m_pVB->Lock( 0, 0, (void**)&amt;pVertices, D3DLOCK_DISCARD );
dwNumTriangles = 0L;
}
}

x += w - (2 * m_dwSpacing) / 10.0f;
}

// Unlock and render the vertex buffer
m_pVB->Unlock();
if( dwNumTriangles > 0 )
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );

return ;
}