home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / Source / Chapter 10 / Engine / Font.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-10-01  |  8.5 KB  |  258 lines

  1. //-----------------------------------------------------------------------------
  2. // Font.h implementation.
  3. // Refer to the Font.h interface for more details.
  4. //
  5. // Programming a Multiplayer First Person Shooter in DirectX
  6. // Copyright (c) 2004 Vaughan Young
  7. //-----------------------------------------------------------------------------
  8. #include "Engine.h"
  9.  
  10. //-----------------------------------------------------------------------------
  11. // The font class constructor
  12. //-----------------------------------------------------------------------------
  13. Font::Font( char *name, short size, unsigned long bold, bool italic )
  14. {
  15.     HBITMAP bitmap = NULL;
  16.     HGDIOBJ oldBitmap = NULL;
  17.     HFONT oldFont = NULL;
  18.     BYTE *dstRow = NULL;
  19.     unsigned long x, y;
  20.  
  21.     HDC hDC = CreateCompatibleDC( NULL );
  22.     SetMapMode( hDC, MM_TEXT );
  23.  
  24.     // Create the font.
  25.     char height = -MulDiv( size, GetDeviceCaps( hDC, LOGPIXELSY ), 72 );
  26.     HFONT font = CreateFont( height, 0, 0, 0, bold, italic, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, name );
  27.     if( font == NULL )
  28.         goto End;
  29.  
  30.     oldFont = (HFONT)SelectObject( hDC, font );
  31.  
  32.     // Find the dimensions of the smallest texture to hold the characters.
  33.     m_textureWidth = m_textureHeight = 128;
  34.     while( !PrepareFont( hDC, true ) )
  35.     {
  36.         m_textureWidth *= 2;
  37.         m_textureHeight *= 2;
  38.     }
  39.  
  40.     // Create a new texture for the font
  41.     if( FAILED( g_engine->GetDevice()->CreateTexture( m_textureWidth, m_textureHeight, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &m_texture, NULL ) ) )
  42.         goto End;
  43.  
  44.     // Prepare the bitmap.
  45.     unsigned long *bitmapBits;
  46.     BITMAPINFO bmi;
  47.     ZeroMemory( &bmi.bmiHeader, sizeof( BITMAPINFOHEADER ) );
  48.     bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
  49.     bmi.bmiHeader.biWidth = (int)m_textureWidth;
  50.     bmi.bmiHeader.biHeight = -(int)m_textureHeight;
  51.     bmi.bmiHeader.biPlanes = 1;
  52.     bmi.bmiHeader.biCompression = BI_RGB;
  53.     bmi.bmiHeader.biBitCount = 32;
  54.  
  55.     // Create a bitmap for the font.
  56.     bitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (void**)&bitmapBits, NULL, 0 );
  57.  
  58.     oldBitmap = SelectObject( hDC, bitmap );
  59.  
  60.     // Set the text properties.
  61.     SetTextColor( hDC, RGB( 255,255,255 ) );
  62.     SetBkColor( hDC, 0x00000000 );
  63.     SetTextAlign( hDC, TA_TOP );
  64.  
  65.     // Prepare the font by drawing the characters onto the bitmap.
  66.     if( !PrepareFont( hDC ) )
  67.         goto End;
  68.  
  69.     // Lock the surface and write the alpha values for the set pixels.
  70.     D3DLOCKED_RECT d3dlr;
  71.     m_texture->LockRect( 0, &d3dlr, 0, 0 );
  72.     dstRow = (BYTE*)d3dlr.pBits;
  73.     WORD *dst16;
  74.     BYTE alpha;
  75.  
  76.     for( y = 0; y < m_textureHeight; y++ )
  77.     {
  78.         dst16 = (WORD*)dstRow;
  79.         for( x = 0; x < m_textureWidth; x++ )
  80.         {
  81.             alpha = (BYTE)( ( bitmapBits[m_textureWidth*y + x] & 0xff ) >> 4 );
  82.             if( alpha > 0 )
  83.                 *dst16++ = (WORD)( ( alpha << 12 ) | 0x0fff );
  84.             else
  85.                 *dst16++ = 0x0000;
  86.         }
  87.         dstRow += d3dlr.Pitch;
  88.     }
  89.  
  90.     // Create the vertex buffer for the characters.
  91.     g_engine->GetDevice()->CreateVertexBuffer( 1020 * sizeof( TLVertex ), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &m_vb, NULL );
  92.  
  93.     // Prepare the alpha testing for rendering the characters.
  94.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHAREF, 0x08 );
  95.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
  96.  
  97.     // Create a state block for capturing the volatile render states.
  98.     g_engine->GetDevice()->BeginStateBlock();
  99.     g_engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, false );
  100.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHATESTENABLE, false );
  101.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGENABLE, false );
  102.     g_engine->GetDevice()->EndStateBlock( &m_states );
  103.  
  104.     // Clean up and return.
  105. End:
  106.     if( m_texture )
  107.         m_texture->UnlockRect( 0 );
  108.  
  109.     SelectObject( hDC, oldBitmap );
  110.     SelectObject( hDC, oldFont );
  111.     DeleteObject( bitmap );
  112.     DeleteObject( font );
  113.     DeleteDC( hDC );
  114. }
  115.  
  116. //-----------------------------------------------------------------------------
  117. // The font class destructor
  118. //-----------------------------------------------------------------------------
  119. Font::~Font()
  120. {
  121.     SAFE_RELEASE( m_states );
  122.     SAFE_RELEASE( m_vb );
  123.     SAFE_RELEASE( m_texture );
  124. }
  125.  
  126. //-----------------------------------------------------------------------------
  127. // Prepares the font by drawing all the characters on the device context.
  128. //-----------------------------------------------------------------------------
  129. bool Font::PrepareFont( HDC hDC, bool measure )
  130. {
  131.     SIZE size;
  132.     char string[2];
  133.  
  134.     // Find the spacing between the characters based on the line height.
  135.     if( GetTextExtentPoint32( hDC, string, 1, &size ) == 0 )
  136.         return false;
  137.     m_spacing = (short)ceil( size.cy * 0.1f );
  138.  
  139.     // Set the position to start drawing at.
  140.     unsigned long x = m_spacing;
  141.     unsigned long y = 0;
  142.  
  143.     // Draw each character on the DC and move to the next position.
  144.     for( char c = 32; c < 127; c++ )
  145.     {
  146.         string[0] = c;
  147.         if( GetTextExtentPoint32( hDC, string, 1, &size ) == 0 )
  148.             return false;
  149.  
  150.         if( (unsigned long)( x + size.cx + m_spacing ) > m_textureWidth )
  151.         {
  152.             x = m_spacing;
  153.             y += size.cy + 1;
  154.         }
  155.  
  156.         // Ensure there is enough room to draw the character.
  157.         if( y + size.cy > m_textureHeight )
  158.             return false;
  159.  
  160.         // Draw the character if not measuring.
  161.         if( !measure )
  162.         {
  163.             if( ExtTextOut( hDC, x + 0, y + 0, ETO_OPAQUE, NULL, string, 1, NULL ) == 0 )
  164.                 return false;
  165.  
  166.             m_textureCoords[c - 32][0] = (float)( x - m_spacing ) / m_textureWidth;
  167.             m_textureCoords[c - 32][1] = (float)( y ) / m_textureHeight;
  168.             m_textureCoords[c - 32][2] = (float)( x + size.cx + m_spacing ) / m_textureWidth;
  169.             m_textureCoords[c - 32][3] = (float)( y + size.cy ) / m_textureHeight;
  170.         }
  171.  
  172.         x += size.cx + ( 2 * m_spacing );
  173.     }
  174.  
  175.     return true;
  176. }
  177.  
  178. //-----------------------------------------------------------------------------
  179. // Renders the given text to the screen using this font.
  180. //-----------------------------------------------------------------------------
  181. void Font::Render( char *text, float x, float y, D3DCOLOR colour )
  182. {
  183.     // Capture the current volatile render states.
  184.     m_states->Capture();
  185.  
  186.     // Set the volatile render states.
  187.     g_engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, false );
  188.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHATESTENABLE, true );
  189.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGENABLE, false );
  190.  
  191.     // Set the non-volatile render states.
  192.     g_engine->GetDevice()->SetTexture( 0, m_texture );
  193.     g_engine->GetDevice()->SetFVF( TL_VERTEX_FVF );
  194.     g_engine->GetDevice()->SetStreamSource( 0, m_vb, 0, TL_VERTEX_FVF_SIZE );
  195.  
  196.     // Adjust for character spacing.
  197.     x -= m_spacing;
  198.     float startX = x;
  199.  
  200.     // Fill the vertex buffer.
  201.     TLVertex* vertices = NULL;
  202.     unsigned long totalTriangles = 0;
  203.     m_vb->Lock( 0, 0, (void**)&vertices, D3DLOCK_DISCARD );
  204.  
  205.     // For each letter in the text, add a textured quad to the vertex buffer.
  206.     while( *text )
  207.     {
  208.         char c = *text++;
  209.  
  210.         if( c == _T( '\n' ) )
  211.         {
  212.             x = startX;
  213.             y += ( m_textureCoords[0][3] - m_textureCoords[0][1] ) * m_textureHeight;
  214.         }
  215.  
  216.         if( ( c - 32 ) < 0 || ( c - 32 ) >= 96 )
  217.             continue;
  218.  
  219.         float tx1 = m_textureCoords[c - 32][0];
  220.         float ty1 = m_textureCoords[c - 32][1];
  221.         float tx2 = m_textureCoords[c - 32][2];
  222.         float ty2 = m_textureCoords[c - 32][3];
  223.  
  224.         float w = ( tx2 - tx1 ) * m_textureWidth;
  225.         float h = ( ty2 - ty1 ) * m_textureHeight;
  226.  
  227.         if( c != _T( ' ' ) )
  228.         {
  229.             *vertices++ = TLVertex( D3DXVECTOR4( x - 0.5f, y + h - 0.5f, 0.0f, 1.0f ), colour, tx1, ty2 );
  230.             *vertices++ = TLVertex( D3DXVECTOR4( x - 0.5f, y - 0.5f, 0.0f, 1.0f ), colour, tx1, ty1 );
  231.             *vertices++ = TLVertex( D3DXVECTOR4( x + w - 0.5f, y + h - 0.5f, 0.0f, 1.0f ), colour, tx2, ty2 );
  232.             *vertices++ = TLVertex( D3DXVECTOR4( x + w - 0.5f, y - 0.5f, 0.0f, 1.0f ), colour, tx2, ty1 );
  233.             *vertices++ = TLVertex( D3DXVECTOR4( x + w - 0.5f, y + h - 0.5f, 0.0f, 1.0f ), colour, tx2, ty2 );
  234.             *vertices++ = TLVertex( D3DXVECTOR4( x - 0.5f, y - 0.5f, 0.0f, 1.0f ), colour, tx1, ty1 );
  235.             totalTriangles += 2;
  236.  
  237.             if( totalTriangles == 340 )
  238.             {
  239.                 // Unlock, render, and relock the vertex buffer.
  240.                 m_vb->Unlock();
  241.                 g_engine->GetDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, totalTriangles );
  242.                 vertices = NULL;
  243.                 m_vb->Lock( 0, 0, (void**)&vertices, D3DLOCK_DISCARD );
  244.                 totalTriangles = 0;
  245.             }
  246.         }
  247.  
  248.         x += w - ( 2 * m_spacing );
  249.     }
  250.  
  251.     // Unlock and render the vertex buffer.
  252.     m_vb->Unlock();
  253.     if( totalTriangles > 0 )
  254.         g_engine->GetDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, totalTriangles );
  255.  
  256.     // Restore the volatile render states.
  257.     m_states->Apply();
  258. }