#include "stdafx.h" /* ----------------------------------------------------------------------------- File: Sprite.cpp Desc: A bitmap actor that animates and moves around. Copyright (c) 2001 Chris Danford. All rights reserved. ----------------------------------------------------------------------------- */ #include "Sprite.h" #include "RageTextureManager.h" #include "PrefsManager.h" #include "IniFile.h" #include #include "RageHelper.h" Sprite::Sprite() { m_pTexture = NULL; m_iNumStates = 0; m_iCurState = 0; m_bIsAnimating = TRUE; m_fSecsIntoState = 0.0; m_bUsingCustomTexCoords = false; } Sprite::~Sprite() { // HELPER.Log( "Sprite Destructor" ); TEXTURE->UnloadTexture( m_sTexturePath ); } bool Sprite::LoadFromTexture( CString sTexturePath, DWORD dwHints, bool bForceReload ) { HELPER.Log( ssprintf("Sprite::LoadFromTexture(%s)", sTexturePath) ); //Init(); return LoadTexture( sTexturePath, dwHints, bForceReload ); } // Sprite file has the format: // // [Sprite] // Texture=Textures\Logo.bmp // Frame0000=0 // Delay0000=1.0 // Frame0001=3 // Delay0000=2.0 bool Sprite::LoadFromSpriteFile( CString sSpritePath, DWORD dwHints, bool bForceReload ) { HELPER.Log( ssprintf("Sprite::LoadFromSpriteFile(%s)", sSpritePath) ); //Init(); m_sSpritePath = sSpritePath; // Split for the directory. We'll need it below CString sFontDir, sFontFileName, sFontExtension; splitrelpath( m_sSpritePath, sFontDir, sFontFileName, sFontExtension ); // read sprite file IniFile ini; ini.SetPath( m_sSpritePath ); if( !ini.ReadFile() ) HELPER.FatalError( ssprintf("Error opening Sprite file '%s'.", m_sSpritePath) ); CString sTextureFile = ini.GetValue( "Sprite", "Texture" ); if( sTextureFile == "" ) HELPER.FatalError( ssprintf("Error reading value 'Texture' from %s.", m_sSpritePath) ); CString sTexturePath = sFontDir + sTextureFile; // save the path of the new texture // Load the texture if( !LoadTexture( sTexturePath ) ) return FALSE; // Read in frames and delays from the sprite file, // overwriting the states that LoadFromTexture created. for( UINT i=0; i= m_pTexture->GetNumFrames() ) HELPER.FatalError( ssprintf("In '%s', %s is %d, but the texture %s only has %d frames.", m_sSpritePath, sFrameKey, m_iStateToFrame[i], sTexturePath, m_pTexture->GetNumFrames()) ); m_fDelay[i] = (float)ini.GetValueF( "Sprite", sDelayKey ); if( m_iStateToFrame[i] == 0 && m_fDelay[i] > -0.00001f && m_fDelay[i] < 0.00001f ) // both values are empty break; m_iNumStates = i+1; } if( m_iNumStates == 0 ) { m_iNumStates = 1; m_iStateToFrame[0] = 0; m_fDelay[0] = 10; } return true; } bool Sprite::LoadTexture( CString sTexturePath, DWORD dwHints, bool bForceReload ) { if( m_pTexture != NULL ) // If there was a previous bitmap... TEXTURE->UnloadTexture( m_sTexturePath ); // Unload it. m_sTexturePath = sTexturePath; m_pTexture = TEXTURE->LoadTexture( m_sTexturePath, dwHints, bForceReload ); assert( m_pTexture != NULL ); // 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() ); // Assume the frames of this animation play in sequential order with 0.2 second delay. for( int i=0; iGetNumFrames(); i++ ) { m_iStateToFrame[i] = i; m_fDelay[i] = 0.1f; m_iNumStates = i+1; } return TRUE; } void Sprite::Update( float fDeltaTime ) { //PrintDebugInfo(); Actor::Update( fDeltaTime ); // do tweening // update animation if( m_bIsAnimating ) { m_fSecsIntoState += fDeltaTime; if( m_fSecsIntoState > m_fDelay[m_iCurState] ) // it's time to switch frames { // increment frame and reset the counter m_fSecsIntoState -= m_fDelay[m_iCurState]; // leave the left over time for the next frame m_iCurState ++; if( m_iCurState >= m_iNumStates ) m_iCurState = 0; } } } void Sprite::RenderPrimitives() { SCREEN->TranslateLocal( -0.5f, -0.5f, 0 ); // offset so that pixels are aligned to texels if( m_pTexture == NULL ) return; // use m_temp_* variables to draw the object // make the object in logical units centered at the origin FRECT quadVerticies; switch( m_HorizAlign ) { case align_top: quadVerticies.left = 0; quadVerticies.right = m_size.x; break; case align_middle: quadVerticies.left = -m_size.x/2; quadVerticies.right = m_size.x/2; break; case align_bottom: quadVerticies.left = -m_size.x; quadVerticies.right = 0; break; default: ASSERT( false ); } switch( m_VertAlign ) { case align_bottom: quadVerticies.top = 0; quadVerticies.bottom = m_size.y; break; case align_middle: quadVerticies.top = -m_size.y/2; quadVerticies.bottom = m_size.y/2; break; case align_top: quadVerticies.top = -m_size.y; quadVerticies.bottom = 0; break; default: ASSERT( false ); } LPDIRECT3DVERTEXBUFFER8 pVB = SCREEN->GetVertexBuffer(); CUSTOMVERTEX* v; pVB->Lock( 0, 0, (BYTE**)&v, 0 ); // shift by one pixel because sprites are not aligned (why?!?) v[0].p = D3DXVECTOR3( quadVerticies.left+1, quadVerticies.bottom, 0 ); // bottom left v[1].p = D3DXVECTOR3( quadVerticies.left+1, quadVerticies.top, 0 ); // top left v[2].p = D3DXVECTOR3( quadVerticies.right+1, quadVerticies.bottom, 0 ); // bottom right v[3].p = D3DXVECTOR3( quadVerticies.right+1, quadVerticies.top, 0 ); // top right if( m_bUsingCustomTexCoords ) { v[0].tu = m_CustomTexCoords[0]; v[0].tv = m_CustomTexCoords[1]; // bottom left v[1].tu = m_CustomTexCoords[2]; v[1].tv = m_CustomTexCoords[3]; // top left v[2].tu = m_CustomTexCoords[4]; v[2].tv = m_CustomTexCoords[5]; // bottom right v[3].tu = m_CustomTexCoords[6]; v[3].tv = m_CustomTexCoords[7]; // top right } else { UINT uFrameNo = m_iStateToFrame[m_iCurState]; FRECT* pTexCoordRect = m_pTexture->GetTextureCoordRect( uFrameNo ); v[0].tu = pTexCoordRect->left; v[0].tv = pTexCoordRect->bottom; // bottom left v[1].tu = pTexCoordRect->left; v[1].tv = pTexCoordRect->top; // top left v[2].tu = pTexCoordRect->right; v[2].tv = pTexCoordRect->bottom; // bottom right v[3].tu = pTexCoordRect->right; v[3].tv = pTexCoordRect->top; // top right } pVB->Unlock(); // Set the stage... LPDIRECT3DDEVICE8 pd3dDevice = SCREEN->GetDevice(); pd3dDevice->SetTexture( 0, m_pTexture->GetD3DTexture() ); 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( m_temp_colorDiffuse[0].a != 0 ) { ////////////////////// // render the shadow ////////////////////// if( m_bShadow ) { SCREEN->PushMatrix(); SCREEN->TranslateLocal( m_fShadowLength, m_fShadowLength, 0 ); // shift by 5 units pVB->Lock( 0, 0, (BYTE**)&v, 0 ); v[0].color = v[1].color = v[2].color = v[3].color = D3DXCOLOR(0,0,0,0.5f*m_temp_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 ); pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); SCREEN->PopMatrix(); } ////////////////////// // render the diffuse pass ////////////////////// pVB->Lock( 0, 0, (BYTE**)&v, 0 ); v[0].color = m_temp_colorDiffuse[2]; // bottom left v[1].color = m_temp_colorDiffuse[0]; // top left v[2].color = m_temp_colorDiffuse[3]; // bottom right v[3].color = m_temp_colorDiffuse[1]; // top right 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 ); // finally! Pump those triangles! pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); } ////////////////////// // render the add pass ////////////////////// if( m_temp_colorAdd.a != 0 ) { pVB->Lock( 0, 0, (BYTE**)&v, 0 ); v[0].color = v[1].color = v[2].color = v[3].color = m_temp_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 ); pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); } } void Sprite::SetState( int iNewState ) { ASSERT( iNewState >= 0 && iNewState < m_iNumStates ); m_iCurState = iNewState; m_fSecsIntoState = 0.0; } void Sprite::SetCustomTextureRect( FRECT new_texcoord_frect ) { m_bUsingCustomTexCoords = true; m_CustomTexCoords[0] = new_texcoord_frect.left; m_CustomTexCoords[1] = new_texcoord_frect.bottom; // bottom left m_CustomTexCoords[2] = new_texcoord_frect.left; m_CustomTexCoords[3] = new_texcoord_frect.top; // top left m_CustomTexCoords[4] = new_texcoord_frect.right; m_CustomTexCoords[5] = new_texcoord_frect.bottom; // bottom right m_CustomTexCoords[6] = new_texcoord_frect.right; m_CustomTexCoords[7] = new_texcoord_frect.top; // top right } void Sprite::SetCustomTextureCoords( float fTexCoords[8] ) // order: bottom left, top left, bottom right, top right { m_bUsingCustomTexCoords = true; for( int i=0; i<8; i++ ) m_CustomTexCoords[i] = fTexCoords[i]; } void Sprite::SetCustomImageRect( FRECT rectImageCoords ) { // Convert to a rectangle in texture coordinate space. rectImageCoords.left *= m_pTexture->GetImageWidth() / (float)m_pTexture->GetTextureWidth(); rectImageCoords.right *= m_pTexture->GetImageWidth() / (float)m_pTexture->GetTextureWidth(); rectImageCoords.top *= m_pTexture->GetImageHeight() / (float)m_pTexture->GetTextureHeight(); rectImageCoords.bottom *= m_pTexture->GetImageHeight() / (float)m_pTexture->GetTextureHeight(); SetCustomTextureRect( rectImageCoords ); } void Sprite::SetCustomImageCoords( float fImageCoords[8] ) // order: bottom left, top left, bottom right, top right { // convert image coords to texture coords in place for( int i=0; i<8; i+=2 ) { fImageCoords[i+0] *= m_pTexture->GetImageWidth() / (float)m_pTexture->GetTextureWidth(); fImageCoords[i+1] *= m_pTexture->GetImageHeight() / (float)m_pTexture->GetTextureHeight(); } SetCustomTextureCoords( fImageCoords ); } void Sprite::StopUsingCustomCoords() { m_bUsingCustomTexCoords = false; }