0001: //-----------------------------------------------------------------------------
0002: // File: D3DFont.cpp
0003: //
0004: // Desc: Texture-based font class
0005: //-----------------------------------------------------------------------------
0006: #define STRICT
0007: #include <stdio.h>
0008: #include <tchar.h>
0009: #include <D3DX9.h>
0010: #include "D3DFont.h"
0011: #include "D3DUtil.h"
0012: #include "DXUtil.h"
0013: 
0014: 
0015: 
0016: 
0017: //-----------------------------------------------------------------------------
0018: // Custom vertex types for rendering text
0019: //-----------------------------------------------------------------------------
0020: #define MAX_NUM_VERTICES 50*6
0021: 
0022: struct FONT2DVERTEX { D3DXVECTOR4 p;   DWORD color;     FLOAT tu, tv; };
0023: struct FONT3DVERTEX { D3DXVECTOR3 p;   D3DXVECTOR3 n;   FLOAT tu, tv; };
0024: 
0025: #define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
0026: #define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)
0027: 
0028: inline FONT2DVERTEX InitFont2DVertex( const D3DXVECTOR4& p, D3DCOLOR color,
0029:                                       FLOAT tu, FLOAT tv )
0030: {
0031:     FONT2DVERTEX v;   v.p = p;   v.color = color;   v.tu = tu;   v.tv = tv;
0032:     return v;
0033: }
0034: 
0035: inline FONT3DVERTEX InitFont3DVertex( const D3DXVECTOR3& p, const D3DXVECTOR3& n,
0036:                                       FLOAT tu, FLOAT tv )
0037: {
0038:     FONT3DVERTEX v;   v.p = p;   v.n = n;   v.tu = tu;   v.tv = tv;
0039:     return v;
0040: }
0041: 
0042: 
0043: 
0044: 
0045: //-----------------------------------------------------------------------------
0046: // Name: CD3DFont()
0047: // Desc: Font class constructor
0048: //-----------------------------------------------------------------------------
0049: CD3DFont::CD3DFont( const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags )
0050: {
0051:     _tcsncpy( m_strFontName, strFontName, sizeof(m_strFontName) / sizeof(TCHAR) );
0052:     m_strFontName[sizeof(m_strFontName) / sizeof(TCHAR) - 1] = _T('\0');
0053:     m_dwFontHeight         = dwHeight;
0054:     m_dwFontFlags          = dwFlags;
0055:     m_dwSpacing            = 0;
0056: 
0057:     m_pd3dDevice           = NULL;
0058:     m_pTexture             = NULL;
0059:     m_pVB                  = NULL;
0060: 
0061:     m_pStateBlockSaved     = NULL;
0062:     m_pStateBlockDrawText  = NULL;
0063: }
0064: 
0065: 
0066: 
0067: 
0068: //-----------------------------------------------------------------------------
0069: // Name: ~CD3DFont()
0070: // Desc: Font class destructor
0071: //-----------------------------------------------------------------------------
0072: CD3DFont::~CD3DFont()
0073: {
0074:     InvalidateDeviceObjects();
0075:     DeleteDeviceObjects();
0076: }
0077: 
0078: 
0079: 
0080: 
0081: //-----------------------------------------------------------------------------
0082: // Name: InitDeviceObjects()
0083: // Desc: Initializes device-dependent objects, including the vertex buffer used
0084: //       for rendering text and the texture map which stores the font image.
0085: //-----------------------------------------------------------------------------
0086: HRESULT CD3DFont::InitDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice )
0087: {
0088:     HRESULT hr;
0089: 
0090:     // Keep a local copy of the device
0091:     m_pd3dDevice = pd3dDevice;
0092: 
0093:     // Establish the font and texture size
0094:     m_fTextScale  = 1.0f; // Draw fonts into texture without scaling
0095: 
0096:     // Large fonts need larger textures
0097:     if( m_dwFontHeight > 60 )
0098:         m_dwTexWidth = m_dwTexHeight = 2048;
0099:     else if( m_dwFontHeight > 30 )
0100:         m_dwTexWidth = m_dwTexHeight = 1024;
0101:     else if( m_dwFontHeight > 15 )
0102:         m_dwTexWidth = m_dwTexHeight = 512;
0103:     else
0104:         m_dwTexWidth  = m_dwTexHeight = 256;
0105: 
0106:     // If requested texture is too big, use a smaller texture and smaller font,
0107:     // and scale up when rendering.
0108:     D3DCAPS9 d3dCaps;
0109:     m_pd3dDevice->GetDeviceCaps( &d3dCaps );
0110: 
0111:     if( m_dwTexWidth > d3dCaps.MaxTextureWidth )
0112:     {
0113:         m_fTextScale = (FLOAT)d3dCaps.MaxTextureWidth / (FLOAT)m_dwTexWidth;
0114:         m_dwTexWidth = m_dwTexHeight = d3dCaps.MaxTextureWidth;
0115:     }
0116: 
0117:     // Create a new texture for the font
0118:     hr = m_pd3dDevice->CreateTexture( m_dwTexWidth, m_dwTexHeight, 1,
0119:                                       0, D3DFMT_A4R4G4B4,
0120:                                       D3DPOOL_MANAGED, &m_pTexture, NULL );
0121:     if( FAILED(hr) )
0122:         return hr;
0123: 
0124:     // Prepare to create a bitmap
0125:     DWORD*      pBitmapBits;
0126:     BITMAPINFO bmi;
0127:     ZeroMemory( &bmi.bmiHeader,  sizeof(BITMAPINFOHEADER) );
0128:     bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
0129:     bmi.bmiHeader.biWidth       =  (int)m_dwTexWidth;
0130:     bmi.bmiHeader.biHeight      = -(int)m_dwTexHeight;
0131:     bmi.bmiHeader.biPlanes      = 1;
0132:     bmi.bmiHeader.biCompression = BI_RGB;
0133:     bmi.bmiHeader.biBitCount    = 32;
0134: 
0135:     // Create a DC and a bitmap for the font
0136:     HDC     hDC       = CreateCompatibleDC( NULL );
0137:     HBITMAP hbmBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
0138:                                           (void**)&pBitmapBits, NULL, 0 );
0139:     SetMapMode( hDC, MM_TEXT );
0140: 
0141:     // Create a font.  By specifying ANTIALIASED_QUALITY, we might get an
0142:     // antialiased font, but this is not guaranteed.
0143:     INT nHeight    = -MulDiv( m_dwFontHeight, 
0144:         (INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72 );
0145:     DWORD dwBold   = (m_dwFontFlags&D3DFONT_BOLD)   ? FW_BOLD : FW_NORMAL;
0146:     DWORD dwItalic = (m_dwFontFlags&D3DFONT_ITALIC) ? TRUE    : FALSE;
0147:     HFONT hFont    = CreateFont( nHeight, 0, 0, 0, dwBold, dwItalic,
0148:                           FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
0149:                           CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
0150:                           VARIABLE_PITCH, m_strFontName );
0151:     if( NULL==hFont )
0152:         return E_FAIL;
0153: 
0154:     HGDIOBJ hbmOld = SelectObject( hDC, hbmBitmap );
0155:     HGDIOBJ hFontOld = SelectObject( hDC, hFont );
0156: 
0157:     // Set text properties
0158:     SetTextColor( hDC, RGB(255,255,255) );
0159:     SetBkColor(   hDC, 0x00000000 );
0160:     SetTextAlign( hDC, TA_TOP );
0161: 
0162:     // Loop through all printable character and output them to the bitmap..
0163:     // Meanwhile, keep track of the corresponding tex coords for each character.
0164:     DWORD x = 0;
0165:     DWORD y = 0;
0166:     TCHAR str[2] = _T("x");
0167:     SIZE size;
0168: 
0169:     // Calculate the spacing between characters based on line height
0170:     GetTextExtentPoint32( hDC, TEXT(" "), 1, &size );
0171:     x = m_dwSpacing = (DWORD) ceil(size.cy * 0.3f);
0172: 
0173:     for( TCHAR c=32; c<127; c++ )
0174:     {
0175:         str[0] = c;
0176:         GetTextExtentPoint32( hDC, str, 1, &size );
0177: 
0178:         if( (DWORD)(x + size.cx + m_dwSpacing) > m_dwTexWidth )
0179:         {
0180:             x  = m_dwSpacing;
0181:             y += size.cy+1;
0182:         }
0183: 
0184:         ExtTextOut( hDC, x+0, y+0, ETO_OPAQUE, NULL, str, 1, NULL );
0185: 
0186:         m_fTexCoords[c-32][0] = ((FLOAT)(x + 0       - m_dwSpacing))/m_dwTexWidth;
0187:         m_fTexCoords[c-32][1] = ((FLOAT)(y + 0       + 0          ))/m_dwTexHeight;
0188:         m_fTexCoords[c-32][2] = ((FLOAT)(x + size.cx + m_dwSpacing))/m_dwTexWidth;
0189:         m_fTexCoords[c-32][3] = ((FLOAT)(y + size.cy + 0          ))/m_dwTexHeight;
0190: 
0191:         x += size.cx + (2 * m_dwSpacing);
0192:     }
0193: 
0194:     // Lock the surface and write the alpha values for the set pixels
0195:     D3DLOCKED_RECT d3dlr;
0196:     m_pTexture->LockRect( 0, &d3dlr, 0, 0 );
0197:     BYTE* pDstRow = (BYTE*)d3dlr.pBits;
0198:     WORD* pDst16;
0199:     BYTE bAlpha; // 4-bit measure of pixel intensity
0200: 
0201:     for( y=0; y < m_dwTexHeight; y++ )
0202:     {
0203:         pDst16 = (WORD*)pDstRow;
0204:         for( x=0; x < m_dwTexWidth; x++ )
0205:         {
0206:             bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth*y + x] & 0xff) >> 4);
0207:             if (bAlpha > 0)
0208:             {
0209:                 *pDst16++ = (WORD) ((bAlpha << 12) | 0x0fff);
0210:             }
0211:             else
0212:             {
0213:                 *pDst16++ = 0x0000;
0214:             }
0215:         }
0216:         pDstRow += d3dlr.Pitch;
0217:     }
0218: 
0219:     // Done updating texture, so clean up used objects
0220:     m_pTexture->UnlockRect(0);
0221:     SelectObject( hDC, hbmOld );
0222:     SelectObject( hDC, hFontOld );
0223:     DeleteObject( hbmBitmap );
0224:     DeleteObject( hFont );
0225:     DeleteDC( hDC );
0226: 
0227:     return S_OK;
0228: }
0229: 
0230: 
0231: 
0232: 
0233: //-----------------------------------------------------------------------------
0234: // Name: RestoreDeviceObjects()
0235: // Desc:
0236: //-----------------------------------------------------------------------------
0237: HRESULT CD3DFont::RestoreDeviceObjects()
0238: {
0239:     HRESULT hr;
0240: 
0241:     // Create vertex buffer for the letters
0242:     int vertexSize = max( sizeof(FONT2DVERTEX), sizeof(FONT3DVERTEX ) );
0243:     if( FAILED( hr = m_pd3dDevice->CreateVertexBuffer( MAX_NUM_VERTICES * vertexSize,
0244:                                                        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
0245:                                                        D3DPOOL_DEFAULT, &m_pVB, NULL ) ) )
0246:     {
0247:         return hr;
0248:     }
0249: 
0250:     // Create the state blocks for rendering text
0251:     for( UINT which=0; which<2; which++ )
0252:     {
0253:         m_pd3dDevice->BeginStateBlock();
0254:         m_pd3dDevice->SetTexture( 0, m_pTexture );
0255: 
0256:         if ( D3DFONT_ZENABLE & m_dwFontFlags )
0257:             m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
0258:         else
0259:             m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
0260: 
0261:         m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
0262:         m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,   D3DBLEND_SRCALPHA );
0263:         m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND,  D3DBLEND_INVSRCALPHA );
0264:         m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,  TRUE );
0265:         m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF,         0x08 );
0266:         m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,  D3DCMP_GREATEREQUAL );
0267:         m_pd3dDevice->SetRenderState( D3DRS_FILLMODE,   D3DFILL_SOLID );
0268:         m_pd3dDevice->SetRenderState( D3DRS_CULLMODE,   D3DCULL_CCW );
0269:         m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,    FALSE );
0270:         m_pd3dDevice->SetRenderState( D3DRS_CLIPPING,         TRUE );
0271:         m_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE,  FALSE );
0272:         m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND,      D3DVBF_DISABLE );
0273:         m_pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
0274:         m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,        FALSE );
0275:         m_pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE,
0276:             D3DCOLORWRITEENABLE_RED  | D3DCOLORWRITEENABLE_GREEN |
0277:             D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );
0278:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
0279:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
0280:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
0281:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
0282:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
0283:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
0284:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
0285:         m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
0286:         m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
0287:         m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
0288:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
0289:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
0290:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
0291: 
0292:         if( which==0 )
0293:             m_pd3dDevice->EndStateBlock( &m_pStateBlockSaved );
0294:         else
0295:             m_pd3dDevice->EndStateBlock( &m_pStateBlockDrawText );
0296:     }
0297: 
0298:     return S_OK;
0299: }
0300: 
0301: 
0302: 
0303: 
0304: //-----------------------------------------------------------------------------
0305: // Name: InvalidateDeviceObjects()
0306: // Desc: Destroys all device-dependent objects
0307: //-----------------------------------------------------------------------------
0308: HRESULT CD3DFont::InvalidateDeviceObjects()
0309: {
0310:     SAFE_RELEASE( m_pVB );
0311:     SAFE_RELEASE( m_pStateBlockSaved );
0312:     SAFE_RELEASE( m_pStateBlockDrawText );
0313: 
0314:     return S_OK;
0315: }
0316: 
0317: 
0318: 
0319: 
0320: //-----------------------------------------------------------------------------
0321: // Name: DeleteDeviceObjects()
0322: // Desc: Destroys all device-dependent objects
0323: //-----------------------------------------------------------------------------
0324: HRESULT CD3DFont::DeleteDeviceObjects()
0325: {
0326:     SAFE_RELEASE( m_pTexture );
0327:     m_pd3dDevice = NULL;
0328: 
0329:     return S_OK;
0330: }
0331: 
0332: 
0333: 
0334: 
0335: //-----------------------------------------------------------------------------
0336: // Name: GetTextExtent()
0337: // Desc: Get the dimensions of a text string
0338: //-----------------------------------------------------------------------------
0339: HRESULT CD3DFont::GetTextExtent( const TCHAR* strText, SIZE* pSize )
0340: {
0341:     if( NULL==strText || NULL==pSize )
0342:         return E_FAIL;
0343: 
0344:     FLOAT fRowWidth  = 0.0f;
0345:     FLOAT fRowHeight = (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
0346:     FLOAT fWidth     = 0.0f;
0347:     FLOAT fHeight    = fRowHeight;
0348: 
0349:     while( *strText )
0350:     {
0351:         TCHAR c = *strText++;
0352: 
0353:         if( c == _T('\n') )
0354:         {
0355:             fRowWidth = 0.0f;
0356:             fHeight  += fRowHeight;
0357:         }
0358: 
0359:         if( (c-32) < 0 || (c-32) >= 128-32 )
0360:             continue;
0361: 
0362:         FLOAT tx1 = m_fTexCoords[c-32][0];
0363:         FLOAT tx2 = m_fTexCoords[c-32][2];
0364: 
0365:         fRowWidth += (tx2-tx1)*m_dwTexWidth - 2*m_dwSpacing;
0366: 
0367:         if( fRowWidth > fWidth )
0368:             fWidth = fRowWidth;
0369:     }
0370: 
0371:     pSize->cx = (int)fWidth;
0372:     pSize->cy = (int)fHeight;
0373: 
0374:     return S_OK;
0375: }
0376: 
0377: 
0378: 
0379: 
0380: //-----------------------------------------------------------------------------
0381: // Name: DrawTextScaled()
0382: // Desc: Draws scaled 2D text.  Note that x and y are in viewport coordinates
0383: //       (ranging from -1 to +1).  fXScale and fYScale are the size fraction 
0384: //       relative to the entire viewport.  For example, a fXScale of 0.25 is
0385: //       1/8th of the screen width.  This allows you to output text at a fixed
0386: //       fraction of the viewport, even if the screen or window size changes.
0387: //-----------------------------------------------------------------------------
0388: HRESULT CD3DFont::DrawTextScaled( FLOAT x, FLOAT y, FLOAT z,
0389:                                   FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
0390:                                   const TCHAR* strText, DWORD dwFlags )
0391: {
0392:     if( m_pd3dDevice == NULL )
0393:         return E_FAIL;
0394: 
0395:     // Set up renderstate
0396:     m_pStateBlockSaved->Capture();
0397:     m_pStateBlockDrawText->Apply();
0398:     m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX );
0399:     m_pd3dDevice->SetPixelShader( NULL );
0400:     m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) );
0401: 
0402:     // Set filter states
0403:     if( dwFlags & D3DFONT_FILTERED )
0404:     {
0405:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
0406:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
0407:     }
0408: 
0409:     D3DVIEWPORT9 vp;
0410:     m_pd3dDevice->GetViewport( &vp );
0411:     FLOAT fLineHeight = ( m_fTexCoords[0][3] - m_fTexCoords[0][1] ) * m_dwTexHeight;
0412: 
0413:     // Center the text block in the viewport
0414:     if( dwFlags & D3DFONT_CENTERED_X )
0415:     {
0416:         const TCHAR* strTextTmp = strText;
0417:         float xFinal = 0.0f;
0418: 
0419:         while( *strTextTmp )
0420:         {
0421:             TCHAR c = *strTextTmp++;
0422:     
0423:             if( c == _T('\n') )
0424:                 break;  // Isn't supported.  
0425:             if( (c-32) < 0 || (c-32) >= 128-32 )
0426:                 continue;
0427: 
0428:             FLOAT tx1 = m_fTexCoords[c-32][0];
0429:             FLOAT tx2 = m_fTexCoords[c-32][2];
0430: 
0431:             FLOAT w = (tx2-tx1)*m_dwTexWidth;
0432: 
0433:             w *= (fXScale*vp.Height)/fLineHeight;
0434: 
0435:             xFinal += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight;
0436:         }
0437: 
0438:         x = -xFinal/vp.Width;
0439:     }
0440:     if( dwFlags & D3DFONT_CENTERED_Y )
0441:     {
0442:         y = -fLineHeight/vp.Height;
0443:     }
0444: 
0445:     FLOAT sx  = (x+1.0f)*vp.Width/2;
0446:     FLOAT sy  = (y+1.0f)*vp.Height/2;
0447:     FLOAT sz  = z;
0448:     FLOAT rhw = 1.0f;
0449: 
0450:     // Adjust for character spacing
0451:     sx -= m_dwSpacing * (fXScale*vp.Height)/fLineHeight;
0452:     FLOAT fStartX = sx;
0453: 
0454:     // Fill vertex buffer
0455:     FONT2DVERTEX* pVertices;
0456:     DWORD         dwNumTriangles = 0L;
0457:     m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
0458: 
0459:     while( *strText )
0460:     {
0461:         TCHAR c = *strText++;
0462: 
0463:         if( c == _T('\n') )
0464:         {
0465:             sx  = fStartX;
0466:             sy += fYScale*vp.Height;
0467:         }
0468: 
0469:         if( (c-32) < 0 || (c-32) >= 128-32 )
0470:             continue;
0471: 
0472:         FLOAT tx1 = m_fTexCoords[c-32][0];
0473:         FLOAT ty1 = m_fTexCoords[c-32][1];
0474:         FLOAT tx2 = m_fTexCoords[c-32][2];
0475:         FLOAT ty2 = m_fTexCoords[c-32][3];
0476: 
0477:         FLOAT w = (tx2-tx1)*m_dwTexWidth;
0478:         FLOAT h = (ty2-ty1)*m_dwTexHeight;
0479: 
0480:         w *= (fXScale*vp.Height)/fLineHeight;
0481:         h *= (fYScale*vp.Height)/fLineHeight;
0482: 
0483:         if( c != _T(' ') )
0484:         {
0485:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx1, ty2 );
0486:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
0487:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
0488:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx2, ty1 );
0489:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
0490:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
0491:             dwNumTriangles += 2;
0492: 
0493:             if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
0494:             {
0495:                 // Unlock, render, and relock the vertex buffer
0496:                 m_pVB->Unlock();
0497:                 m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
0498:                 m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
0499:                 dwNumTriangles = 0L;
0500:             }
0501:         }
0502: 
0503:         sx += w - (2 * m_dwSpacing) * (fXScale*vp.Height)/fLineHeight;
0504:     }
0505: 
0506:     // Unlock and render the vertex buffer
0507:     m_pVB->Unlock();
0508:     if( dwNumTriangles > 0 )
0509:         m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
0510: 
0511:     // Restore the modified renderstates
0512:     m_pStateBlockSaved->Apply();
0513: 
0514:     return S_OK;
0515: }
0516: 
0517: 
0518: 
0519: 
0520: //-----------------------------------------------------------------------------
0521: // Name: DrawText()
0522: // Desc: Draws 2D text. Note that sx and sy are in pixels
0523: //-----------------------------------------------------------------------------
0524: HRESULT CD3DFont::DrawText( FLOAT sx, FLOAT sy, DWORD dwColor,
0525:                             const TCHAR* strText, DWORD dwFlags )
0526: {
0527:     if( m_pd3dDevice == NULL )
0528:         return E_FAIL;
0529: 
0530:     // Setup renderstate
0531:     m_pStateBlockSaved->Capture();
0532:     m_pStateBlockDrawText->Apply();
0533:     m_pd3dDevice->SetFVF( D3DFVF_FONT2DVERTEX );
0534:     m_pd3dDevice->SetPixelShader( NULL );
0535:     m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) );
0536: 
0537:     // Set filter states
0538:     if( dwFlags & D3DFONT_FILTERED )
0539:     {
0540:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
0541:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
0542:     }
0543: 
0544:     // Center the text block in the viewport
0545:     if( dwFlags & D3DFONT_CENTERED_X )
0546:     {
0547:         D3DVIEWPORT9 vp;
0548:         m_pd3dDevice->GetViewport( &vp );
0549:         const TCHAR* strTextTmp = strText;
0550:         float xFinal = 0.0f;
0551: 
0552:         while( *strTextTmp )
0553:         {
0554:             TCHAR c = *strTextTmp++;
0555:     
0556:             if( c == _T('\n') )
0557:                 break;  // Isn't supported.  
0558:             if( (c-32) < 0 || (c-32) >= 128-32 )
0559:                 continue;
0560: 
0561:             FLOAT tx1 = m_fTexCoords[c-32][0];
0562:             FLOAT tx2 = m_fTexCoords[c-32][2];
0563:     
0564:             FLOAT w = (tx2-tx1) *  m_dwTexWidth / m_fTextScale;
0565:     
0566:             xFinal += w - (2 * m_dwSpacing);
0567:         }
0568: 
0569:         sx = (vp.Width-xFinal)/2.0f;
0570:     }
0571:     if( dwFlags & D3DFONT_CENTERED_Y )
0572:     {
0573:         D3DVIEWPORT9 vp;
0574:         m_pd3dDevice->GetViewport( &vp );
0575:         float fLineHeight = ((m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight);
0576:         sy = (vp.Height-fLineHeight)/2;
0577:     }
0578: 
0579:     // Adjust for character spacing
0580:     sx -= m_dwSpacing;
0581:     FLOAT fStartX = sx;
0582: 
0583:     // Fill vertex buffer
0584:     FONT2DVERTEX* pVertices = NULL;
0585:     DWORD         dwNumTriangles = 0;
0586:     m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
0587: 
0588:     while( *strText )
0589:     {
0590:         TCHAR c = *strText++;
0591: 
0592:         if( c == _T('\n') )
0593:         {
0594:             sx = fStartX;
0595:             sy += (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
0596:         }
0597: 
0598:         if( (c-32) < 0 || (c-32) >= 128-32 )
0599:             continue;
0600: 
0601:         FLOAT tx1 = m_fTexCoords[c-32][0];
0602:         FLOAT ty1 = m_fTexCoords[c-32][1];
0603:         FLOAT tx2 = m_fTexCoords[c-32][2];
0604:         FLOAT ty2 = m_fTexCoords[c-32][3];
0605: 
0606:         FLOAT w = (tx2-tx1) *  m_dwTexWidth / m_fTextScale;
0607:         FLOAT h = (ty2-ty1) * m_dwTexHeight / m_fTextScale;
0608: 
0609:         if( c != _T(' ') )
0610:         {
0611:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx1, ty2 );
0612:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
0613:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
0614:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx2, ty1 );
0615:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
0616:             *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
0617:             dwNumTriangles += 2;
0618: 
0619:             if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
0620:             {
0621:                 // Unlock, render, and relock the vertex buffer
0622:                 m_pVB->Unlock();
0623:                 m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
0624:                 pVertices = NULL;
0625:                 m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
0626:                 dwNumTriangles = 0L;
0627:             }
0628:         }
0629: 
0630:         sx += w - (2 * m_dwSpacing);
0631:     }
0632: 
0633:     // Unlock and render the vertex buffer
0634:     m_pVB->Unlock();
0635:     if( dwNumTriangles > 0 )
0636:         m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
0637: 
0638:     // Restore the modified renderstates
0639:     m_pStateBlockSaved->Apply();
0640: 
0641:     return S_OK;
0642: }
0643: 
0644: 
0645: 
0646: 
0647: //-----------------------------------------------------------------------------
0648: // Name: Render3DText()
0649: // Desc: Renders 3D text
0650: //-----------------------------------------------------------------------------
0651: HRESULT CD3DFont::Render3DText( const TCHAR* strText, DWORD dwFlags )
0652: {
0653:     if( m_pd3dDevice == NULL )
0654:         return E_FAIL;
0655: 
0656:     // Setup renderstate
0657:     m_pStateBlockSaved->Capture();
0658:     m_pStateBlockDrawText->Apply();
0659:     m_pd3dDevice->SetFVF( D3DFVF_FONT3DVERTEX );
0660:     m_pd3dDevice->SetPixelShader( NULL );
0661:     m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT3DVERTEX) );
0662: 
0663:     // Set filter states
0664:     if( dwFlags & D3DFONT_FILTERED )
0665:     {
0666:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
0667:         m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
0668:     }
0669: 
0670:     // Position for each text element
0671:     FLOAT x = 0.0f;
0672:     FLOAT y = 0.0f;
0673: 
0674:     // Center the text block at the origin (not the viewport)
0675:     if( dwFlags & D3DFONT_CENTERED_X )
0676:     {
0677:         SIZE sz;
0678:         GetTextExtent( strText, &sz );
0679:         x = -(((FLOAT)sz.cx)/10.0f)/2.0f;
0680:     }
0681:     if( dwFlags & D3DFONT_CENTERED_Y )
0682:     {
0683:         SIZE sz;
0684:         GetTextExtent( strText, &sz );
0685:         y = -(((FLOAT)sz.cy)/10.0f)/2.0f;
0686:     }
0687: 
0688:     // Turn off culling for two-sided text
0689:     if( dwFlags & D3DFONT_TWOSIDED )
0690:         m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
0691: 
0692:     // Adjust for character spacing
0693:     x -= m_dwSpacing / 10.0f;
0694:     FLOAT fStartX = x;
0695:     TCHAR c;
0696: 
0697:     // Fill vertex buffer
0698:     FONT3DVERTEX* pVertices;
0699:     DWORD         dwNumTriangles = 0L;
0700:     m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
0701: 
0702:     while( (c = *strText++) != 0 )
0703:     {
0704:         if( c == '\n' )
0705:         {
0706:             x = fStartX;
0707:             y -= (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight/10.0f;
0708:         }
0709: 
0710:         if( (c-32) < 0 || (c-32) >= 128-32 )
0711:             continue;
0712: 
0713:         FLOAT tx1 = m_fTexCoords[c-32][0];
0714:         FLOAT ty1 = m_fTexCoords[c-32][1];
0715:         FLOAT tx2 = m_fTexCoords[c-32][2];
0716:         FLOAT ty2 = m_fTexCoords[c-32][3];
0717: 
0718:         FLOAT w = (tx2-tx1) * m_dwTexWidth  / ( 10.0f * m_fTextScale );
0719:         FLOAT h = (ty2-ty1) * m_dwTexHeight / ( 10.0f * m_fTextScale );
0720: 
0721:         if( c != _T(' ') )
0722:         {
0723:             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+0,0), D3DXVECTOR3(0,0,-1), tx1, ty2 );
0724:             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
0725:             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
0726:             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+h,0), D3DXVECTOR3(0,0,-1), tx2, ty1 );
0727:             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
0728:             *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
0729:             dwNumTriangles += 2;
0730: 
0731:             if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
0732:             {
0733:                 // Unlock, render, and relock the vertex buffer
0734:                 m_pVB->Unlock();
0735:                 m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
0736:                 m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
0737:                 dwNumTriangles = 0L;
0738:             }
0739:         }
0740: 
0741:         x += w - (2 * m_dwSpacing) / 10.0f;
0742:     }
0743: 
0744:     // Unlock and render the vertex buffer
0745:     m_pVB->Unlock();
0746:     if( dwNumTriangles > 0 )
0747:         m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
0748: 
0749:     // Restore the modified renderstates
0750:     m_pStateBlockSaved->Apply();
0751: 
0752:     return S_OK;
0753: }
0754: 
0755: 
0756: 
0757: 
0758: