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"
#include "GameConstantsAndTypes.h"
2003-02-14 06:31:09 +00:00
#include "SDL_utils.h"
2003-07-11 03:15:28 +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
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
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 );
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
2002-12-30 02:43:52 +00:00
ID . filename = sFontDir + sTextureFile ; // save the path of the real texture
2003-05-05 08:26:30 +00:00
{
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 () );
2002-09-24 03:12:04 +00:00
2003-05-05 08:26:30 +00:00
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 ;
if ( ! ini . GetValueI ( "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 () );
if ( ! ini . GetValueF ( "Sprite" , sDelayKey , newState . fDelay ) )
2002-05-20 08:59:37 +00:00
break ;
2001-11-03 10:52:42 +00:00
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-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 );
2003-02-06 07:32:57 +00:00
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
{
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
}
2003-03-10 00:16:49 +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
// Assume the frames of this animation play in sequential order with 0.2 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-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
//
// update animation frame
//
m_fSecsIntoState += fDelta ;
2002-10-16 00:28:01 +00:00
2003-05-22 05:28:37 +00:00
while ( m_fSecsIntoState > m_States [ m_iCurState ]. fDelay ) // 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
2003-05-22 05:28:37 +00:00
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
//
// 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
2002-05-19 01:59:48 +00:00
void Sprite :: DrawPrimitives ()
2001-11-03 10:52:42 +00:00
{
2002-11-11 04:53:31 +00:00
if ( m_pTexture == NULL && ! m_bDrawIfTextureNull )
2002-08-18 16:19:26 +00:00
return ;
2003-06-23 04:49:28 +00:00
// bail if cropped all the way
if ( m_temp . crop . left + m_temp . crop . right > 1 ||
2003-06-20 23:04:11 +00:00
m_temp . crop . top + m_temp . crop . bottom > 1 )
2003-06-23 04:49:28 +00:00
return ;
2003-06-20 23:04:11 +00:00
2003-06-22 17:48:45 +00:00
// This is causing terrible movie performance on an Athlon 1.3+Voodoo3.
2003-06-23 04:49:28 +00:00
// With the default delay of 2ms, the frame rate of demonstration dives
2003-06-24 22:00:38 +00:00
// from 110fps to 84. SDL_Delay must behave differently than Win32's
// Sleep(). Leave this commented for now, and uncomment if we hear
// about problems with dropped movie frames. -Chris
// if( m_pTexture && m_pTexture->IsAMovie() && m_pTexture->IsPlaying() )
// SDL_Delay( PREFSMAN->m_iMovieDecodeMS ); // let the movie decode a frame
2002-08-18 16:19:26 +00:00
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-12-11 11:25:37 +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-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
}
2003-06-23 04:49:28 +00:00
RectF croppedQuadVerticies = quadVerticies ;
2003-06-20 23:04:11 +00:00
#define IF_CROP_POS(side,opp_side) if(m_temp.crop.side>0) croppedQuadVerticies.side = SCALE( m_temp.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-06-20 23:04:11 +00:00
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
2002-01-16 10:01:32 +00:00
2002-11-21 22:25:00 +00:00
DISPLAY -> SetTexture ( m_pTexture );
2002-01-16 10:01:32 +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 );
2003-06-20 23:04:11 +00:00
2003-06-23 04:49:28 +00:00
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
};
if ( m_temp . crop . left > 0 )
{
v [ 0 ]. t . x = SCALE ( m_temp . crop . left , 0.f , 1.f , texCoords [ 0 ]. x , texCoords [ 3 ]. x );
v [ 1 ]. t . x = SCALE ( m_temp . crop . left , 0.f , 1.f , texCoords [ 1 ]. x , texCoords [ 2 ]. x );
}
if ( m_temp . crop . right > 0 )
{
v [ 2 ]. t . x = SCALE ( m_temp . crop . right , 0.f , 1.f , texCoords [ 2 ]. x , texCoords [ 1 ]. x );
v [ 3 ]. t . x = SCALE ( m_temp . crop . right , 0.f , 1.f , texCoords [ 3 ]. x , texCoords [ 0 ]. x );
}
if ( m_temp . crop . top > 0 )
{
v [ 0 ]. t . y = SCALE ( m_temp . crop . top , 0.f , 1.f , texCoords [ 0 ]. y , texCoords [ 1 ]. y );
v [ 3 ]. t . y = SCALE ( m_temp . crop . top , 0.f , 1.f , texCoords [ 3 ]. y , texCoords [ 2 ]. y );
}
if ( m_temp . crop . bottom > 0 )
{
v [ 1 ]. t . y = SCALE ( m_temp . crop . bottom , 0.f , 1.f , texCoords [ 1 ]. y , texCoords [ 0 ]. y );
v [ 2 ]. t . y = SCALE ( m_temp . crop . bottom , 0.f , 1.f , texCoords [ 2 ]. y , texCoords [ 3 ]. y );
}
2002-01-16 10:01:32 +00:00
}
2002-11-11 04:53:31 +00:00
DISPLAY -> SetTextureModeModulate ();
2002-01-16 10:01:32 +00:00
2003-05-15 06:09:19 +00:00
/* 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 ||
2003-05-15 06:09:19 +00:00
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-11 11:25:37 +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
2002-10-28 05:30:45 +00:00
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
2002-11-11 04:53:31 +00:00
DISPLAY -> DrawQuad ( v );
2002-05-19 01:59:48 +00:00
DISPLAY -> PopMatrix ();
2001-12-11 11:25:37 +00:00
}
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
2002-10-28 05:30:45 +00:00
v [ 2 ]. c = m_temp . diffuse [ 3 ]; // bottom right
v [ 3 ]. c = m_temp . diffuse [ 1 ]; // top right
2002-11-11 04:53:31 +00:00
DISPLAY -> DrawQuad ( v );
2003-05-04 05:13:30 +00:00
// glEnable(GL_BLEND);
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-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 );
2002-10-28 05:30:45 +00:00
v [ 0 ]. c = v [ 1 ]. c = v [ 2 ]. c = v [ 3 ]. c = m_temp . glow ;
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-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-06-23 04:49:28 +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 ;
2003-06-23 04:49:28 +00:00
TexCoordArrayFromRect ( m_CustomTexCoords , new_texcoord_frect );
2001-11-25 04:31:44 +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
2001-12-28 10:15:59 +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 ];
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 :: 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
inline CString GetParam ( const CStringArray & sParams , int iIndex , int & iMaxIndexAccessed )
{
iMaxIndexAccessed = max ( iIndex , iMaxIndexAccessed );
if ( iIndex < int ( sParams . size ()) )
return sParams [ iIndex ];
else
return "" ;
}
void Sprite :: HandleCommand ( const CStringArray & asTokens )
{
int iMaxIndexAccessed = 0 ;
#define sParam(i) (GetParam(asTokens,i,iMaxIndexAccessed))
#define fParam(i) ((float)atof(sParam(i)))
#define iParam(i) (atoi(sParam(i)))
#define bParam(i) (iParam(i)!=0)
const CString & sName = asTokens [ 0 ];
// Commands that go in the tweening queue:
/* Add left/right/top/bottom for alpha if needed. */
// 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 ) );
else
{
Actor :: HandleCommand ( asTokens );
return ;
}
if ( iMaxIndexAccessed != ( int ) asTokens . size () - 1 )
{
CString sError = ssprintf ( "Actor::HandleCommand: Wrong number of parameters in command '%s'. Expected %d but there are %d." , join ( "," , asTokens ). c_str (), iMaxIndexAccessed + 1 , ( int ) asTokens . size () );
LOG -> Warn ( sError );
if ( DISPLAY -> IsWindowed () )
HOOKS -> MessageBoxOK ( sError );
}
}