2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2001-11-04 19:34:28 +00:00
|
|
|
/*
|
|
|
|
|
-----------------------------------------------------------------------------
|
2002-08-18 16:19:26 +00:00
|
|
|
Class: Sprite
|
2001-11-04 19:34:28 +00:00
|
|
|
|
2002-08-18 16:19:26 +00:00
|
|
|
Desc: See header.
|
2001-11-04 19:34:28 +00:00
|
|
|
|
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-04 19:34:28 +00:00
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
*/
|
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"
|
2002-11-11 04:53:31 +00:00
|
|
|
#include "RageDisplay.h"
|
2003-11-07 20:55:30 +00:00
|
|
|
#include "RageTexture.h"
|
2002-11-11 04:53:31 +00:00
|
|
|
#include "GameConstantsAndTypes.h"
|
2003-02-14 06:31:09 +00:00
|
|
|
#include "SDL_utils.h"
|
2003-10-30 21:58:18 +00:00
|
|
|
#include "ActorUtil.h"
|
2003-12-22 10:30:10 +00:00
|
|
|
#include "arch/ArchHooks/ArchHooks.h"
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
Sprite::Sprite()
|
|
|
|
|
{
|
|
|
|
|
m_pTexture = NULL;
|
2002-11-11 04:53:31 +00:00
|
|
|
m_bDrawIfTextureNull = false;
|
2002-03-30 20:00:13 +00:00
|
|
|
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;
|
2003-07-03 06:38:57 +00:00
|
|
|
|
|
|
|
|
m_fRememberedClipWidth = -1;
|
|
|
|
|
m_fRememberedClipHeight = -1;
|
|
|
|
|
|
|
|
|
|
m_fTexCoordVelocityX = 0;
|
|
|
|
|
m_fTexCoordVelocityY = 0;
|
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()
|
|
|
|
|
{
|
2002-12-30 02:43:52 +00:00
|
|
|
UnloadTexture();
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-03-23 18:52:18 +00:00
|
|
|
bool Sprite::LoadBG( RageTextureID ID )
|
|
|
|
|
{
|
|
|
|
|
ID.iMipMaps = 1;
|
2003-07-22 07:47:27 +00:00
|
|
|
// ID.bDither = true;
|
2003-03-23 18:52:18 +00:00
|
|
|
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
|
2003-02-06 07:32:57 +00:00
|
|
|
// BaseRotationXDegrees=0
|
|
|
|
|
// BaseRotationYDegrees=0
|
|
|
|
|
// BaseRotationZDegrees=0
|
|
|
|
|
// BaseZoomX=1
|
|
|
|
|
// BaseZoomY=1
|
|
|
|
|
// BaseZoomZ=1
|
2002-12-30 02:43:52 +00:00
|
|
|
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
|
|
|
|
2003-12-22 10:30:10 +00:00
|
|
|
retry:
|
|
|
|
|
|
2002-01-16 10:01:32 +00:00
|
|
|
//Init();
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-12-30 02:43:52 +00:00
|
|
|
m_sSpritePath = ID.filename;
|
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 );
|
2002-09-24 03:12:04 +00:00
|
|
|
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
|
|
|
|
2003-10-29 20:46:37 +00:00
|
|
|
// save the path of the real texture
|
|
|
|
|
ID.filename = Dirname(m_sSpritePath) + sTextureFile;
|
2003-05-05 08:26:30 +00:00
|
|
|
{
|
|
|
|
|
vector<CString> asElementPaths;
|
|
|
|
|
GetDirListing( ID.filename + "*", asElementPaths, false, true );
|
|
|
|
|
if(asElementPaths.size() == 0)
|
2003-12-22 10:30:10 +00:00
|
|
|
{
|
|
|
|
|
CString sMessage = ssprintf( "The sprite file '%s' points to a texture '%s' which doesn't exist.", m_sSpritePath.c_str(), ID.filename.c_str() );
|
|
|
|
|
switch( HOOKS->MessageBoxAbortRetryIgnore(sMessage) )
|
|
|
|
|
{
|
|
|
|
|
case ArchHooks::abort:
|
|
|
|
|
RageException::Throw( "Error reading value 'Texture' from %s.", m_sSpritePath.c_str() );
|
|
|
|
|
case ArchHooks::retry:
|
|
|
|
|
goto retry;
|
|
|
|
|
case ArchHooks::ignore:
|
|
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-05-05 08:26:30 +00:00
|
|
|
if(asElementPaths.size() > 1)
|
|
|
|
|
{
|
|
|
|
|
CString message = ssprintf(
|
|
|
|
|
"There is more than one file that matches "
|
2003-12-17 04:50:39 +00:00
|
|
|
"'%s'. Please remove all but one of these matches.",
|
2003-05-05 08:26:30 +00:00
|
|
|
ID.filename.c_str() );
|
|
|
|
|
|
|
|
|
|
RageException::Throw( message );
|
|
|
|
|
}
|
|
|
|
|
ID.filename = asElementPaths[0];
|
|
|
|
|
}
|
2002-09-24 03:12:04 +00:00
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
// Load the texture
|
2002-12-30 02:43:52 +00:00
|
|
|
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.
|
2003-05-22 05:28:37 +00:00
|
|
|
// If the .sprite file doesn't define any states, leave
|
|
|
|
|
// frames and delays created during LoadFromTexture().
|
|
|
|
|
for( int i=0; true; 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 );
|
2003-05-22 05:28:37 +00:00
|
|
|
State newState;
|
|
|
|
|
|
2003-10-02 02:03:29 +00:00
|
|
|
if( !ini.GetValue( "Sprite", sFrameKey, newState.iFrameIndex ) )
|
2002-05-20 08:59:37 +00:00
|
|
|
break;
|
2003-05-22 05:28:37 +00:00
|
|
|
if( newState.iFrameIndex >= 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-05-22 05:28:37 +00:00
|
|
|
m_sSpritePath.c_str(), sFrameKey.c_str(), newState.iFrameIndex, ID.filename.c_str(), m_pTexture->GetNumFrames() );
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-10-02 02:03:29 +00:00
|
|
|
if( !ini.GetValue( "Sprite", sDelayKey, newState.fDelay ) )
|
2001-11-03 10:52:42 +00:00
|
|
|
break;
|
|
|
|
|
|
2003-05-22 05:28:37 +00:00
|
|
|
if( i == 0 ) // the ini file defines at least one frame
|
|
|
|
|
m_States.clear(); // clear before adding
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-05-22 05:28:37 +00:00
|
|
|
m_States.push_back( newState );
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-02-06 07:32:57 +00:00
|
|
|
float f;
|
2003-10-02 02:03:29 +00:00
|
|
|
if( ini.GetValue( "Sprite", "BaseRotationXDegrees", f ) ) Actor::SetBaseRotationX( f );
|
|
|
|
|
if( ini.GetValue( "Sprite", "BaseRotationYDegrees", f ) ) Actor::SetBaseRotationY( f );
|
|
|
|
|
if( ini.GetValue( "Sprite", "BaseRotationZDegrees", f ) ) Actor::SetBaseRotationZ( f );
|
|
|
|
|
if( ini.GetValue( "Sprite", "BaseZoomX", f ) ) Actor::SetBaseZoomX( f );
|
|
|
|
|
if( ini.GetValue( "Sprite", "BaseZoomY", f ) ) Actor::SetBaseZoomY( f );
|
|
|
|
|
if( ini.GetValue( "Sprite", "BaseZoomZ", f ) ) Actor::SetBaseZoomZ( f );
|
2003-02-06 07:32:57 +00:00
|
|
|
|
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
|
|
|
{
|
2002-03-30 20:00:13 +00:00
|
|
|
if( m_pTexture != NULL ) // If there was a previous bitmap...
|
2002-12-30 02:43:52 +00:00
|
|
|
TEXTUREMAN->UnloadTexture( m_pTexture ); // Unload it.
|
2002-04-16 17:31:00 +00:00
|
|
|
m_pTexture = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-23 18:52:18 +00:00
|
|
|
void Sprite::EnableAnimation( bool bEnable )
|
|
|
|
|
{
|
|
|
|
|
Actor::EnableAnimation( bEnable );
|
|
|
|
|
if(m_pTexture)
|
|
|
|
|
bEnable ? m_pTexture->Play() : m_pTexture->Pause();
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-30 02:43:52 +00:00
|
|
|
bool Sprite::LoadFromTexture( RageTextureID ID )
|
2002-04-16 17:31:00 +00:00
|
|
|
{
|
2003-07-22 07:47:27 +00:00
|
|
|
LOG->Trace( "Sprite::LoadFromTexture( %s )", ID.filename.c_str() );
|
|
|
|
|
|
2003-03-16 00:45:06 +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 );
|
2003-05-22 05:28:37 +00:00
|
|
|
ASSERT( m_pTexture->GetTextureWidth() >= 0 );
|
|
|
|
|
ASSERT( m_pTexture->GetTextureHeight() >= 0 );
|
2003-03-16 00:45:06 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-12-30 02:43:52 +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
|
2002-09-02 21:59:58 +00:00
|
|
|
Sprite::m_size.x = (float)m_pTexture->GetSourceFrameWidth();
|
|
|
|
|
Sprite::m_size.y = (float)m_pTexture->GetSourceFrameHeight();
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-12-22 06:22:49 +00:00
|
|
|
// Assume the frames of this animation play in sequential order with 0.1 second delay.
|
2003-05-22 05:28:37 +00:00
|
|
|
m_States.clear();
|
2002-03-30 20:00:13 +00:00
|
|
|
for( int i=0; i<m_pTexture->GetNumFrames(); i++ )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2003-05-22 05:28:37 +00:00
|
|
|
State newState = { i, 0.1f };
|
|
|
|
|
m_States.push_back( newState );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
2003-02-07 05:48:30 +00:00
|
|
|
|
2003-07-03 06:38:57 +00:00
|
|
|
// apply clipping (if any)
|
|
|
|
|
if( m_fRememberedClipWidth != -1 && m_fRememberedClipHeight != -1 )
|
|
|
|
|
ScaleToClipped( m_fRememberedClipWidth, m_fRememberedClipHeight );
|
|
|
|
|
|
2002-08-23 08:26:11 +00:00
|
|
|
return true;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-12-22 10:30:10 +00:00
|
|
|
void Sprite::UpdateAnimationState()
|
|
|
|
|
{
|
|
|
|
|
// Don't bother with state switching logic if there's only one state.
|
|
|
|
|
// We already know what's going to show.
|
|
|
|
|
if( m_States.size() > 1 )
|
|
|
|
|
{
|
|
|
|
|
while( m_fSecsIntoState > m_States[m_iCurState].fDelay ) // it's time to switch frames
|
|
|
|
|
{
|
|
|
|
|
// increment frame and reset the counter
|
|
|
|
|
m_fSecsIntoState -= m_States[m_iCurState].fDelay; // leave the left over time for the next frame
|
|
|
|
|
m_iCurState = (m_iCurState+1) % m_States.size();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-07-03 06:38:57 +00:00
|
|
|
void Sprite::Update( float fDelta )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2003-07-03 06:38:57 +00:00
|
|
|
Actor::Update( fDelta ); // do tweening
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-10-16 00:28:01 +00:00
|
|
|
if( !m_bIsAnimating )
|
|
|
|
|
return;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-05-22 05:28:37 +00:00
|
|
|
if( !m_pTexture ) // no texture, nothing to animate
|
|
|
|
|
return;
|
|
|
|
|
|
2003-07-03 06:38:57 +00:00
|
|
|
m_fSecsIntoState += fDelta;
|
2003-12-22 10:30:10 +00:00
|
|
|
UpdateAnimationState();
|
2003-07-03 06:38:57 +00:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// update scrolling
|
|
|
|
|
//
|
|
|
|
|
if( m_fTexCoordVelocityX != 0 || m_fTexCoordVelocityY != 0 )
|
|
|
|
|
{
|
|
|
|
|
float fTexCoords[8];
|
|
|
|
|
Sprite::GetActiveTextureCoords( fTexCoords );
|
|
|
|
|
|
|
|
|
|
// top left, bottom left, bottom right, top right
|
|
|
|
|
fTexCoords[0] += fDelta*m_fTexCoordVelocityX;
|
|
|
|
|
fTexCoords[1] += fDelta*m_fTexCoordVelocityY;
|
|
|
|
|
fTexCoords[2] += fDelta*m_fTexCoordVelocityX;
|
|
|
|
|
fTexCoords[3] += fDelta*m_fTexCoordVelocityY;
|
|
|
|
|
fTexCoords[4] += fDelta*m_fTexCoordVelocityX;
|
|
|
|
|
fTexCoords[5] += fDelta*m_fTexCoordVelocityY;
|
|
|
|
|
fTexCoords[6] += fDelta*m_fTexCoordVelocityX;
|
|
|
|
|
fTexCoords[7] += fDelta*m_fTexCoordVelocityY;
|
|
|
|
|
|
|
|
|
|
Sprite::SetCustomTextureCoords( fTexCoords );
|
|
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-07 01:29:18 +00:00
|
|
|
static void TexCoordsFromArray(RageSpriteVertex *v, const float *f)
|
2003-04-21 01:35:37 +00:00
|
|
|
{
|
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
|
2003-04-21 01:35:37 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-11-08 19:38:38 +00:00
|
|
|
void Sprite::DrawTexture( const TweenState *state )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2003-06-23 04:49:28 +00:00
|
|
|
// bail if cropped all the way
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->crop.left + state->crop.right >= 1 ||
|
|
|
|
|
state->crop.top + state->crop.bottom >= 1 )
|
2003-06-23 04:49:28 +00:00
|
|
|
return;
|
|
|
|
|
|
2002-03-30 20:00:13 +00:00
|
|
|
// 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 )
|
2001-12-11 11:25:37 +00:00
|
|
|
{
|
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-07-09 02:27:05 +00:00
|
|
|
// FIXME: Top and bottom are flipped, but changing them now breaks a lot
|
|
|
|
|
// in our themes. -Chris
|
2003-10-16 06:50:53 +00:00
|
|
|
// Looks correct to me? -glenn
|
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;
|
2003-05-15 06:09:19 +00:00
|
|
|
default: ASSERT(0);
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
2001-11-25 04:31:44 +00:00
|
|
|
|
|
|
|
|
|
2003-06-23 04:49:28 +00:00
|
|
|
RectF croppedQuadVerticies = quadVerticies;
|
2003-11-08 19:38:38 +00:00
|
|
|
#define IF_CROP_POS(side,opp_side) \
|
|
|
|
|
if(state->crop.side>0) \
|
|
|
|
|
croppedQuadVerticies.side = \
|
|
|
|
|
SCALE( state->crop.side, 0.f, 1.f, quadVerticies.side, quadVerticies.opp_side );
|
2003-06-23 04:49:28 +00:00
|
|
|
IF_CROP_POS( left, right );
|
|
|
|
|
IF_CROP_POS( top, bottom );
|
|
|
|
|
IF_CROP_POS( right, left );
|
|
|
|
|
IF_CROP_POS( bottom, top );
|
|
|
|
|
|
2003-07-07 01:29:18 +00:00
|
|
|
static RageSpriteVertex v[4];
|
2003-06-20 23:04:11 +00:00
|
|
|
v[0].p = RageVector3( croppedQuadVerticies.left, croppedQuadVerticies.top, 0 ); // top left
|
|
|
|
|
v[1].p = RageVector3( croppedQuadVerticies.left, croppedQuadVerticies.bottom, 0 ); // bottom left
|
|
|
|
|
v[2].p = RageVector3( croppedQuadVerticies.right, croppedQuadVerticies.bottom, 0 ); // bottom right
|
|
|
|
|
v[3].p = RageVector3( croppedQuadVerticies.right, croppedQuadVerticies.top, 0 ); // top right
|
2001-11-25 04:31:44 +00:00
|
|
|
|
2002-11-21 22:25:00 +00:00
|
|
|
DISPLAY->SetTexture( m_pTexture );
|
2001-11-25 04:31:44 +00:00
|
|
|
|
2003-05-15 06:09:19 +00:00
|
|
|
// Must call this after setting the texture or else texture
|
|
|
|
|
// parameters have no effect.
|
|
|
|
|
Actor::SetRenderStates(); // set Actor-specified render states
|
|
|
|
|
|
2002-11-11 04:53:31 +00:00
|
|
|
if( m_pTexture )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
2003-06-23 04:49:28 +00:00
|
|
|
float f[8];
|
2003-07-03 06:38:57 +00:00
|
|
|
GetActiveTextureCoords(f);
|
2003-06-23 04:49:28 +00:00
|
|
|
TexCoordsFromArray(v, f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RageVector2 texCoords[4] = {
|
|
|
|
|
RageVector2( f[0], f[1] ), // top left
|
|
|
|
|
RageVector2( f[2], f[3] ), // bottom left
|
|
|
|
|
RageVector2( f[4], f[5] ), // bottom right
|
|
|
|
|
RageVector2( f[6], f[7] ) // top right
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->crop.left>0 )
|
2003-06-23 04:49:28 +00:00
|
|
|
{
|
2003-11-08 19:38:38 +00:00
|
|
|
v[0].t.x = SCALE( state->crop.left, 0.f, 1.f, texCoords[0].x, texCoords[3].x );
|
|
|
|
|
v[1].t.x = SCALE( state->crop.left, 0.f, 1.f, texCoords[1].x, texCoords[2].x );
|
2003-06-23 04:49:28 +00:00
|
|
|
}
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->crop.right>0 )
|
2003-06-23 04:49:28 +00:00
|
|
|
{
|
2003-11-08 19:38:38 +00:00
|
|
|
v[2].t.x = SCALE( state->crop.right, 0.f, 1.f, texCoords[2].x, texCoords[1].x );
|
|
|
|
|
v[3].t.x = SCALE( state->crop.right, 0.f, 1.f, texCoords[3].x, texCoords[0].x );
|
2003-06-23 04:49:28 +00:00
|
|
|
}
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->crop.top>0 )
|
2003-06-23 04:49:28 +00:00
|
|
|
{
|
2003-11-08 19:38:38 +00:00
|
|
|
v[0].t.y = SCALE( state->crop.top, 0.f, 1.f, texCoords[0].y, texCoords[1].y );
|
|
|
|
|
v[3].t.y = SCALE( state->crop.top, 0.f, 1.f, texCoords[3].y, texCoords[2].y );
|
2003-06-23 04:49:28 +00:00
|
|
|
}
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->crop.bottom>0 )
|
2003-06-23 04:49:28 +00:00
|
|
|
{
|
2003-11-08 19:38:38 +00:00
|
|
|
v[1].t.y = SCALE( state->crop.bottom, 0.f, 1.f, texCoords[1].y, texCoords[0].y );
|
|
|
|
|
v[2].t.y = SCALE( state->crop.bottom, 0.f, 1.f, texCoords[2].y, texCoords[3].y );
|
2003-06-23 04:49:28 +00:00
|
|
|
}
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
2003-11-08 19:38:38 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Just make sure we don't throw NaN/INF at the renderer:
|
|
|
|
|
for( unsigned i = 0; i < 4; ++i )
|
|
|
|
|
v[i].t.x = v[i].t.y = 0;
|
|
|
|
|
}
|
2001-12-11 11:25:37 +00:00
|
|
|
|
2003-05-15 06:09:19 +00:00
|
|
|
/* Draw if we're not fully transparent */
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->diffuse[0].a > 0 ||
|
|
|
|
|
state->diffuse[1].a > 0 ||
|
|
|
|
|
state->diffuse[2].a > 0 ||
|
|
|
|
|
state->diffuse[3].a > 0 )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
2003-11-08 19:38:38 +00:00
|
|
|
DISPLAY->SetTextureModeModulate();
|
|
|
|
|
|
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();
|
2003-05-22 05:28:37 +00:00
|
|
|
DISPLAY->TranslateWorld( m_fShadowLength, m_fShadowLength, 0 ); // shift by 5 units
|
2003-11-08 19:38:38 +00:00
|
|
|
v[0].c = v[1].c = v[2].c = v[3].c = RageColor(0,0,0,0.5f*state->diffuse[0].a); // semi-transparent black
|
2002-11-11 04:53:31 +00:00
|
|
|
DISPLAY->DrawQuad( v );
|
2002-05-19 01:59:48 +00:00
|
|
|
DISPLAY->PopMatrix();
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
2001-12-11 11:25:37 +00:00
|
|
|
|
|
|
|
|
//////////////////////
|
2002-01-16 10:01:32 +00:00
|
|
|
// render the diffuse pass
|
2001-12-11 11:25:37 +00:00
|
|
|
//////////////////////
|
2003-11-08 19:38:38 +00:00
|
|
|
v[0].c = state->diffuse[0]; // top left
|
|
|
|
|
v[1].c = state->diffuse[2]; // bottom left
|
|
|
|
|
v[2].c = state->diffuse[3]; // bottom right
|
|
|
|
|
v[3].c = state->diffuse[1]; // top right
|
2002-11-11 04:53:31 +00:00
|
|
|
DISPLAY->DrawQuad( v );
|
2001-12-11 11:25:37 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-01-16 10:01:32 +00:00
|
|
|
//////////////////////
|
2002-08-18 16:19:26 +00:00
|
|
|
// render the glow pass
|
2002-01-16 10:01:32 +00:00
|
|
|
//////////////////////
|
2003-11-08 19:38:38 +00:00
|
|
|
if( state->glow.a > 0.0001f )
|
2002-01-16 10:01:32 +00:00
|
|
|
{
|
2003-11-08 19:38:38 +00:00
|
|
|
DISPLAY->SetTextureModeGlow(state->glowmode);
|
|
|
|
|
v[0].c = v[1].c = v[2].c = v[3].c = state->glow;
|
2002-11-11 04:53:31 +00:00
|
|
|
DISPLAY->DrawQuad( v );
|
2002-01-16 10:01:32 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-11-08 19:38:38 +00:00
|
|
|
static RageColor scale( float x, float l1, float h1, const RageColor &a, const RageColor &b )
|
|
|
|
|
{
|
|
|
|
|
return RageColor(
|
|
|
|
|
SCALE( x, l1, h1, a.r, b.r ),
|
|
|
|
|
SCALE( x, l1, h1, a.g, b.g ),
|
|
|
|
|
SCALE( x, l1, h1, a.b, b.b ),
|
|
|
|
|
SCALE( x, l1, h1, a.a, b.a ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Sprite::DrawPrimitives()
|
|
|
|
|
{
|
|
|
|
|
if( m_pTexture == NULL && !m_bDrawIfTextureNull )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if( m_pTempState->fade.top > 0 ||
|
|
|
|
|
m_pTempState->fade.bottom > 0 ||
|
|
|
|
|
m_pTempState->fade.left > 0 ||
|
|
|
|
|
m_pTempState->fade.right > 0 )
|
|
|
|
|
{
|
|
|
|
|
/* We're fading the edges. */
|
|
|
|
|
const RectF &FadeDist = m_pTempState->fade;
|
|
|
|
|
|
|
|
|
|
/* Actual size of the fade on each side: */
|
|
|
|
|
RectF FadeSize = FadeDist;
|
|
|
|
|
|
|
|
|
|
/* If the cropped size is less than the fade distance in either dimension, clamp. */
|
|
|
|
|
const float HorizRemaining = 1.0f - (m_pTempState->crop.left + m_pTempState->crop.right);
|
|
|
|
|
if( FadeDist.left+FadeDist.right > 0 &&
|
|
|
|
|
HorizRemaining < FadeDist.left+FadeDist.right )
|
|
|
|
|
{
|
|
|
|
|
const float LeftPercent = FadeDist.left/(FadeDist.left+FadeDist.right);
|
|
|
|
|
FadeSize.left = LeftPercent * HorizRemaining;
|
|
|
|
|
FadeSize.right = (1.0f-LeftPercent) * HorizRemaining;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float VertRemaining = 1.0f - (m_pTempState->crop.top + m_pTempState->crop.bottom);
|
|
|
|
|
if( FadeDist.top+FadeDist.bottom > 0 &&
|
|
|
|
|
VertRemaining < FadeDist.top+FadeDist.bottom )
|
|
|
|
|
{
|
|
|
|
|
const float TopPercent = FadeDist.top/(FadeDist.top+FadeDist.bottom);
|
|
|
|
|
FadeSize.top = TopPercent * VertRemaining;
|
|
|
|
|
FadeSize.bottom = (1.0f-TopPercent) * VertRemaining;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RageColor &FadeColor = m_pTempState->fadecolor;
|
|
|
|
|
|
|
|
|
|
/* Alpha value of the un-faded side of each fade rect: */
|
|
|
|
|
const RageColor RightColor = scale( FadeSize.right, FadeDist.right, 0, RageColor(1,1,1,1), FadeColor );
|
|
|
|
|
const RageColor LeftColor = scale( FadeSize.left, FadeDist.left, 0, RageColor(1,1,1,1), FadeColor );
|
|
|
|
|
const RageColor TopColor = scale( FadeSize.top, FadeDist.top, 0, RageColor(1,1,1,1), FadeColor );
|
|
|
|
|
const RageColor BottomColor = scale( FadeSize.bottom, FadeDist.bottom, 0, RageColor(1,1,1,1), FadeColor );
|
|
|
|
|
|
|
|
|
|
/* Draw the inside: */
|
|
|
|
|
TweenState ts = *m_pTempState;
|
|
|
|
|
ts.crop.left += FadeDist.left;
|
|
|
|
|
ts.crop.right += FadeDist.right;
|
|
|
|
|
ts.crop.top += FadeDist.top;
|
|
|
|
|
ts.crop.bottom += FadeDist.bottom;
|
|
|
|
|
DrawTexture( &ts );
|
|
|
|
|
|
|
|
|
|
if( FadeSize.left > 0.001f )
|
|
|
|
|
{
|
|
|
|
|
/* Draw the left: */
|
|
|
|
|
ts.crop = m_pTempState->crop; // restore
|
|
|
|
|
memcpy( ts.diffuse, m_pTempState->diffuse, sizeof(ts.diffuse) ); // restore
|
|
|
|
|
|
|
|
|
|
ts.crop.right = 1 - (ts.crop.left + FadeSize.left);
|
|
|
|
|
ts.crop.top += FadeDist.top; // lop off the corner if fading both x and y
|
|
|
|
|
ts.crop.bottom += FadeDist.bottom;
|
|
|
|
|
ts.diffuse[0] *= FadeColor; // top left
|
|
|
|
|
ts.diffuse[2] *= FadeColor; // bottom left
|
|
|
|
|
ts.diffuse[3] *= LeftColor; // bottom right
|
|
|
|
|
ts.diffuse[1] *= LeftColor; // top right
|
|
|
|
|
DrawTexture( &ts );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FadeSize.right > 0.001f )
|
|
|
|
|
{
|
|
|
|
|
/* Draw the right: */
|
|
|
|
|
ts.crop = m_pTempState->crop; // restore
|
|
|
|
|
memcpy( ts.diffuse, m_pTempState->diffuse, sizeof(ts.diffuse) ); // restore
|
|
|
|
|
|
|
|
|
|
ts.crop.left = 1 - (ts.crop.right + FadeSize.right);
|
|
|
|
|
ts.crop.top += FadeDist.top;
|
|
|
|
|
ts.crop.bottom += FadeDist.bottom;
|
|
|
|
|
ts.diffuse[0] *= RightColor; // top left
|
|
|
|
|
ts.diffuse[2] *= RightColor; // bottom left
|
|
|
|
|
ts.diffuse[3] *= FadeColor; // bottom right
|
|
|
|
|
ts.diffuse[1] *= FadeColor; // top right
|
|
|
|
|
DrawTexture( &ts );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FadeSize.top > 0.001f )
|
|
|
|
|
{
|
|
|
|
|
/* Draw the top: */
|
|
|
|
|
ts.crop = m_pTempState->crop; // restore
|
|
|
|
|
memcpy( ts.diffuse, m_pTempState->diffuse, sizeof(ts.diffuse) ); // restore
|
|
|
|
|
|
|
|
|
|
ts.crop.bottom = 1 - (ts.crop.top + FadeSize.top);
|
|
|
|
|
ts.crop.left += FadeDist.left;
|
|
|
|
|
ts.crop.right += FadeDist.right;
|
|
|
|
|
ts.diffuse[0] *= FadeColor; // top left
|
|
|
|
|
ts.diffuse[2] *= TopColor; // bottom left
|
|
|
|
|
ts.diffuse[3] *= TopColor; // bottom right
|
|
|
|
|
ts.diffuse[1] *= FadeColor; // top right
|
|
|
|
|
DrawTexture( &ts );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FadeSize.bottom > 0.001f )
|
|
|
|
|
{
|
|
|
|
|
/* Draw the bottom: */
|
|
|
|
|
ts.crop = m_pTempState->crop; // restore
|
|
|
|
|
memcpy( ts.diffuse, m_pTempState->diffuse, sizeof(ts.diffuse) ); // restore
|
|
|
|
|
|
|
|
|
|
ts.crop.top = 1 - (ts.crop.bottom + FadeSize.bottom);
|
|
|
|
|
ts.crop.left += FadeDist.left;
|
|
|
|
|
ts.crop.right += FadeDist.right;
|
|
|
|
|
ts.diffuse[0] *= BottomColor; // top left
|
|
|
|
|
ts.diffuse[2] *= FadeColor; // bottom left
|
|
|
|
|
ts.diffuse[3] *= FadeColor; // bottom right
|
|
|
|
|
ts.diffuse[1] *= BottomColor; // top right
|
|
|
|
|
DrawTexture( &ts );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Not yet sure how to compute the inner diffuse color. */
|
|
|
|
|
if( FadeSize.top > 0.001f && FadeSize.left > 0.001f )
|
|
|
|
|
{
|
|
|
|
|
/* Draw the top-left: */
|
|
|
|
|
ts.crop = m_pTempState->crop; // restore
|
|
|
|
|
memcpy( ts.diffuse, m_pTempState->diffuse, sizeof(ts.diffuse) ); // restore
|
|
|
|
|
|
|
|
|
|
ts.crop.right = 1 - (ts.crop.left + FadeSize.left);
|
|
|
|
|
ts.crop.bottom = 1 - (ts.crop.top + FadeSize.top);
|
|
|
|
|
|
|
|
|
|
ts.diffuse[0] *= FadeColor; // top left
|
|
|
|
|
ts.diffuse[2] *= FadeColor; // bottom left
|
|
|
|
|
// XXX?
|
|
|
|
|
ts.diffuse[3] *= (TopColor+LeftColor) * 0.5f; // bottom right
|
|
|
|
|
ts.diffuse[1] *= FadeColor; // top right
|
|
|
|
|
DrawTexture( &ts );
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DrawTexture( m_pTempState );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-03-30 20:00:13 +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.
|
2003-05-22 05:28:37 +00:00
|
|
|
DEBUG_ASSERT( iNewState >= 0 && iNewState < (int)m_States.size() );
|
2003-04-24 06:16:04 +00:00
|
|
|
|
2003-05-22 05:28:37 +00:00
|
|
|
CLAMP(iNewState, 0, (int)m_States.size()-1);
|
2002-03-30 20:00:13 +00:00
|
|
|
m_iCurState = iNewState;
|
2001-11-03 10:52:42 +00:00
|
|
|
m_fSecsIntoState = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-22 10:30:10 +00:00
|
|
|
void Sprite::SetSecondsIntoAnimation( float fSeconds )
|
|
|
|
|
{
|
|
|
|
|
SetState( 0 ); // rewind to the first state
|
|
|
|
|
m_fSecsIntoState = fSeconds;
|
|
|
|
|
UpdateAnimationState();
|
|
|
|
|
}
|
|
|
|
|
|
2003-11-07 20:55:30 +00:00
|
|
|
CString Sprite::GetTexturePath() const
|
|
|
|
|
{
|
|
|
|
|
if( m_pTexture==NULL )
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
|
|
return m_pTexture->GetID().filename;
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-23 04:49:28 +00:00
|
|
|
void Sprite::SetCustomTextureRect( const RectF &new_texcoord_frect )
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
|
|
|
|
m_bUsingCustomTexCoords = true;
|
2003-04-21 02:45:00 +00:00
|
|
|
m_bTextureWrapping = true;
|
2003-06-23 04:49:28 +00:00
|
|
|
TexCoordArrayFromRect(m_CustomTexCoords, new_texcoord_frect);
|
2001-12-28 10:15:59 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-06-23 04:49:28 +00:00
|
|
|
void Sprite::SetCustomTextureCoords( float fTexCoords[8] ) // order: top left, bottom left, bottom right, top right
|
2002-06-27 17:49:10 +00:00
|
|
|
{
|
2003-06-23 04:49:28 +00:00
|
|
|
m_bUsingCustomTexCoords = true;
|
|
|
|
|
m_bTextureWrapping = true;
|
|
|
|
|
for( int i=0; i<8; i++ )
|
|
|
|
|
m_CustomTexCoords[i] = fTexCoords[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Sprite::SetCustomImageRect( RectF rectImageCoords )
|
2002-01-29 21:56:14 +00:00
|
|
|
{
|
|
|
|
|
// Convert to a rectangle in texture coordinate space.
|
2003-06-23 04:49:28 +00:00
|
|
|
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();
|
2002-01-29 21:56:14 +00:00
|
|
|
|
2003-06-23 04:49:28 +00:00
|
|
|
SetCustomTextureRect( rectImageCoords );
|
2002-01-29 21:56:14 +00:00
|
|
|
}
|
|
|
|
|
|
2003-06-23 04:49:28 +00:00
|
|
|
void Sprite::SetCustomImageCoords( float fImageCoords[8] ) // order: top left, bottom 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 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RectF *Sprite::GetCurrentTextureCoordRect() const
|
2003-02-08 23:45:06 +00:00
|
|
|
{
|
2003-05-22 05:28:37 +00:00
|
|
|
unsigned int uFrameNo = m_States[m_iCurState].iFrameIndex;
|
2003-02-08 23:45:06 +00:00
|
|
|
return m_pTexture->GetTextureCoordRect( uFrameNo );
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-23 04:49:28 +00:00
|
|
|
|
2003-04-21 02:45:00 +00:00
|
|
|
/* If we're using custom coordinates, return them; otherwise return the coordinates
|
|
|
|
|
* for the current state. */
|
2003-07-03 06:38:57 +00:00
|
|
|
void Sprite::GetActiveTextureCoords(float fTexCoordsOut[8]) const
|
2003-04-21 02:45:00 +00:00
|
|
|
{
|
2003-07-03 06:38:57 +00:00
|
|
|
if(m_bUsingCustomTexCoords)
|
|
|
|
|
{
|
|
|
|
|
// GetCustomTextureCoords
|
|
|
|
|
for( int i=0; i<8; i++ )
|
|
|
|
|
fTexCoordsOut[i] = m_CustomTexCoords[i];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// GetCurrentTextureCoords
|
|
|
|
|
const RectF *pTexCoordRect = GetCurrentTextureCoordRect();
|
|
|
|
|
TexCoordArrayFromRect(fTexCoordsOut, *pTexCoordRect);
|
|
|
|
|
}
|
2003-04-21 02:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-03-31 07:55:25 +00:00
|
|
|
void Sprite::StopUsingCustomCoords()
|
|
|
|
|
{
|
|
|
|
|
m_bUsingCustomTexCoords = false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-03 06:38:57 +00:00
|
|
|
|
|
|
|
|
void Sprite::ScaleToClipped( float fWidth, float fHeight )
|
|
|
|
|
{
|
|
|
|
|
m_fRememberedClipWidth = fWidth;
|
|
|
|
|
m_fRememberedClipHeight = fHeight;
|
|
|
|
|
|
|
|
|
|
if( !m_pTexture )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int iSourceWidth = m_pTexture->GetSourceWidth();
|
|
|
|
|
int iSourceHeight = m_pTexture->GetSourceHeight();
|
|
|
|
|
|
|
|
|
|
// save the original X&Y. We're going to resore them later.
|
|
|
|
|
float fOriginalX = GetX();
|
|
|
|
|
float fOriginalY = GetY();
|
|
|
|
|
|
|
|
|
|
if( IsDiagonalBanner(iSourceWidth, iSourceHeight) ) // this is a SSR/DWI CroppedSprite
|
|
|
|
|
{
|
|
|
|
|
float fCustomImageCoords[8] = {
|
|
|
|
|
0.02f, 0.78f, // top left
|
|
|
|
|
0.22f, 0.98f, // bottom left
|
|
|
|
|
0.98f, 0.22f, // bottom right
|
|
|
|
|
0.78f, 0.02f, // top right
|
|
|
|
|
};
|
|
|
|
|
Sprite::SetCustomImageCoords( fCustomImageCoords );
|
|
|
|
|
|
|
|
|
|
if( fWidth != -1 && fHeight != -1)
|
|
|
|
|
m_size = RageVector2( fWidth, fHeight );
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If no crop size is set, then we're only being used to crop diagonal
|
|
|
|
|
* banners so they look like regular ones. We don't actually care about
|
|
|
|
|
* the size of the image, only that it has an aspect ratio of 4:1. */
|
|
|
|
|
m_size = RageVector2(256, 64);
|
|
|
|
|
}
|
|
|
|
|
SetZoom( 1 );
|
|
|
|
|
}
|
|
|
|
|
else if( m_pTexture->GetID().filename.find( "(was rotated)" ) != m_pTexture->GetID().filename.npos &&
|
|
|
|
|
fWidth != -1 && fHeight != -1 )
|
|
|
|
|
{
|
|
|
|
|
/* Dumb hack. Normally, we crop all sprites except for diagonal banners,
|
|
|
|
|
* which are stretched. Low-res versions of banners need to do the same
|
|
|
|
|
* thing as their full resolution counterpart, so the crossfade looks right.
|
|
|
|
|
* However, low-res diagonal banners are un-rotated, to save space. BannerCache
|
|
|
|
|
* drops the above text into the "filename" (which is otherwise unused for
|
|
|
|
|
* these banners) to tell us this.
|
|
|
|
|
*/
|
|
|
|
|
Sprite::StopUsingCustomCoords();
|
|
|
|
|
m_size = RageVector2( fWidth, fHeight );
|
|
|
|
|
SetZoom( 1 );
|
|
|
|
|
}
|
|
|
|
|
else if( fWidth != -1 && fHeight != -1 )
|
|
|
|
|
{
|
|
|
|
|
// this is probably a background graphic or something not intended to be a CroppedSprite
|
|
|
|
|
Sprite::StopUsingCustomCoords();
|
|
|
|
|
|
|
|
|
|
// first find the correct zoom
|
|
|
|
|
Sprite::ScaleToCover( RectI(0, 0,
|
|
|
|
|
(int)fWidth,
|
|
|
|
|
(int)fHeight )
|
|
|
|
|
);
|
|
|
|
|
// find which dimension is larger
|
|
|
|
|
bool bXDimNeedsToBeCropped = GetZoomedWidth() > fWidth+0.01;
|
|
|
|
|
|
|
|
|
|
if( bXDimNeedsToBeCropped ) // crop X
|
|
|
|
|
{
|
|
|
|
|
float fPercentageToCutOff = (this->GetZoomedWidth() - fWidth) / this->GetZoomedWidth();
|
|
|
|
|
float fPercentageToCutOffEachSide = fPercentageToCutOff / 2;
|
|
|
|
|
|
|
|
|
|
// generate a rectangle with new texture coordinates
|
|
|
|
|
RectF fCustomImageRect(
|
|
|
|
|
fPercentageToCutOffEachSide,
|
|
|
|
|
0,
|
|
|
|
|
1 - fPercentageToCutOffEachSide,
|
|
|
|
|
1 );
|
|
|
|
|
SetCustomImageRect( fCustomImageRect );
|
|
|
|
|
}
|
|
|
|
|
else // crop Y
|
|
|
|
|
{
|
|
|
|
|
float fPercentageToCutOff = (this->GetZoomedHeight() - fHeight) / this->GetZoomedHeight();
|
|
|
|
|
float fPercentageToCutOffEachSide = fPercentageToCutOff / 2;
|
|
|
|
|
|
|
|
|
|
// generate a rectangle with new texture coordinates
|
|
|
|
|
RectF fCustomImageRect(
|
|
|
|
|
0,
|
|
|
|
|
fPercentageToCutOffEachSide,
|
|
|
|
|
1,
|
|
|
|
|
1 - fPercentageToCutOffEachSide );
|
|
|
|
|
SetCustomImageRect( fCustomImageRect );
|
|
|
|
|
}
|
|
|
|
|
m_size = RageVector2( fWidth, fHeight );
|
|
|
|
|
SetZoom( 1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// restore original XY
|
|
|
|
|
SetXY( fOriginalX, fOriginalY );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Sprite::IsDiagonalBanner( int iWidth, int iHeight )
|
|
|
|
|
{
|
|
|
|
|
/* A diagonal banner is a square. Give a couple pixels of leeway. */
|
|
|
|
|
return iWidth >= 100 && abs(iWidth - iHeight) < 2;
|
|
|
|
|
}
|
2003-07-07 01:29:18 +00:00
|
|
|
|
2003-10-30 22:56:45 +00:00
|
|
|
void Sprite::StretchTexCoords( float fX, float fY )
|
|
|
|
|
{
|
|
|
|
|
float fTexCoords[8];
|
|
|
|
|
GetActiveTextureCoords( fTexCoords );
|
|
|
|
|
|
|
|
|
|
for( int j=0; j<8; j+=2 )
|
|
|
|
|
{
|
|
|
|
|
fTexCoords[j ] += fX;
|
|
|
|
|
fTexCoords[j+1] += fY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetCustomTextureCoords( fTexCoords );
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-10 20:34:18 +00:00
|
|
|
void Sprite::HandleCommand( const ParsedCommand &command )
|
2003-07-07 01:29:18 +00:00
|
|
|
{
|
2003-10-30 21:58:18 +00:00
|
|
|
HandleParams;
|
2003-07-07 01:29:18 +00:00
|
|
|
|
2004-01-10 20:34:18 +00:00
|
|
|
const CString& sName = sParam(0);
|
2003-07-07 01:29:18 +00:00
|
|
|
|
|
|
|
|
// Commands that go in the tweening queue:
|
|
|
|
|
// Commands that take effect immediately (ignoring the tweening queue):
|
|
|
|
|
if( sName=="customtexturerect" ) SetCustomTextureRect( RectF(fParam(1),fParam(2),fParam(3),fParam(4)) );
|
|
|
|
|
else if( sName=="texcoordvelocity" ) SetTexCoordVelocity( fParam(1),fParam(2) );
|
|
|
|
|
else if( sName=="scaletoclipped" ) ScaleToClipped( fParam(1),fParam(2) );
|
2003-10-30 22:56:45 +00:00
|
|
|
else if( sName=="stretchtexcoords" ) StretchTexCoords( fParam(1),fParam(2) );
|
2003-10-30 21:58:18 +00:00
|
|
|
|
|
|
|
|
/* Texture commands; these could be moved to RageTexture* (even though that's
|
|
|
|
|
* not an Actor) if these are needed for other things that use textures.
|
|
|
|
|
* We'd need to break the command helpers into a separate function; RageTexture
|
|
|
|
|
* shouldn't depend on Actor. */
|
|
|
|
|
else if( sName=="position" ) GetTexture()->SetPosition( fParam(1) );
|
|
|
|
|
else if( sName=="loop" ) GetTexture()->SetLooping( bParam(1) );
|
|
|
|
|
else if( sName=="play" ) GetTexture()->Play();
|
|
|
|
|
else if( sName=="pause" ) GetTexture()->Pause();
|
|
|
|
|
else if( sName=="rate" ) GetTexture()->SetPlaybackRate( fParam(1) );
|
2003-07-07 01:29:18 +00:00
|
|
|
else
|
|
|
|
|
{
|
2004-01-10 20:34:18 +00:00
|
|
|
Actor::HandleCommand( command );
|
2003-07-07 01:29:18 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2003-10-30 21:58:18 +00:00
|
|
|
CheckHandledParams;
|
2003-07-07 01:29:18 +00:00
|
|
|
}
|