Files
itgmania212121/stepmania/src/Sprite.cpp
T

417 lines
12 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
/*
-----------------------------------------------------------------------------
Class: Sprite
Desc: See header.
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-06-27 17:49:10 +00:00
Chris Danford
-----------------------------------------------------------------------------
*/
2001-11-03 10:52:42 +00:00
2002-08-23 01:06:36 +00:00
#include <math.h>
#include <assert.h>
2001-11-03 10:52:42 +00:00
#include "Sprite.h"
#include "RageTextureManager.h"
#include "IniFile.h"
2002-05-01 19:14:55 +00:00
#include "RageLog.h"
2002-07-27 19:29:51 +00:00
#include "RageException.h"
2002-05-27 08:23:27 +00:00
#include "PrefsManager.h"
#include "RageDisplay.h"
#include "GameConstantsAndTypes.h"
2003-02-14 06:31:09 +00:00
#include "SDL_utils.h"
2001-11-03 10:52:42 +00:00
2003-01-11 04:09:49 +00:00
#include "RageDisplayInternal.h"
2001-11-03 10:52:42 +00:00
Sprite::Sprite()
{
m_pTexture = NULL;
m_bDrawIfTextureNull = false;
m_iNumStates = 0;
m_iCurState = 0;
2001-11-03 10:52:42 +00:00
m_fSecsIntoState = 0.0;
2001-12-28 10:15:59 +00:00
m_bUsingCustomTexCoords = false;
2001-11-03 10:52:42 +00:00
}
2002-01-16 10:01:32 +00:00
2001-11-03 10:52:42 +00:00
Sprite::~Sprite()
{
UnloadTexture();
2001-11-03 10:52:42 +00:00
}
bool Sprite::LoadBG( RageTextureID ID )
{
ID.iMipMaps = 1;
ID.bDither = true;
return Load(ID);
}
bool Sprite::Load( RageTextureID ID )
{
if( ID.filename == "" ) return true;
if( ID.filename.Right(7) == ".sprite" )
return LoadFromSpriteFile( ID );
else
return LoadFromTexture( ID );
};
2001-11-03 10:52:42 +00:00
// Sprite file has the format:
//
// [Sprite]
2001-12-19 01:50:57 +00:00
// Texture=Textures\Logo.bmp
2001-11-03 10:52:42 +00:00
// Frame0000=0
// Delay0000=1.0
// Frame0001=3
// Delay0000=2.0
// BaseRotationXDegrees=0
// BaseRotationYDegrees=0
// BaseRotationZDegrees=0
// BaseZoomX=1
// BaseZoomY=1
// BaseZoomZ=1
bool Sprite::LoadFromSpriteFile( RageTextureID ID )
2001-11-03 10:52:42 +00:00
{
2003-04-25 00:01:35 +00:00
LOG->Trace( ssprintf("Sprite::LoadFromSpriteFile(%s)", ID.filename.c_str()) );
2001-11-03 10:52:42 +00:00
2002-01-16 10:01:32 +00:00
//Init();
2001-11-03 10:52:42 +00:00
m_sSpritePath = ID.filename;
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( m_sSpritePath, sFontDir, sFontFileName, sFontExtension );
2001-11-03 10:52:42 +00:00
// read sprite file
IniFile ini;
ini.SetPath( m_sSpritePath );
if( !ini.ReadFile() )
2003-04-25 00:01:35 +00:00
RageException::Throw( "Error opening Sprite file '%s'.", m_sSpritePath.c_str() );
2001-11-03 10:52:42 +00:00
2002-05-20 08:59:37 +00:00
CString sTextureFile;
ini.GetValue( "Sprite", "Texture", sTextureFile );
if( sTextureFile == "" )
2003-04-25 00:01:35 +00:00
RageException::Throw( "Error reading value 'Texture' from %s.", m_sSpritePath.c_str() );
2001-11-03 10:52:42 +00:00
ID.filename = sFontDir + sTextureFile; // save the path of the real texture
{
vector<CString> asElementPaths;
GetDirListing( ID.filename + "*", asElementPaths, false, true );
if(asElementPaths.size() == 0)
RageException::Throw( "The sprite file '%s' points to a texture '%s' which doesn't exist.", m_sSpritePath.c_str(), ID.filename.c_str() );
if(asElementPaths.size() > 1)
{
CString message = ssprintf(
"There is more than one file that matches "
"'%s/%s'. Please remove all but one of these matches.",
ID.filename.c_str() );
RageException::Throw( message );
}
ID.filename = asElementPaths[0];
}
2001-11-03 10:52:42 +00:00
// Load the texture
LoadFromTexture( ID );
2001-11-03 10:52:42 +00:00
// Read in frames and delays from the sprite file,
// overwriting the states that LoadFromTexture created.
for( int i=0; i<MAX_SPRITE_STATES; i++ )
2001-11-03 10:52:42 +00:00
{
2002-07-23 01:41:40 +00:00
CString sFrameKey = ssprintf( "Frame%04d", i );
CString sDelayKey = ssprintf( "Delay%04d", i );
2001-11-03 10:52:42 +00:00
2002-05-20 08:59:37 +00:00
m_iStateToFrame[i] = 0;
if( !ini.GetValueI( "Sprite", sFrameKey, m_iStateToFrame[i] ) )
break;
if( m_iStateToFrame[i] >= m_pTexture->GetNumFrames() )
2002-12-21 19:34:02 +00:00
RageException::Throw( "In '%s', %s is %d, but the texture %s only has %d frames.",
2003-04-25 00:01:35 +00:00
m_sSpritePath.c_str(), sFrameKey.c_str(), m_iStateToFrame[i], ID.filename.c_str(), m_pTexture->GetNumFrames() );
2002-05-20 08:59:37 +00:00
m_fDelay[i] = 0.2f;
if( !ini.GetValueF( "Sprite", sDelayKey, m_fDelay[i] ) )
break;
2001-11-03 10:52:42 +00:00
if( m_iStateToFrame[i] == 0 && m_fDelay[i] > -0.00001f && m_fDelay[i] < 0.00001f ) // both values are empty
2001-11-03 10:52:42 +00:00
break;
m_iNumStates = i+1;
2001-11-03 10:52:42 +00:00
}
if( m_iNumStates == 0 )
2002-01-16 10:01:32 +00:00
{
m_iNumStates = 1;
m_iStateToFrame[0] = 0;
2002-01-16 10:01:32 +00:00
m_fDelay[0] = 10;
}
2001-11-03 10:52:42 +00:00
float f;
2003-04-02 07:24:40 +00:00
if( ini.GetValueF( "Sprite", "BaseRotationXDegrees", f ) ) Actor::SetBaseRotationX( f );
if( ini.GetValueF( "Sprite", "BaseRotationYDegrees", f ) ) Actor::SetBaseRotationY( f );
if( ini.GetValueF( "Sprite", "BaseRotationZDegrees", f ) ) Actor::SetBaseRotationZ( f );
if( ini.GetValueF( "Sprite", "BaseZoomX", f ) ) Actor::SetBaseZoomX( f );
if( ini.GetValueF( "Sprite", "BaseZoomY", f ) ) Actor::SetBaseZoomY( f );
if( ini.GetValueF( "Sprite", "BaseZoomZ", f ) ) Actor::SetBaseZoomZ( f );
2002-01-16 10:01:32 +00:00
return true;
2001-11-03 10:52:42 +00:00
}
2002-04-16 17:31:00 +00:00
void Sprite::UnloadTexture()
2001-11-03 10:52:42 +00:00
{
if( m_pTexture != NULL ) // If there was a previous bitmap...
TEXTUREMAN->UnloadTexture( m_pTexture ); // Unload it.
2002-04-16 17:31:00 +00:00
m_pTexture = NULL;
}
void Sprite::EnableAnimation( bool bEnable )
{
Actor::EnableAnimation( bEnable );
if(m_pTexture)
bEnable ? m_pTexture->Play() : m_pTexture->Pause();
}
bool Sprite::LoadFromTexture( RageTextureID ID )
2002-04-16 17:31:00 +00:00
{
if( !m_pTexture || !(m_pTexture->GetID() == ID) )
{
/* Load the texture if it's not already loaded. We still need
* to do the rest, even if it's the same texture, since we need
* to reset Sprite::m_size, etc. */
UnloadTexture();
m_pTexture = TEXTUREMAN->LoadTexture( ID );
}
2001-11-03 10:52:42 +00:00
ASSERT( m_pTexture != NULL );
2001-11-25 04:31:44 +00:00
// the size of the sprite is the size of the image before it was scaled
Sprite::m_size.x = (float)m_pTexture->GetSourceFrameWidth();
Sprite::m_size.y = (float)m_pTexture->GetSourceFrameHeight();
2001-11-03 10:52:42 +00:00
// Assume the frames of this animation play in sequential order with 0.2 second delay.
for( int i=0; i<m_pTexture->GetNumFrames(); i++ )
2001-11-03 10:52:42 +00:00
{
m_iStateToFrame[i] = i;
2001-11-03 10:52:42 +00:00
m_fDelay[i] = 0.1f;
}
2003-02-07 05:48:30 +00:00
m_iNumStates = m_pTexture->GetNumFrames();
2002-08-23 08:26:11 +00:00
return true;
2001-11-03 10:52:42 +00:00
}
void Sprite::Update( float fDeltaTime )
2001-11-03 10:52:42 +00:00
{
Actor::Update( fDeltaTime ); // do tweening
2002-10-16 00:28:01 +00:00
if( !m_bIsAnimating )
return;
2001-11-03 10:52:42 +00:00
// update animation
2002-10-16 00:28:01 +00:00
m_fSecsIntoState += fDeltaTime;
if( m_fSecsIntoState > m_fDelay[m_iCurState] ) // it's time to switch frames
2001-11-03 10:52:42 +00:00
{
2002-10-16 00:28:01 +00:00
// 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;
2001-11-03 10:52:42 +00:00
}
}
2003-04-21 02:45:00 +00:00
static void TexCoordsFromArray(RageVertex *v, const float *f)
{
2003-04-21 02:45:00 +00:00
v[0].t = RageVector2( f[0], f[1] ); // top left
v[1].t = RageVector2( f[2], f[3] ); // bottom left
v[2].t = RageVector2( f[4], f[5] ); // bottom right
v[3].t = RageVector2( f[6], f[7] ); // top right
}
void TexCoordArrayFromRect(float fImageCoords[8], const RectF &rect)
{
fImageCoords[0] = rect.left; fImageCoords[1] = rect.top; // top left
fImageCoords[2] = rect.left; fImageCoords[3] = rect.bottom; // bottom left
fImageCoords[4] = rect.right; fImageCoords[5] = rect.bottom; // bottom right
fImageCoords[6] = rect.right; fImageCoords[7] = rect.top; // top right
}
2001-11-03 10:52:42 +00:00
2002-05-19 01:59:48 +00:00
void Sprite::DrawPrimitives()
2001-11-03 10:52:42 +00:00
{
if( m_pTexture == NULL && !m_bDrawIfTextureNull )
return;
if( m_pTexture && m_pTexture->IsAMovie() && m_pTexture->IsPlaying() )
2003-02-14 06:31:09 +00:00
SDL_Delay( PREFSMAN->m_iMovieDecodeMS ); // let the movie decode a frame
// use m_temp_* variables to draw the object
2002-10-31 06:00:30 +00:00
RectF quadVerticies;
2001-11-25 04:31:44 +00:00
2002-01-16 10:01:32 +00:00
switch( m_HorizAlign )
{
2003-02-17 12:19:42 +00:00
case align_left: quadVerticies.left = 0; quadVerticies.right = m_size.x; break;
case align_center: quadVerticies.left = -m_size.x/2; quadVerticies.right = m_size.x/2; break;
case align_right: quadVerticies.left = -m_size.x; quadVerticies.right = 0; break;
2002-02-11 04:46:31 +00:00
default: ASSERT( false );
2002-01-16 10:01:32 +00:00
}
2001-11-25 04:31:44 +00:00
2002-01-16 10:01:32 +00:00
switch( m_VertAlign )
{
2003-02-17 12:19:42 +00:00
case align_top: quadVerticies.top = 0; quadVerticies.bottom = m_size.y; break;
2002-01-16 10:01:32 +00:00
case align_middle: quadVerticies.top = -m_size.y/2; quadVerticies.bottom = m_size.y/2; break;
2003-02-17 12:19:42 +00:00
case align_bottom: quadVerticies.top = -m_size.y; quadVerticies.bottom = 0; break;
default: ASSERT(0);
2002-01-16 10:01:32 +00:00
}
2001-11-25 04:31:44 +00:00
static RageVertex v[4];
v[0].p = RageVector3( quadVerticies.left, quadVerticies.top, 0 ); // top left
v[1].p = RageVector3( quadVerticies.left, quadVerticies.bottom, 0 ); // bottom left
v[2].p = RageVector3( quadVerticies.right, quadVerticies.bottom, 0 ); // bottom right
v[3].p = RageVector3( quadVerticies.right, quadVerticies.top, 0 ); // top right
2001-11-25 04:31:44 +00:00
DISPLAY->SetTexture( m_pTexture );
2001-11-25 04:31:44 +00:00
// Must call this after setting the texture or else texture
// parameters have no effect.
Actor::SetRenderStates(); // set Actor-specified render states
if( m_pTexture )
2002-01-16 10:01:32 +00:00
{
2003-04-21 02:45:00 +00:00
float TexCoords[8];
GetActiveTexCoords(TexCoords);
TexCoordsFromArray(v, TexCoords);
2002-01-16 10:01:32 +00:00
}
2001-12-28 10:15:59 +00:00
DISPLAY->SetTextureModeModulate();
/* Draw if we're not fully transparent */
2003-03-05 08:48:35 +00:00
if( m_temp.diffuse[0].a > 0 ||
m_temp.diffuse[1].a > 0 ||
m_temp.diffuse[2].a > 0 ||
m_temp.diffuse[3].a > 0 )
2002-01-16 10:01:32 +00:00
{
//////////////////////
// render the shadow
//////////////////////
2002-05-01 19:14:55 +00:00
if( m_bShadow )
2001-12-19 01:50:57 +00:00
{
2002-05-19 01:59:48 +00:00
DISPLAY->PushMatrix();
DISPLAY->TranslateLocal( m_fShadowLength, m_fShadowLength, 0 ); // shift by 5 units
v[0].c = v[1].c = v[2].c = v[3].c = RageColor(0,0,0,0.5f*m_temp.diffuse[0].a); // semi-transparent black
DISPLAY->DrawQuad( v );
2002-05-19 01:59:48 +00:00
DISPLAY->PopMatrix();
2001-12-19 01:50:57 +00:00
}
//////////////////////
2002-01-16 10:01:32 +00:00
// render the diffuse pass
//////////////////////
2003-04-03 22:10:40 +00:00
v[0].c = m_temp.diffuse[0]; // top left
v[1].c = m_temp.diffuse[2]; // bottom left
v[2].c = m_temp.diffuse[3]; // bottom right
v[3].c = m_temp.diffuse[1]; // top right
DISPLAY->DrawQuad( v );
2003-05-04 05:13:30 +00:00
// glEnable(GL_BLEND);
}
2001-11-03 10:52:42 +00:00
2002-01-16 10:01:32 +00:00
//////////////////////
// render the glow pass
2002-01-16 10:01:32 +00:00
//////////////////////
2003-05-04 05:10:51 +00:00
if( m_temp.glow.a > 0.0001f )
2002-01-16 10:01:32 +00:00
{
2003-05-04 22:35:42 +00:00
DISPLAY->SetTextureModeGlow(m_temp.glowmode);
v[0].c = v[1].c = v[2].c = v[3].c = m_temp.glow;
DISPLAY->DrawQuad( v );
2002-01-16 10:01:32 +00:00
}
2001-11-03 10:52:42 +00:00
}
void Sprite::SetState( int iNewState )
2001-11-03 10:52:42 +00:00
{
2003-04-24 06:16:04 +00:00
// This assert will likely trigger if the "missing" theme element graphic
// is loaded in place of a multi-frame sprite. We want to know about these
// problems in debug builds, but they're not fatal.
DEBUG_ASSERT( iNewState >= 0 && iNewState < m_iNumStates );
2002-09-11 05:09:09 +00:00
CLAMP(iNewState, 0, m_iNumStates-1);
m_iCurState = iNewState;
2001-11-03 10:52:42 +00:00
m_fSecsIntoState = 0.0;
}
2002-10-31 06:00:30 +00:00
void Sprite::SetCustomTextureRect( const RectF &new_texcoord_frect )
2001-11-03 10:52:42 +00:00
{
2001-12-28 10:15:59 +00:00
m_bUsingCustomTexCoords = true;
2003-04-21 02:45:00 +00:00
m_bTextureWrapping = true;
TexCoordArrayFromRect(m_CustomTexCoords, new_texcoord_frect);
2001-11-25 04:31:44 +00:00
}
2001-11-03 10:52:42 +00:00
2002-01-29 21:56:14 +00:00
void Sprite::SetCustomTextureCoords( float fTexCoords[8] ) // order: bottom left, top left, bottom right, top right
2001-12-28 10:15:59 +00:00
{
m_bUsingCustomTexCoords = true;
2003-04-21 02:45:00 +00:00
m_bTextureWrapping = true;
2001-12-28 10:15:59 +00:00
for( int i=0; i<8; i++ )
m_CustomTexCoords[i] = fTexCoords[i];
}
2001-11-03 10:52:42 +00:00
2003-04-21 02:45:00 +00:00
void Sprite::GetCustomTextureCoords( float fTexCoordsOut[8] ) const // order: bottom left, top left, bottom right, top right
2002-06-27 17:49:10 +00:00
{
for( int i=0; i<8; i++ )
fTexCoordsOut[i] = m_CustomTexCoords[i];
}
2002-01-29 21:56:14 +00:00
2002-10-31 06:00:30 +00:00
void Sprite::SetCustomImageRect( RectF rectImageCoords )
2002-01-29 21:56:14 +00:00
{
// 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 );
}
2003-02-08 23:45:06 +00:00
const RectF *Sprite::GetCurrentTextureCoordRect() const
{
unsigned int uFrameNo = m_iStateToFrame[m_iCurState];
return m_pTexture->GetTextureCoordRect( uFrameNo );
}
2003-04-21 02:45:00 +00:00
void Sprite::GetCurrentTextureCoords(float fImageCoords[8]) const
{
const RectF *pTexCoordRect = GetCurrentTextureCoordRect();
TexCoordArrayFromRect(fImageCoords, *pTexCoordRect);
}
/* If we're using custom coordinates, return them; otherwise return the coordinates
* for the current state. */
void Sprite::GetActiveTexCoords(float fImageCoords[8]) const
{
if(m_bUsingCustomTexCoords) GetCustomTextureCoords(fImageCoords);
else GetCurrentTextureCoords(fImageCoords);
}
2002-03-31 07:55:25 +00:00
void Sprite::StopUsingCustomCoords()
{
m_bUsingCustomTexCoords = false;
}