2001-11-03 10:52:42 +00:00
#include "stdafx.h"
2001-11-04 19:34:28 +00:00
/*
-----------------------------------------------------------------------------
File: BitmapText.h
2001-11-03 10:52:42 +00:00
2001-11-04 19:34:28 +00:00
Desc: A font class that draws characters from a bitmap.
Copyright (c) 2001 Chris Danford. All rights reserved.
-----------------------------------------------------------------------------
*/
2001-11-03 10:52:42 +00:00
#include "BitmapText.h"
#include "IniFile.h"
2001-11-27 22:47:30 +00:00
#include <stdio.h>
2002-01-16 10:01:32 +00:00
#include "RageTextureManager.h"
2001-11-03 10:52:42 +00:00
BitmapText :: BitmapText ()
{
2001-11-27 22:47:30 +00:00
// m_colorTop = D3DXCOLOR(1,1,1,1);
// m_colorBottom = D3DXCOLOR(1,1,1,1);
2001-11-03 10:52:42 +00:00
2001-11-27 22:47:30 +00:00
for ( int i = 0 ; i < NUM_CHARS ; i ++ )
2002-01-16 10:01:32 +00:00
{
m_iCharToFrameNo [ i ] = - 1 ;
m_fFrameNoToWidth [ i ] = - 1 ;
}
m_HorizAlign = align_center ;
m_VertAlign = align_middle ;
2001-12-19 01:50:57 +00:00
2002-02-02 07:42:52 +00:00
// m_bHasShadow = false;
2002-02-02 05:11:12 +00:00
2002-02-02 07:42:52 +00:00
// create the vertex buffer
HRESULT hr = SCREEN -> GetDevice () -> CreateVertexBuffer (
MAX_NUM_VERTICIES * sizeof ( CUSTOMVERTEX ),
D3DUSAGE_WRITEONLY ,
D3DFVF_CUSTOMVERTEX ,
D3DPOOL_MANAGED ,
& m_pVB );
if ( FAILED ( hr ) )
{
RageErrorHr ( "Vertex Buffer Could Not Be Created" , hr );
}
m_iNumV = 0 ;
2002-02-02 05:11:12 +00:00
}
BitmapText ::~ BitmapText ()
{
SAFE_RELEASE ( m_pVB );
2001-11-25 04:31:44 +00:00
}
2001-11-03 10:52:42 +00:00
2002-01-16 10:01:32 +00:00
bool BitmapText :: Load ( CString sFontFilePath )
2001-11-25 04:31:44 +00:00
{
2002-01-20 07:51:09 +00:00
//RageLog( "BitmapText::LoadFromFontName(%s)", sFontFilePath );
2001-11-25 04:31:44 +00:00
2002-01-16 10:01:32 +00:00
m_sFontFilePath = sFontFilePath ; // save
2001-11-03 10:52:42 +00:00
2002-01-16 10:01:32 +00:00
// Split for the directory. We'll need it below
CString sFontDir , sFontFileName , sFontExtension ;
splitrelpath ( sFontFilePath , sFontDir , sFontFileName , sFontExtension );
2001-11-03 10:52:42 +00:00
2002-01-16 10:01:32 +00:00
// Read .font file
IniFile ini ;
ini . SetPath ( m_sFontFilePath );
if ( ! ini . ReadFile () )
RageError ( ssprintf ( "Error opening Font file '%s'." , m_sFontFilePath ) );
// load texture
CString sTextureFile = ini . GetValue ( "Font" , "Texture" );
if ( sTextureFile == "" )
RageError ( ssprintf ( "Error reading value 'Texture' from %s." , m_sFontFilePath ) );
CString sTexturePath = sFontDir + sTextureFile ; // save the path of the new texture
// is this the first time the texture is being loaded?
bool bFirstTimeBeingLoaded = ! TM -> IsTextureLoaded ( sTexturePath );
LoadTexture ( sTexturePath );
// the size of the sprite is the size of the image before it was scaled
SetWidth ( ( float ) m_pTexture -> GetSourceFrameWidth () );
SetHeight ( ( float ) m_pTexture -> GetSourceFrameHeight () );
// find out what characters are in this font
CString sCharacters = ini . GetValue ( "Font" , "Characters" );
if ( sCharacters != "" ) // the creator supplied characters
{
// sanity check
2002-02-24 01:43:11 +00:00
if ( sCharacters . GetLength () != ( int ) Sprite :: GetNumStates () )
2002-01-16 10:01:32 +00:00
RageError ( ssprintf ( "The characters in '%s' does not match the number of frames in the texture." , m_sFontFilePath ) );
// set the char to frameno map
for ( int i = 0 ; i < sCharacters . GetLength (); i ++ )
{
char c = sCharacters [ i ];
int iFrameNo = i ;
m_iCharToFrameNo [ c ] = iFrameNo ;
}
2001-11-27 22:47:30 +00:00
}
2002-01-16 10:01:32 +00:00
else // no creator supplied characters. Assume this is a full high ASCII set
{
for ( int i = 0 ; i < NUM_CHARS ; i ++ )
m_iCharToFrameNo [ i ] = i ;
}
// and load character widths
CString sWidthsValue = ini . GetValue ( "Font" , "Widths" );
if ( sWidthsValue != "" ) // the creator supplied witdths
{
CStringArray arrayCharWidths ;
split ( sWidthsValue , "," , arrayCharWidths );
2002-02-24 01:43:11 +00:00
if ( arrayCharWidths . GetSize () != ( int ) Sprite :: GetNumStates () )
2002-01-24 08:01:24 +00:00
RageError ( ssprintf ( "The number of widths specified in '%s' (%d) do not match the number of frames in the texture (%u)." , m_sFontFilePath , arrayCharWidths . GetSize (), Sprite :: GetNumStates ()) );
2002-01-16 10:01:32 +00:00
for ( int i = 0 ; i < arrayCharWidths . GetSize (); i ++ )
{
2002-02-24 01:43:11 +00:00
m_fFrameNoToWidth [ i ] = ( float ) atof ( arrayCharWidths [ i ] );
2002-01-16 10:01:32 +00:00
}
}
else // no creator supplied withds. Assume each character is the width of the frame
{
2002-02-24 01:43:11 +00:00
for ( int i = 0 ; i < ( int ) Sprite :: GetNumStates (); i ++ )
2002-01-16 10:01:32 +00:00
{
2002-02-24 01:43:11 +00:00
m_fFrameNoToWidth [ i ] = ( float ) m_pTexture -> GetSourceFrameWidth ();
2002-01-16 10:01:32 +00:00
}
}
if ( bFirstTimeBeingLoaded )
{
// tweak the textures frame rectangles so we don't draw extra to the left and right of the character
2002-02-24 01:43:11 +00:00
for ( int i = 0 ; i < ( int ) Sprite :: GetNumStates (); i ++ )
2002-01-16 10:01:32 +00:00
{
FRECT * pFrect = m_pTexture -> GetTextureCoordRect ( i );
float fPixelsToChopOff = m_fFrameNoToWidth [ i ] - GetUnzoomedWidth ();
2002-01-24 08:01:24 +00:00
float fTexCoordsToChopOff = fPixelsToChopOff / m_pTexture -> GetSourceWidth ();
2002-01-16 10:01:32 +00:00
pFrect -> left -= fTexCoordsToChopOff / 2 ;
pFrect -> right += fTexCoordsToChopOff / 2 ;
}
}
2001-11-27 22:47:30 +00:00
return true ;
2001-11-03 10:52:42 +00:00
}
2001-11-27 22:47:30 +00:00
2002-01-16 10:01:32 +00:00
2001-11-27 22:47:30 +00:00
// get a rectangle for the text, considering a possible text scaling.
// useful to know if some text is visible or not
2001-12-28 10:15:59 +00:00
float BitmapText :: GetWidestLineWidthInSourcePixels ()
2001-11-27 22:47:30 +00:00
{
2001-12-28 10:15:59 +00:00
float fWidestLineWidth = 0 ;
2001-11-27 22:47:30 +00:00
2001-12-28 10:15:59 +00:00
for ( int i = 0 ; i < m_sTextLines . GetSize (); i ++ )
{
float fLineWidth = GetLineWidthInSourcePixels ( i );
if ( fLineWidth > fWidestLineWidth )
fWidestLineWidth = fLineWidth ;
}
return fWidestLineWidth ;
}
float BitmapText :: GetLineWidthInSourcePixels ( int iLineNo )
{
2002-01-16 10:01:32 +00:00
CString & sLine = m_sTextLines [ iLineNo ];
2001-12-28 10:15:59 +00:00
float fLineWidth = 0 ;
2002-01-16 10:01:32 +00:00
for ( int i = 0 ; i < sLine . GetLength (); i ++ )
{
char c = sLine [ i ];
int iFrameNo = m_iCharToFrameNo [ c ];
2002-02-09 04:30:27 +00:00
if ( iFrameNo == - 1 ) // this font doesn't impelemnt this character
RageError ( ssprintf ( "The font '%s' does not implement the character '%c'" , m_sFontFilePath , c ) );
2002-01-16 10:01:32 +00:00
fLineWidth += m_fFrameNoToWidth [ iFrameNo ];
}
2001-12-28 10:15:59 +00:00
return fLineWidth ;
2001-11-27 22:47:30 +00:00
}
2002-02-02 07:42:52 +00:00
void BitmapText :: RebuildVertexBuffer ()
{
// make the object in logical units centered at the origin
LPDIRECT3DVERTEXBUFFER8 pVB = m_pVB ;
CUSTOMVERTEX * v ;
pVB -> Lock ( 0 , 0 , ( BYTE ** ) & v , 0 );
m_iNumV = 0 ; // the current vertex number
float fHeight = GetUnzoomedHeight ();
float fY ;
switch ( m_VertAlign )
{
case align_top : fY = - ( m_sTextLines . GetSize ()) * fHeight / 2 ; break ;
case align_middle : fY = 0 ; break ;
case align_bottom : fY = ( m_sTextLines . GetSize ()) * fHeight / 2 ; break ;
2002-02-11 04:46:31 +00:00
default : ASSERT ( false );
2002-02-02 07:42:52 +00:00
}
for ( int i = 0 ; i < m_sTextLines . GetSize (); i ++ ) // foreach line
{
CString & sLine = m_sTextLines [ i ];
float fLineWidth = GetLineWidthInSourcePixels ( i );
float fX ;
switch ( m_HorizAlign )
{
case align_left : fX = 0 ; break ;
case align_center : fX = - ( fLineWidth / 2 ); break ;
case align_right : fX = - fLineWidth ; break ;
2002-02-11 04:46:31 +00:00
default : ASSERT ( false );
2002-02-02 07:42:52 +00:00
}
for ( int j = 0 ; j < sLine . GetLength (); j ++ ) // for each character in the line
{
char c = sLine [ j ];
int iFrameNo = m_iCharToFrameNo [ c ];
2002-02-09 04:30:27 +00:00
if ( iFrameNo == - 1 ) // this font doesn't impelemnt this character
RageError ( ssprintf ( "The font '%s' does not implement the character '%c'" , m_sFontFilePath , c ) );
2002-02-02 07:42:52 +00:00
float fCharWidth = m_fFrameNoToWidth [ iFrameNo ];
//if( c == ' ' )
//{
// fX += fCharWidth;
// continue;
//}
// HACK:
// The right side of italic letters is being cropped. So, we're going to draw a little bit
// to the right of the normal character.
float fPercentExtra = 0.20f ;
// don't go over the frame boundary and draw part of the adjacent character
float fPercentageOfFrame = fCharWidth / GetUnzoomedWidth ();
if ( fPercentExtra > 1 - fPercentageOfFrame )
fPercentExtra = 1 - fPercentageOfFrame ;
float fExtraPixels = fPercentExtra * GetUnzoomedWidth ();
2002-02-03 20:45:08 +00:00
// first triangle
v [ m_iNumV ++ ]. p = D3DXVECTOR3 ( fX , fY - fHeight / 2 , 0 ); // top left
v [ m_iNumV ++ ]. p = D3DXVECTOR3 ( fX , fY + fHeight / 2 , 0 ); // bottom left
fX += fCharWidth ;
2002-02-02 07:42:52 +00:00
v [ m_iNumV ++ ]. p = D3DXVECTOR3 ( fX + fExtraPixels , fY - fHeight / 2 , 0 ); // top right
2002-02-03 20:45:08 +00:00
// 2nd triangle
v [ m_iNumV ++ ]. p = v [ m_iNumV - 1 ]. p ; // top right
v [ m_iNumV ++ ]. p = v [ m_iNumV - 3 ]. p ; // bottom left
v [ m_iNumV ++ ]. p = D3DXVECTOR3 ( fX + fExtraPixels , fY + fHeight / 2 , 0 ); // bottom right
2002-02-02 07:42:52 +00:00
// set texture coordinates
2002-02-03 20:45:08 +00:00
m_iNumV -= 6 ;
2002-02-02 07:42:52 +00:00
FRECT * pTexCoordRect = m_pTexture -> GetTextureCoordRect ( iFrameNo );
float fExtraTexCoords = fPercentExtra * m_pTexture -> GetTextureFrameWidth () / m_pTexture -> GetTextureWidth ();
v [ m_iNumV ]. tu = pTexCoordRect -> left ; v [ m_iNumV ++ ]. tv = pTexCoordRect -> top ; // top left
2002-02-03 20:45:08 +00:00
v [ m_iNumV ]. tu = pTexCoordRect -> left ; v [ m_iNumV ++ ]. tv = pTexCoordRect -> bottom ; // bottom left
2002-02-02 07:42:52 +00:00
v [ m_iNumV ]. tu = pTexCoordRect -> right + fExtraTexCoords ; v [ m_iNumV ++ ]. tv = pTexCoordRect -> top ; // top right
2002-02-03 20:45:08 +00:00
v [ m_iNumV ]. tu = v [ m_iNumV - 1 ]. tu ; v [ m_iNumV ++ ]. tv = v [ m_iNumV - 1 ]. tv ; // top right
v [ m_iNumV ]. tu = v [ m_iNumV - 3 ]. tu ; v [ m_iNumV ++ ]. tv = v [ m_iNumV - 3 ]. tv ; // bottom left
v [ m_iNumV ]. tu = pTexCoordRect -> right + fExtraTexCoords ; v [ m_iNumV ++ ]. tv = pTexCoordRect -> bottom ; // bottom right
2002-02-02 07:42:52 +00:00
}
fY += fHeight ;
}
pVB -> Unlock ();
}
2001-11-27 22:47:30 +00:00
// draw text at x, y using colorTop blended down to colorBottom, with size multiplied by scale
2002-01-16 10:01:32 +00:00
void BitmapText :: RenderPrimitives ()
2001-11-27 22:47:30 +00:00
{
2001-12-28 10:15:59 +00:00
if ( m_sTextLines . GetSize () == 0 )
return ;
2002-01-16 10:01:32 +00:00
if ( m_pTexture == NULL )
return ;
2001-12-28 10:15:59 +00:00
2002-01-16 10:01:32 +00:00
D3DXCOLOR colorDiffuse [ 4 ];
for ( int i = 0 ; i < 4 ; i ++ )
colorDiffuse [ i ] = m_colorDiffuse [ i ];
D3DXCOLOR colorAdd = m_colorAdd ;
2001-12-19 01:50:57 +00:00
2002-01-16 10:01:32 +00:00
// update properties based on SpriteEffects
switch ( m_Effect )
2001-12-19 01:50:57 +00:00
{
2002-01-16 10:01:32 +00:00
case no_effect :
break ;
case blinking :
2001-12-28 10:15:59 +00:00
{
2002-01-16 10:01:32 +00:00
for ( int i = 0 ; i < 4 ; i ++ )
colorDiffuse [ i ] = m_bTweeningTowardEndColor ? m_effect_colorDiffuse1 : m_effect_colorDiffuse2 ;
2001-12-19 01:50:57 +00:00
}
2002-01-16 10:01:32 +00:00
break ;
case camelion :
{
for ( int i = 0 ; i < 4 ; i ++ )
colorDiffuse [ i ] = m_effect_colorDiffuse1 * m_fPercentBetweenColors + m_effect_colorDiffuse2 * ( 1.0f - m_fPercentBetweenColors );
}
break ;
case glowing :
colorAdd = m_effect_colorAdd1 * m_fPercentBetweenColors + m_effect_colorAdd2 * ( 1.0f - m_fPercentBetweenColors );
break ;
case wagging :
break ;
case spinning :
// nothing special needed
break ;
case vibrating :
break ;
case flickering :
m_bVisibleThisFrame = ! m_bVisibleThisFrame ;
if ( ! m_bVisibleThisFrame )
for ( int i = 0 ; i < 4 ; i ++ )
colorDiffuse [ i ] = D3DXCOLOR ( 0 , 0 , 0 , 0 ); // don't draw the frame
break ;
2001-11-27 22:47:30 +00:00
}
2002-02-02 05:11:12 +00:00
2002-01-16 10:01:32 +00:00
2002-02-02 07:42:52 +00:00
2002-02-03 20:45:08 +00:00
RebuildVertexBuffer ();
2002-02-02 07:42:52 +00:00
LPDIRECT3DVERTEXBUFFER8 pVB = m_pVB ;
2002-01-16 10:01:32 +00:00
CUSTOMVERTEX * v ;
// Set the stage...
LPDIRECT3DDEVICE8 pd3dDevice = SCREEN -> GetDevice ();
pd3dDevice -> SetTexture ( 0 , m_pTexture -> GetD3DTexture () );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_MINFILTER , D3DTEXF_LINEAR );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_MAGFILTER , D3DTEXF_LINEAR );
//pd3dDevice->SetRenderState( D3DRS_SRCBLEND, bBlendAdd ? D3DBLEND_ONE : D3DBLEND_SRCALPHA );
//pd3dDevice->SetRenderState( D3DRS_DESTBLEND, bBlendAdd ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA );
pd3dDevice -> SetRenderState ( D3DRS_SRCBLEND , m_bBlendAdd ? D3DBLEND_ONE : D3DBLEND_SRCALPHA );
pd3dDevice -> SetRenderState ( D3DRS_DESTBLEND , m_bBlendAdd ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA );
pd3dDevice -> SetVertexShader ( D3DFVF_CUSTOMVERTEX );
pd3dDevice -> SetStreamSource ( 0 , pVB , sizeof ( CUSTOMVERTEX ) );
if ( colorDiffuse [ 0 ]. a != 0 )
{
//////////////////////
// render the shadow
//////////////////////
if ( m_bHasShadow )
{
SCREEN -> PushMatrix ();
SCREEN -> Translate ( 5 , 5 , 0 ); // shift by 5 units
pVB -> Lock ( 0 , 0 , ( BYTE ** ) & v , 0 );
2002-02-02 07:42:52 +00:00
for ( int i = 0 ; i < m_iNumV ; i ++ )
2002-01-16 10:01:32 +00:00
v [ i ]. color = D3DXCOLOR ( 0 , 0 , 0 , 0.5f * colorDiffuse [ 0 ]. a ); // semi-transparent black
pVB -> Unlock ();
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TEXTURE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG2 );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_MODULATE );
2002-02-03 20:45:08 +00:00
pd3dDevice -> DrawPrimitive ( D3DPT_TRIANGLELIST , 0 , m_iNumV / 3 );
2002-01-16 10:01:32 +00:00
SCREEN -> PopMatrix ();
}
//////////////////////
// render the diffuse pass
//////////////////////
pVB -> Lock ( 0 , 0 , ( BYTE ** ) & v , 0 );
2002-02-02 07:42:52 +00:00
for ( int i = 0 ; i < m_iNumV ; i ++ )
2002-01-16 10:01:32 +00:00
{
2002-02-02 07:42:52 +00:00
if ( i % 2 == 0 ) // this is a bottom vertex
2002-01-16 10:01:32 +00:00
v [ i ]. color = colorDiffuse [ 0 ];
else // this is a top vertex
v [ i ]. color = colorDiffuse [ 1 ];
}
pVB -> Unlock ();
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TEXTURE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_MODULATE ); //bBlendAdd ? D3DTOP_ADD : D3DTOP_MODULATE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_MODULATE ); //bBlendAdd ? D3DTOP_ADD : D3DTOP_MODULATE );
2002-02-03 20:45:08 +00:00
pd3dDevice -> DrawPrimitive ( D3DPT_TRIANGLELIST , 0 , m_iNumV / 3 );
2002-01-16 10:01:32 +00:00
}
//////////////////////
// render the add pass
//////////////////////
if ( colorAdd . a != 0 )
{
pVB -> Lock ( 0 , 0 , ( BYTE ** ) & v , 0 );
2002-02-02 07:42:52 +00:00
for ( int i = 0 ; i < m_iNumV ; i ++ )
2002-01-16 10:01:32 +00:00
v [ i ]. color = colorAdd ;
pVB -> Unlock ();
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TEXTURE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG2 );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE );
pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_MODULATE );
2002-02-03 20:45:08 +00:00
pd3dDevice -> DrawPrimitive ( D3DPT_TRIANGLELIST , 0 , m_iNumV / 3 );
2002-01-16 10:01:32 +00:00
}
2001-12-28 10:15:59 +00:00
}
void BitmapText :: SetText ( CString sText )
{
2002-01-16 10:01:32 +00:00
if ( sText . GetLength () > MAX_NUM_VERTICIES )
sText = sText . Left ( MAX_NUM_VERTICIES );
// strip out foreign chars
2002-02-09 04:30:27 +00:00
for ( int i = 0 ; i < sText . GetLength (); i ++ ) // for each character
if ( sText [ i ] < 0 || sText [ i ] > NUM_CHARS - 1 )
sText . SetAt ( i , ' ' );
2002-01-16 10:01:32 +00:00
2001-12-28 10:15:59 +00:00
m_sTextLines . RemoveAll ();
split ( sText , " \n " , m_sTextLines , false );
2002-02-02 07:42:52 +00:00
RebuildVertexBuffer ();
2001-12-28 10:15:59 +00:00
}
CString BitmapText :: GetText ()
{
return join ( " \n " , m_sTextLines );
2001-11-27 22:47:30 +00:00
}
2002-02-02 05:11:12 +00:00