home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the 3D Game Programming Gurus / gurus.iso / DirectX / dx9sdkcp.exe / SDK (C++) / Bin / DXUtils / Visual Studio 6.0 Wizards / Source Code / Template / d3dfont.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2002-11-12  |  25.9 KB  |  761 lines

  1. //-----------------------------------------------------------------------------
  2. // File: D3DFont.cpp
  3. //
  4. // Desc: Texture-based font class
  5. //-----------------------------------------------------------------------------
  6. #define STRICT
  7. $$IF(DLG)
  8. #include "stdafx.h"
  9. $$ENDIF
  10. #include <stdio.h>
  11. #include <tchar.h>
  12. #include <D3DX9.h>
  13. #include "D3DFont.h"
  14. #include "D3DUtil.h"
  15. #include "DXUtil.h"
  16.  
  17.  
  18.  
  19.  
  20. //-----------------------------------------------------------------------------
  21. // Custom vertex types for rendering text
  22. //-----------------------------------------------------------------------------
  23. #define MAX_NUM_VERTICES 50*6
  24.  
  25. struct FONT2DVERTEX { D3DXVECTOR4 p;   DWORD color;     FLOAT tu, tv; };
  26. struct FONT3DVERTEX { D3DXVECTOR3 p;   D3DXVECTOR3 n;   FLOAT tu, tv; };
  27.  
  28. #define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
  29. #define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)
  30.  
  31. inline FONT2DVERTEX InitFont2DVertex( const D3DXVECTOR4& p, D3DCOLOR color,
  32.                                       FLOAT tu, FLOAT tv )
  33. {
  34.     FONT2DVERTEX v;   v.p = p;   v.color = color;   v.tu = tu;   v.tv = tv;
  35.     return v;
  36. }
  37.  
  38. inline FONT3DVERTEX InitFont3DVertex( const D3DXVECTOR3& p, const D3DXVECTOR3& n,
  39.                                       FLOAT tu, FLOAT tv )
  40. {
  41.     FONT3DVERTEX v;   v.p = p;   v.n = n;   v.tu = tu;   v.tv = tv;
  42.     return v;
  43. }
  44.  
  45.  
  46.  
  47.  
  48. //-----------------------------------------------------------------------------
  49. // Name: CD3DFont()
  50. // Desc: Font class constructor
  51. //-----------------------------------------------------------------------------
  52. CD3DFont::CD3DFont( const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags )
  53. {
  54.     _tcsncpy( m_strFontName, strFontName, sizeof(m_strFontName) / sizeof(TCHAR) );
  55.     m_strFontName[sizeof(m_strFontName) / sizeof(TCHAR) - 1] = _T('\0');
  56.     m_dwFontHeight         = dwHeight;
  57.     m_dwFontFlags          = dwFlags;
  58.     m_dwSpacing            = 0;
  59.  
  60.     m_pd3dDevice           = NULL;
  61.     m_pTexture             = NULL;
  62.     m_pVB                  = NULL;
  63.  
  64.     m_pStateBlockSaved     = NULL;
  65.     m_pStateBlockDrawText  = NULL;
  66. }
  67.  
  68.  
  69.  
  70.  
  71. //-----------------------------------------------------------------------------
  72. // Name: ~CD3DFont()
  73. // Desc: Font class destructor
  74. //-----------------------------------------------------------------------------
  75. CD3DFont::~CD3DFont()
  76. {
  77.     InvalidateDeviceObjects();
  78.     DeleteDeviceObjects();
  79. }
  80.  
  81.  
  82.  
  83.  
  84. //-----------------------------------------------------------------------------
  85. // Name: InitDeviceObjects()
  86. // Desc: Initializes device-dependent objects, including the vertex buffer used
  87. //       for rendering text and the texture map which stores the font image.
  88. //-----------------------------------------------------------------------------
  89. HRESULT CD3DFont::InitDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice )
  90. {
  91.     HRESULT hr;
  92.  
  93.     // Keep a local copy of the device
  94.     m_pd3dDevice = pd3dDevice;
  95.  
  96.     // Establish the font and texture size
  97.     m_fTextScale  = 1.0f; // Draw fonts into texture without scaling
  98.  
  99.     // Large fonts need larger textures
  100.     if( m_dwFontHeight > 60 )
  101.         m_dwTexWidth = m_dwTexHeight = 2048;
  102.     else if( m_dwFontHeight > 30 )
  103.         m_dwTexWidth = m_dwTexHeight = 1024;
  104.     else if( m_dwFontHeight > 15 )
  105.         m_dwTexWidth = m_dwTexHeight = 512;
  106.     else
  107.         m_dwTexWidth  = m_dwTexHeight = 256;
  108.  
  109.     // If requested texture is too big, use a smaller texture and smaller font,
  110.     // and scale up when rendering.
  111.     D3DCAPS9 d3dCaps;
  112.     m_pd3dDevice->GetDeviceCaps( &d3dCaps );
  113.  
  114.     if( m_dwTexWidth > d3dCaps.MaxTextureWidth )
  115.     {
  116.         m_fTextScale = (FLOAT)d3dCaps.MaxTextureWidth / (FLOAT)m_dwTexWidth;
  117.         m_dwTexWidth = m_dwTexHeight = d3dCaps.MaxTextureWidth;
  118.     }
  119.  
  120.     // Create a new texture for the font
  121.     hr = m_pd3dDevice->CreateTexture( m_dwTexWidth, m_dwTexHeight, 1,
  122.                                       0, D3DFMT_A4R4G4B4,
  123.                                       D3DPOOL_MANAGED, &m_pTexture, NULL );
  124.     if( FAILED(hr) )
  125.         return hr;
  126.  
  127.     // Prepare to create a bitmap
  128.     DWORD*      pBitmapBits;
  129.     BITMAPINFO bmi;
  130.     ZeroMemory( &bmi.bmiHeader,  sizeof(BITMAPINFOHEADER) );
  131.     bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
  132.     bmi.bmiHeader.biWidth       =  (int)m_dwTexWidth;
  133.     bmi.bmiHeader.biHeight      = -(int)m_dwTexHeight;
  134.     bmi.bmiHeader.biPlanes      = 1;
  135.     bmi.bmiHeader.biCompression = BI_RGB;
  136.     bmi.bmiHeader.biBitCount    = 32;
  137.  
  138.     // Create a DC and a bitmap for the font
  139.     HDC     hDC       = CreateCompatibleDC( NULL );
  140.     HBITMAP hbmBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
  141.                                           (void**)&pBitmapBits, NULL, 0 );
  142.     SetMapMode( hDC, MM_TEXT );
  143.  
  144.     // Create a font.  By specifying ANTIALIASED_QUALITY, we might get an
  145.     // antialiased font, but this is not guaranteed.
  146.     INT nHeight    = -MulDiv( m_dwFontHeight, 
  147.         (INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72 );
  148.     DWORD dwBold   = (m_dwFontFlags&D3DFONT_BOLD)   ? FW_BOLD : FW_NORMAL;
  149.     DWORD dwItalic = (m_dwFontFlags&D3DFONT_ITALIC) ? TRUE    : FALSE;
  150.     HFONT hFont    = CreateFont( nHeight, 0, 0, 0, dwBold, dwItalic,
  151.                           FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
  152.                           CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
  153.                           VARIABLE_PITCH, m_strFontName );
  154.     if( NULL==hFont )
  155.         return E_FAIL;
  156.  
  157.     HGDIOBJ hbmOld = SelectObject( hDC, hbmBitmap );
  158.     HGDIOBJ hFontOld = SelectObject( hDC, hFont );
  159.  
  160.     // Set text properties
  161.     SetTextColor( hDC, RGB(255,255,255) );
  162.     SetBkColor(   hDC, 0x00000000 );
  163.     SetTextAlign( hDC, TA_TOP );
  164.  
  165.     // Loop through all printable character and output them to the bitmap..
  166.     // Meanwhile, keep track of the corresponding tex coords for each character.
  167.     DWORD x = 0;
  168.     DWORD y = 0;
  169.     TCHAR str[2] = _T("x");
  170.     SIZE size;
  171.  
  172.     // Calculate the spacing between characters based on line height
  173.     GetTextExtentPoint32( hDC, TEXT(" "), 1, &size );
  174.     x = m_dwSpacing = (DWORD) ceil(size.cy * 0.3f);
  175.  
  176.     for( TCHAR c=32; c<127; c++ )
  177.     {
  178.         str[0] = c;
  179.         GetTextExtentPoint32( hDC, str, 1, &size );
  180.  
  181.         if( (DWORD)(x + size.cx + m_dwSpacing) > m_dwTexWidth )
  182.         {
  183.             x  = m_dwSpacing;
  184.             y += size.cy+1;
  185.         }
  186.  
  187.         ExtTextOut( hDC, x+0, y+0, ETO_OPAQUE, NULL, str, 1, NULL );
  188.  
  189.         m_fTexCoords[c-32][0] = ((FLOAT)(x + 0       - m_dwSpacing))/m_dwTexWidth;
  190.         m_fTexCoords[c-32][1] = ((FLOAT)(y + 0       + 0          ))/m_dwTexHeight;
  191.         m_fTexCoords[c-32][2] = ((FLOAT)(x + size.cx + m_dwSpacing))/m_dwTexWidth;
  192.         m_fTexCoords[c-32][3] = ((FLOAT)(y + size.cy + 0          ))/m_dwTexHeight;
  193.  
  194.         x += size.cx + (2 * m_dwSpacing);
  195.     }
  196.  
  197.     // Lock the surface and write the alpha values for the set pixels
  198.     D3DLOCKED_RECT d3dlr;
  199.     m_pTexture->LockRect( 0, &d3dlr, 0, 0 );
  200.     BYTE* pDstRow = (BYTE*)d3dlr.pBits;
  201.     WORD* pDst16;
  202.     BYTE bAlpha; // 4-bit measure of pixel intensity
  203.  
  204.     for( y=0; y < m_dwTexHeight; y++ )
  205.     {
  206.         pDst16 = (WORD*)pDstRow;
  207.         for( x=0; x < m_dwTexWidth; x++ )
  208.         {
  209.             bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth*y + x] & 0xff) >> 4);
  210.             if (bAlpha > 0)
  211.             {
  212.                 *pDst16++ = (WORD) ((bAlpha << 12) | 0x0fff);
  213.             }
  214.             else
  215.             {
  216.                 *pDst16++ = 0x0000;
  217.             }
  218.         }
  219.         pDstRow += d3dlr.Pitch;
  220.     }
  221.  
  222.     // Done updating texture, so clean up used objects
  223.     m_pTexture->UnlockRect(0);
  224.     SelectObject( hDC, hbmOld );
  225.     SelectObject( hDC, hFontOld );
  226.     DeleteObject( hbmBitmap );
  227.     DeleteObject( hFont );
  228.     DeleteDC( hDC );
  229.  
  230.     return S_OK;
  231. }
  232.  
  233.  
  234.  
  235.  
  236. //-----------------------------------------------------------------------------
  237. // Name: RestoreDeviceObjects()
  238. // Desc:
  239. //-----------------------------------------------------------------------------
  240. HRESULT CD3DFont::RestoreDeviceObjects()
  241. {
  242.     HRESULT hr;
  243.  
  244.     // Create vertex buffer for the letters
  245.     int vertexSize = max( sizeof(FONT2DVERTEX), sizeof(FONT3DVERTEX ) );
  246.     if( FAILED( hr = m_pd3dDevice->CreateVertexBuffer( MAX_NUM_VERTICES * vertexSize,
  247.                                                        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
  248.                                                        D3DPOOL_DEFAULT, &m_pVB, NULL ) ) )
  249.     {
  250.         return hr;
  251.     }
  252.  
  253.     // Create the state blocks for rendering text
  254.     for( UINT which=0; which<2; which++ )
  255.     {
  256.         m_pd3dDevice->BeginStateBlock();
  257.         m_pd3dDevice->SetTexture( 0, m_pTexture );
  258.  
  259.         if ( D3DFONT_ZENABLE & m_dwFontFlags )
  260.             m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
  261.         else
  262.             m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
  263.  
  264.         m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
  265.         m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,   D3DBLEND_SRCALPHA );
  266.         m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND,  D3DBLEND_INVSRCALPHA );
  267.         m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,  TRUE );
  268.         m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF,         0x08 );
  269.         m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,  D3DCMP_GREATEREQUAL );
  270.         m_pd3dDevice->SetRenderState( D3DRS_FILLMODE,   D3DFILL_SOLID );
  271.         m_pd3dDevice->SetRenderState( D3DRS_CULLMODE,   D3DCULL_CCW );
  272.         m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,    FALSE );
  273.         m_pd3dDevice->SetRenderState( D3DRS_CLIPPING,         TRUE );
  274.         m_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE,  FALSE );
  275.         m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND,      D3DVBF_DISABLE );
  276.         m_pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
  277.         m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,        FALSE );
  278.         m_pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE,
  279.             D3DCOLORWRITEENABLE_RED  | D3DCOLORWRITEENABLE_GREEN |
  280.             D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );
  281.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  282.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  283.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  284.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
  285.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  286.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
  287.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
  288.         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
  289.         m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
  290.         m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
  291.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
  292.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
  293.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
  294.  
  295.         if( which==0 )
  296.             m_pd3dDevice->EndStateBlock( &m_pStateBlockSaved );
  297.         else
  298.             m_pd3dDevice->EndStateBlock( &m_pStateBlockDrawText );
  299.     }
  300.  
  301.     return S_OK;
  302. }
  303.  
  304.  
  305.  
  306.  
  307. //-----------------------------------------------------------------------------
  308. // Name: InvalidateDeviceObjects()
  309. // Desc: Destroys all device-dependent objects
  310. //-----------------------------------------------------------------------------
  311. HRESULT CD3DFont::InvalidateDeviceObjects()
  312. {
  313.     SAFE_RELEASE( m_pVB );
  314.     SAFE_RELEASE( m_pStateBlockSaved );
  315.     SAFE_RELEASE( m_pStateBlockDrawText );
  316.  
  317.     return S_OK;
  318. }
  319.  
  320.  
  321.  
  322.  
  323. //-----------------------------------------------------------------------------
  324. // Name: DeleteDeviceObjects()
  325. // Desc: Destroys all device-dependent objects
  326. //-----------------------------------------------------------------------------
  327. HRESULT CD3DFont::DeleteDeviceObjects()
  328. {
  329.     SAFE_RELEASE( m_pTexture );
  330.     m_pd3dDevice = NULL;
  331.  
  332.     return S_OK;
  333. }
  334.  
  335.  
  336.  
  337.  
  338. //-----------------------------------------------------------------------------
  339. // Name: GetTextExtent()
  340. // Desc: Get the dimensions of a text string
  341. //-----------------------------------------------------------------------------
  342. HRESULT CD3DFont::GetTextExtent( const TCHAR* strText, SIZE* pSize )
  343. {
  344.     if( NULL==strText || NULL==pSize )
  345.         return E_FAIL;
  346.  
  347.     FLOAT fRowWidth  = 0.0f;
  348.     FLOAT fRowHeight = (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
  349.     FLOAT fWidth     = 0.0f;
  350.     FLOAT fHeight    = fRowHeight;
  351.  
  352.     while( *strText )
  353.     {
  354.         TCHAR c = *strText++;
  355.  
  356.         if( c == _T('\n') )
  357.         {
  358.             fRowWidth = 0.0f;
  359.             fHeight  += fRowHeight;
  360.         }
  361.  
  362.         if( (c-32) < 0 || (c-32) >= 128-32 )
  363.             continue;
  364.  
  365.         FLOAT tx1 = m_fTexCoords[c-32][0];
  366.         FLOAT tx2 = m_fTexCoords[c-32][2];
  367.  
  368.         fRowWidth += (tx2-tx1)*m_dwTexWidth - 2*m_dwSpacing;
  369.  
  370.         if( fRowWidth > fWidth )
  371.             fWidth = fRowWidth;
  372.     }
  373.  
  374.     pSize->cx = (int)fWidth;
  375.     pSize->cy = (int)fHeight;
  376.  
  377.     return S_OK;
  378. }
  379.  
  380.  
  381.  
  382.  
  383. //-----------------------------------------------------------------------------
  384. // Name: DrawTextScaled()
  385. // Desc: Draws scaled 2D text.  Note that x and y are in viewport coordinates
  386. //       (ranging from -1 to +1).  fXScale and fYScale are the size fraction 
  387. //       relative to the entire viewport.  For example, a fXScale of 0.25 is
  388. //       1/8th of the screen width.  This allows you to output text at a fixed
  389. //       fraction of the viewport, even if the screen or window size changes.
  390. //-----------------------------------------------------------------------------
  391. HRESULT CD3DFont::DrawTextScaled( FLOAT x, FLOAT y, FLOAT z,
  392.                                   FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
  393.                                   const TCHAR* strText, DWORD dwFlags )
  394. {
  395.     if( m_pd3dDevice == NULL )
  396.         return E_FAIL;
  397.  
  398.     // Set up renderstate
  399.     m_pStateBlockSaved->Capture();
  400.     m_pStateBlockDrawText->Apply();
  401.     m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX );
  402.     m_pd3dDevice->SetPixelShader( NULL );
  403.     m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) );
  404.  
  405.     // Set filter states
  406.     if( dwFlags & D3DFONT_FILTERED )
  407.     {
  408.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
  409.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
  410.     }
  411.  
  412.     D3DVIEWPORT9 vp;
  413.     m_pd3dDevice->GetViewport( &vp );
  414.     FLOAT fLineHeight = ( m_fTexCoords[0][3] - m_fTexCoords[0][1] ) * m_dwTexHeight;
  415.  
  416.     // Center the text block in the viewport
  417.     if( dwFlags & D3DFONT_CENTERED_X )
  418.     {
  419.         const TCHAR* strTextTmp = strText;
  420.         float xFinal = 0.0f;
  421.  
  422.         while( *strTextTmp )
  423.         {
  424.             TCHAR c = *strTextTmp++;
  425.     
  426.             if( c == _T('\n') )
  427.                 break;  // Isn't supported.  
  428.             if( (c-32) < 0 || (c-32) >= 128-32 )
  429.                 continue;
  430.  
  431.             FLOAT tx1 = m_fTexCoords[c-32][0];
  432.             FLOAT tx2 = m_fTexCoords[c-32][2];
  433.  
  434.             FLOAT w = (tx2-tx1)*m_dwTexWidth;
  435.  
  436.             w *= (fXScale*vp.Height)/fLineHeight;
  437.  
  438.             xFinal += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight;
  439.         }
  440.  
  441.         x = -xFinal/vp.Width;
  442.     }
  443.     if( dwFlags & D3DFONT_CENTERED_Y )
  444.     {
  445.         y = -fLineHeight/vp.Height;
  446.     }
  447.  
  448.     FLOAT sx  = (x+1.0f)*vp.Width/2;
  449.     FLOAT sy  = (y+1.0f)*vp.Height/2;
  450.     FLOAT sz  = z;
  451.     FLOAT rhw = 1.0f;
  452.  
  453.     // Adjust for character spacing
  454.     sx -= m_dwSpacing * (fXScale*vp.Height)/fLineHeight;
  455.     FLOAT fStartX = sx;
  456.  
  457.     // Fill vertex buffer
  458.     FONT2DVERTEX* pVertices;
  459.     DWORD         dwNumTriangles = 0L;
  460.     m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
  461.  
  462.     while( *strText )
  463.     {
  464.         TCHAR c = *strText++;
  465.  
  466.         if( c == _T('\n') )
  467.         {
  468.             sx  = fStartX;
  469.             sy += fYScale*vp.Height;
  470.         }
  471.  
  472.         if( (c-32) < 0 || (c-32) >= 128-32 )
  473.             continue;
  474.  
  475.         FLOAT tx1 = m_fTexCoords[c-32][0];
  476.         FLOAT ty1 = m_fTexCoords[c-32][1];
  477.         FLOAT tx2 = m_fTexCoords[c-32][2];
  478.         FLOAT ty2 = m_fTexCoords[c-32][3];
  479.  
  480.         FLOAT w = (tx2-tx1)*m_dwTexWidth;
  481.         FLOAT h = (ty2-ty1)*m_dwTexHeight;
  482.  
  483.         w *= (fXScale*vp.Height)/fLineHeight;
  484.         h *= (fYScale*vp.Height)/fLineHeight;
  485.  
  486.         if( c != _T(' ') )
  487.         {
  488.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx1, ty2 );
  489.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
  490.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
  491.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx2, ty1 );
  492.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
  493.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
  494.             dwNumTriangles += 2;
  495.  
  496.             if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
  497.             {
  498.                 // Unlock, render, and relock the vertex buffer
  499.                 m_pVB->Unlock();
  500.                 m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
  501.                 m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
  502.                 dwNumTriangles = 0L;
  503.             }
  504.         }
  505.  
  506.         sx += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight;
  507.     }
  508.  
  509.     // Unlock and render the vertex buffer
  510.     m_pVB->Unlock();
  511.     if( dwNumTriangles > 0 )
  512.         m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
  513.  
  514.     // Restore the modified renderstates
  515.     m_pStateBlockSaved->Apply();
  516.  
  517.     return S_OK;
  518. }
  519.  
  520.  
  521.  
  522.  
  523. //-----------------------------------------------------------------------------
  524. // Name: DrawText()
  525. // Desc: Draws 2D text. Note that sx and sy are in pixels
  526. //-----------------------------------------------------------------------------
  527. HRESULT CD3DFont::DrawText( FLOAT sx, FLOAT sy, DWORD dwColor,
  528.                             const TCHAR* strText, DWORD dwFlags )
  529. {
  530.     if( m_pd3dDevice == NULL )
  531.         return E_FAIL;
  532.  
  533.     // Setup renderstate
  534.     m_pStateBlockSaved->Capture();
  535.     m_pStateBlockDrawText->Apply();
  536.     m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX );
  537.     m_pd3dDevice->SetPixelShader( NULL );
  538.     m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) );
  539.  
  540.     // Set filter states
  541.     if( dwFlags & D3DFONT_FILTERED )
  542.     {
  543.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
  544.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
  545.     }
  546.  
  547.     // Center the text block in the viewport
  548.     if( dwFlags & D3DFONT_CENTERED_X )
  549.     {
  550.         D3DVIEWPORT9 vp;
  551.         m_pd3dDevice->GetViewport( &vp );
  552.         const TCHAR* strTextTmp = strText;
  553.         float xFinal = 0.0f;
  554.  
  555.         while( *strTextTmp )
  556.         {
  557.             TCHAR c = *strTextTmp++;
  558.     
  559.             if( c == _T('\n') )
  560.                 break;  // Isn't supported.  
  561.             if( (c-32) < 0 || (c-32) >= 128-32 )
  562.                 continue;
  563.  
  564.             FLOAT tx1 = m_fTexCoords[c-32][0];
  565.             FLOAT tx2 = m_fTexCoords[c-32][2];
  566.     
  567.             FLOAT w = (tx2-tx1) *  m_dwTexWidth / m_fTextScale;
  568.     
  569.             xFinal += w - (2 * m_dwSpacing);
  570.         }
  571.  
  572.         sx = (vp.Width-xFinal)/2.0f;
  573.     }
  574.     if( dwFlags & D3DFONT_CENTERED_Y )
  575.     {
  576.         D3DVIEWPORT9 vp;
  577.         m_pd3dDevice->GetViewport( &vp );
  578.         float fLineHeight = ((m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight);
  579.         sy = (vp.Height-fLineHeight)/2;
  580.     }
  581.  
  582.     // Adjust for character spacing
  583.     sx -= m_dwSpacing;
  584.     FLOAT fStartX = sx;
  585.  
  586.     // Fill vertex buffer
  587.     FONT2DVERTEX* pVertices = NULL;
  588.     DWORD         dwNumTriangles = 0;
  589.     m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
  590.  
  591.     while( *strText )
  592.     {
  593.         TCHAR c = *strText++;
  594.  
  595.         if( c == _T('\n') )
  596.         {
  597.             sx = fStartX;
  598.             sy += (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
  599.         }
  600.  
  601.         if( (c-32) < 0 || (c-32) >= 128-32 )
  602.             continue;
  603.  
  604.         FLOAT tx1 = m_fTexCoords[c-32][0];
  605.         FLOAT ty1 = m_fTexCoords[c-32][1];
  606.         FLOAT tx2 = m_fTexCoords[c-32][2];
  607.         FLOAT ty2 = m_fTexCoords[c-32][3];
  608.  
  609.         FLOAT w = (tx2-tx1) *  m_dwTexWidth / m_fTextScale;
  610.         FLOAT h = (ty2-ty1) * m_dwTexHeight / m_fTextScale;
  611.  
  612.         if( c != _T(' ') )
  613.         {
  614.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx1, ty2 );
  615.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
  616.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
  617.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx2, ty1 );
  618.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
  619.             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
  620.             dwNumTriangles += 2;
  621.  
  622.             if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
  623.             {
  624.                 // Unlock, render, and relock the vertex buffer
  625.                 m_pVB->Unlock();
  626.                 m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
  627.                 pVertices = NULL;
  628.                 m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
  629.                 dwNumTriangles = 0L;
  630.             }
  631.         }
  632.  
  633.         sx += w - (2 * m_dwSpacing);
  634.     }
  635.  
  636.     // Unlock and render the vertex buffer
  637.     m_pVB->Unlock();
  638.     if( dwNumTriangles > 0 )
  639.         m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
  640.  
  641.     // Restore the modified renderstates
  642.     m_pStateBlockSaved->Apply();
  643.  
  644.     return S_OK;
  645. }
  646.  
  647.  
  648.  
  649.  
  650. //-----------------------------------------------------------------------------
  651. // Name: Render3DText()
  652. // Desc: Renders 3D text
  653. //-----------------------------------------------------------------------------
  654. HRESULT CD3DFont::Render3DText( const TCHAR* strText, DWORD dwFlags )
  655. {
  656.     if( m_pd3dDevice == NULL )
  657.         return E_FAIL;
  658.  
  659.     // Setup renderstate
  660.     m_pStateBlockSaved->Capture();
  661.     m_pStateBlockDrawText->Apply();
  662.     m_pd3dDevice->SetFVF( D3DFVF_FONT3DVERTEX );
  663.     m_pd3dDevice->SetPixelShader( NULL );
  664.     m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT3DVERTEX) );
  665.  
  666.     // Set filter states
  667.     if( dwFlags & D3DFONT_FILTERED )
  668.     {
  669.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
  670.         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
  671.     }
  672.  
  673.     // Position for each text element
  674.     FLOAT x = 0.0f;
  675.     FLOAT y = 0.0f;
  676.  
  677.     // Center the text block at the origin (not the viewport)
  678.     if( dwFlags & D3DFONT_CENTERED_X )
  679.     {
  680.         SIZE sz;
  681.         GetTextExtent( strText, &sz );
  682.         x = -(((FLOAT)sz.cx)/10.0f)/2.0f;
  683.     }
  684.     if( dwFlags & D3DFONT_CENTERED_Y )
  685.     {
  686.         SIZE sz;
  687.         GetTextExtent( strText, &sz );
  688.         y = -(((FLOAT)sz.cy)/10.0f)/2.0f;
  689.     }
  690.  
  691.     // Turn off culling for two-sided text
  692.     if( dwFlags & D3DFONT_TWOSIDED )
  693.         m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
  694.  
  695.     // Adjust for character spacing
  696.     x -= m_dwSpacing / 10.0f;
  697.     FLOAT fStartX = x;
  698.     TCHAR c;
  699.  
  700.     // Fill vertex buffer
  701.     FONT3DVERTEX* pVertices;
  702.     DWORD         dwNumTriangles = 0L;
  703.     m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
  704.  
  705.     while( (c = *strText++) != 0 )
  706.     {
  707.         if( c == '\n' )
  708.         {
  709.             x = fStartX;
  710.             y -= (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight/10.0f;
  711.         }
  712.  
  713.         if( (c-32) < 0 || (c-32) >= 128-32 )
  714.             continue;
  715.  
  716.         FLOAT tx1 = m_fTexCoords[c-32][0];
  717.         FLOAT ty1 = m_fTexCoords[c-32][1];
  718.         FLOAT tx2 = m_fTexCoords[c-32][2];
  719.         FLOAT ty2 = m_fTexCoords[c-32][3];
  720.  
  721.         FLOAT w = (tx2-tx1) * m_dwTexWidth  / ( 10.0f * m_fTextScale );
  722.         FLOAT h = (ty2-ty1) * m_dwTexHeight / ( 10.0f * m_fTextScale );
  723.  
  724.         if( c != _T(' ') )
  725.         {
  726.             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+0,0), D3DXVECTOR3(0,0,-1), tx1, ty2 );
  727.             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
  728.             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
  729.             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+h,0), D3DXVECTOR3(0,0,-1), tx2, ty1 );
  730.             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
  731.             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
  732.             dwNumTriangles += 2;
  733.  
  734.             if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
  735.             {
  736.                 // Unlock, render, and relock the vertex buffer
  737.                 m_pVB->Unlock();
  738.                 m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
  739.                 m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
  740.                 dwNumTriangles = 0L;
  741.             }
  742.         }
  743.  
  744.         x += w - (2 * m_dwSpacing) / 10.0f;
  745.     }
  746.  
  747.     // Unlock and render the vertex buffer
  748.     m_pVB->Unlock();
  749.     if( dwNumTriangles > 0 )
  750.         m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
  751.  
  752.     // Restore the modified renderstates
  753.     m_pStateBlockSaved->Apply();
  754.  
  755.     return S_OK;
  756. }
  757.  
  758.  
  759.  
  760.  
  761.