www.pudn.com > Water_Simulation.rar > d3dfont.cpp


//----------------------------------------------------------------------------- 
// File: D3DFont.cpp 
// 
// Desc: Texture-based font class 
//----------------------------------------------------------------------------- 
#define STRICT 
#include  
#include  
#include  
#include "D3DFont.h" 
#include "D3DUtil.h" 
#include "DXUtil.h" 
 
 
 
 
//----------------------------------------------------------------------------- 
// Custom vertex types for rendering text 
//----------------------------------------------------------------------------- 
#define MAX_NUM_VERTICES 50*6 
 
struct FONT2DVERTEX { D3DXVECTOR4 p;   DWORD color;     FLOAT tu, tv; }; 
struct FONT3DVERTEX { D3DXVECTOR3 p;   D3DXVECTOR3 n;   FLOAT tu, tv; }; 
 
#define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) 
#define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1) 
 
inline FONT2DVERTEX InitFont2DVertex( const D3DXVECTOR4& p, D3DCOLOR color, 
                                      FLOAT tu, FLOAT tv ) 
{ 
    FONT2DVERTEX v;   v.p = p;   v.color = color;   v.tu = tu;   v.tv = tv; 
    return v; 
} 
 
inline FONT3DVERTEX InitFont3DVertex( const D3DXVECTOR3& p, const D3DXVECTOR3& n, 
                                      FLOAT tu, FLOAT tv ) 
{ 
    FONT3DVERTEX v;   v.p = p;   v.n = n;   v.tu = tu;   v.tv = tv; 
    return v; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// 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; 
 
    m_pStateBlockSaved     = NULL; 
    m_pStateBlockDrawText  = 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 
 
    // Large fonts need larger textures 
    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( &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, &m_pTexture, NULL ); 
    if( FAILED(hr) ) 
        return hr; 
 
    // Prepare to create a bitmap 
    DWORD*      pBitmapBits; 
    BITMAPINFO bmi; 
    ZeroMemory( &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, &bmi, DIB_RGB_COLORS, 
                                          (void**)&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&D3DFONT_BOLD)   ? FW_BOLD : FW_NORMAL; 
    DWORD dwItalic = (m_dwFontFlags&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, &size ); 
    x = m_dwSpacing = (DWORD) ceil(size.cy * 0.3f); 
 
    for( TCHAR c=32; c<127; c++ ) 
    { 
        str[0] = c; 
        GetTextExtentPoint32( hDC, str, 1, &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, &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] & 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, &m_pVB, NULL ) ) ) 
    { 
        return hr; 
    } 
 
    // Create the state blocks for rendering text 
    for( UINT which=0; which<2; which++ ) 
    { 
        m_pd3dDevice->BeginStateBlock(); 
        m_pd3dDevice->SetTexture( 0, m_pTexture ); 
 
        if ( D3DFONT_ZENABLE & m_dwFontFlags ) 
            m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); 
        else 
            m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, 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 ); 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT ); 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); 
 
        if( which==0 ) 
            m_pd3dDevice->EndStateBlock( &m_pStateBlockSaved ); 
        else 
            m_pd3dDevice->EndStateBlock( &m_pStateBlockDrawText ); 
    } 
 
    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; 
        } 
 
        if( (c-32) < 0 || (c-32) >= 128-32 ) 
            continue; 
 
        FLOAT tx1 = m_fTexCoords[c-32][0]; 
        FLOAT tx2 = m_fTexCoords[c-32][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. 
//----------------------------------------------------------------------------- 
HRESULT CD3DFont::DrawTextScaled( FLOAT x, FLOAT y, FLOAT z, 
                                  FLOAT fXScale, FLOAT fYScale, DWORD dwColor, 
                                  const TCHAR* strText, DWORD dwFlags ) 
{ 
    if( m_pd3dDevice == NULL ) 
        return E_FAIL; 
 
    // Set up renderstate 
    m_pStateBlockSaved->Capture(); 
    m_pStateBlockDrawText->Apply(); 
    m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX ); 
    m_pd3dDevice->SetPixelShader( NULL ); 
    m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) ); 
 
    // Set filter states 
    if( dwFlags & D3DFONT_FILTERED ) 
    { 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); 
    } 
 
    D3DVIEWPORT9 vp; 
    m_pd3dDevice->GetViewport( &vp ); 
    FLOAT fLineHeight = ( m_fTexCoords[0][3] - m_fTexCoords[0][1] ) * m_dwTexHeight; 
 
    // Center the text block in the viewport 
    if( dwFlags & D3DFONT_CENTERED_X ) 
    { 
        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; 
 
            FLOAT tx1 = m_fTexCoords[c-32][0]; 
            FLOAT tx2 = m_fTexCoords[c-32][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 & 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**)&pVertices, D3DLOCK_DISCARD ); 
 
    while( *strText ) 
    { 
        TCHAR c = *strText++; 
 
        if( c == _T('\n') ) 
        { 
            sx  = fStartX; 
            sy += fYScale*vp.Height; 
        } 
 
        if( (c-32) < 0 || (c-32) >= 128-32 ) 
            continue; 
 
        FLOAT tx1 = m_fTexCoords[c-32][0]; 
        FLOAT ty1 = m_fTexCoords[c-32][1]; 
        FLOAT tx2 = m_fTexCoords[c-32][2]; 
        FLOAT ty2 = m_fTexCoords[c-32][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**)&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 ); 
 
    // Restore the modified renderstates 
    m_pStateBlockSaved->Apply(); 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DrawText() 
// Desc: Draws 2D text. Note that sx and sy are in pixels 
//----------------------------------------------------------------------------- 
HRESULT CD3DFont::DrawText( FLOAT sx, FLOAT sy, DWORD dwColor, 
                            const TCHAR* strText, DWORD dwFlags ) 
{ 
    if( m_pd3dDevice == NULL ) 
        return E_FAIL; 
 
    // Setup renderstate 
    m_pStateBlockSaved->Capture(); 
    m_pStateBlockDrawText->Apply(); 
    m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX ); 
    m_pd3dDevice->SetPixelShader( NULL ); 
    m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) ); 
 
    // Set filter states 
    if( dwFlags & D3DFONT_FILTERED ) 
    { 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); 
    } 
 
    // Center the text block in the viewport 
    if( dwFlags & D3DFONT_CENTERED_X ) 
    { 
        D3DVIEWPORT9 vp; 
        m_pd3dDevice->GetViewport( &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; 
 
            FLOAT tx1 = m_fTexCoords[c-32][0]; 
            FLOAT tx2 = m_fTexCoords[c-32][2]; 
     
            FLOAT w = (tx2-tx1) *  m_dwTexWidth / m_fTextScale; 
     
            xFinal += w - (2 * m_dwSpacing); 
        } 
 
        sx = (vp.Width-xFinal)/2.0f; 
    } 
    if( dwFlags & D3DFONT_CENTERED_Y ) 
    { 
        D3DVIEWPORT9 vp; 
        m_pd3dDevice->GetViewport( &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**)&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; 
        } 
 
        if( (c-32) < 0 || (c-32) >= 128-32 ) 
            continue; 
 
        FLOAT tx1 = m_fTexCoords[c-32][0]; 
        FLOAT ty1 = m_fTexCoords[c-32][1]; 
        FLOAT tx2 = m_fTexCoords[c-32][2]; 
        FLOAT ty2 = m_fTexCoords[c-32][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**)&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 ); 
 
    // Restore the modified renderstates 
    m_pStateBlockSaved->Apply(); 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: Render3DText() 
// Desc: Renders 3D text 
//----------------------------------------------------------------------------- 
HRESULT CD3DFont::Render3DText( const TCHAR* strText, DWORD dwFlags ) 
{ 
    if( m_pd3dDevice == NULL ) 
        return E_FAIL; 
 
    // Setup renderstate 
    m_pStateBlockSaved->Capture(); 
    m_pStateBlockDrawText->Apply(); 
    m_pd3dDevice->SetFVF( D3DFVF_FONT3DVERTEX ); 
    m_pd3dDevice->SetPixelShader( NULL ); 
    m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT3DVERTEX) ); 
 
    // Set filter states 
    if( dwFlags & D3DFONT_FILTERED ) 
    { 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); 
        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); 
    } 
 
    // 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 & D3DFONT_CENTERED_X ) 
    { 
        SIZE sz; 
        GetTextExtent( strText, &sz ); 
        x = -(((FLOAT)sz.cx)/10.0f)/2.0f; 
    } 
    if( dwFlags & D3DFONT_CENTERED_Y ) 
    { 
        SIZE sz; 
        GetTextExtent( strText, &sz ); 
        y = -(((FLOAT)sz.cy)/10.0f)/2.0f; 
    } 
 
    // Turn off culling for two-sided text 
    if( dwFlags & 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**)&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; 
        } 
 
        if( (c-32) < 0 || (c-32) >= 128-32 ) 
            continue; 
 
        FLOAT tx1 = m_fTexCoords[c-32][0]; 
        FLOAT ty1 = m_fTexCoords[c-32][1]; 
        FLOAT tx2 = m_fTexCoords[c-32][2]; 
        FLOAT ty2 = m_fTexCoords[c-32][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**)&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 ); 
 
    // Restore the modified renderstates 
    m_pStateBlockSaved->Apply(); 
 
    return S_OK; 
}