//----------------------------------------------------------------------------- // 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; }