diff --git a/stepmania/src/Actor.cpp b/stepmania/src/Actor.cpp index b680b094f5..f337e79a67 100644 --- a/stepmania/src/Actor.cpp +++ b/stepmania/src/Actor.cpp @@ -622,39 +622,7 @@ void Actor::UpdateTweening( float fDeltaTime ) const float fPercentThroughTween = 1-(TI.m_fTimeLeftInTween / TI.m_fTweenTime); // distort the percentage if appropriate - float fPercentAlongPath = 0.f; - switch( TI.m_TweenType ) - { - case TWEEN_LINEAR: fPercentAlongPath = fPercentThroughTween; break; - case TWEEN_ACCELERATE: fPercentAlongPath = fPercentThroughTween * fPercentThroughTween; break; - case TWEEN_DECELERATE: fPercentAlongPath = 1 - (1-fPercentThroughTween) * (1-fPercentThroughTween); break; - case TWEEN_SMOOTH: - { - /* Accelerate, reaching full speed at fShift, then decelerate the rest of - * the way. fShift = 1 degrades to TWEEN_ACCELERATE. fShift = 0 degrades - * to TWEEN_DECELERATE. (This is a rough approximation of a sigmoid.) */ - const float fShift = 0.5f; - if( fPercentThroughTween < fShift ) - { - fPercentAlongPath = SCALE( fPercentThroughTween, 0.0f, fShift, 0.0f, 1.0f ); - fPercentAlongPath = fPercentAlongPath * fPercentAlongPath; - fPercentAlongPath = SCALE( fPercentAlongPath, 0.0f, 1.0f, 0.0f, fShift ); - } - else - { - fPercentAlongPath = SCALE( fPercentThroughTween, fShift, 1.0f, 0.0f, 1.0f ); - fPercentAlongPath = 1 - (1-fPercentAlongPath) * (1-fPercentAlongPath); - fPercentAlongPath = SCALE( fPercentAlongPath, 0.0f, 1.0f, fShift, 1.0f ); - } - break; - } - - case TWEEN_BOUNCE_BEGIN:fPercentAlongPath = 1 - RageFastSin( 1.1f + fPercentThroughTween*(PI-1.1f) ) / 0.89f; break; - case TWEEN_BOUNCE_END: fPercentAlongPath = RageFastSin( 1.1f + (1-fPercentThroughTween)*(PI-1.1f) ) / 0.89f; break; - case TWEEN_SPRING: fPercentAlongPath = 1 - RageFastCos( fPercentThroughTween*PI*2.5f )/(1+fPercentThroughTween*3); break; - default: ASSERT(0); - } - + float fPercentAlongPath = TI.m_pTween->Tween( fPercentThroughTween ); TweenState::MakeWeightedAverage( m_current, m_start, TS, fPercentAlongPath ); } } @@ -734,7 +702,7 @@ void Actor::UpdateInternal( float fDeltaTime ) UpdateTweening( fDeltaTime ); } -void Actor::BeginTweening( float time, TweenType tt ) +void Actor::BeginTweening( float time, ITween *pTween ) { ASSERT( time >= 0 ); @@ -769,11 +737,17 @@ void Actor::BeginTweening( float time, TweenType tt ) TS = m_current; } - TI.m_TweenType = tt; + TI.m_pTween = pTween; TI.m_fTweenTime = time; TI.m_fTimeLeftInTween = time; } +void Actor::BeginTweening( float time, TweenType tt ) +{ + ITween *pTween = ITween::CreateFromType( tt ); + BeginTweening( time, pTween ); +} + void Actor::StopTweening() { for( unsigned i = 0; i < m_Tweens.size(); ++i ) @@ -1316,6 +1290,32 @@ void Actor::SubscribeToMessage( Message message ) m_vsSubscribedTo.push_back( MessageToString(message) ); } +Actor::TweenInfo::TweenInfo() +{ + m_pTween = NULL; +} + +Actor::TweenInfo::~TweenInfo() +{ + delete m_pTween; +} + +Actor::TweenInfo::TweenInfo( const TweenInfo &cpy ) +{ + m_pTween = NULL; + *this = cpy; +} + +Actor::TweenInfo &Actor::TweenInfo::operator=( const TweenInfo &rhs ) +{ + delete m_pTween; + m_pTween = (rhs.m_pTween? m_pTween->Copy():NULL); + m_fTimeLeftInTween = rhs.m_fTimeLeftInTween; + m_fTweenTime = rhs.m_fTweenTime; + m_sCommandName = rhs.m_sCommandName; + return *this; +} + // lua start #include "LuaBinding.h" @@ -1325,14 +1325,18 @@ public: LunaActor() { LUA->Register( Register ); } static int sleep( T* p, lua_State *L ) { p->Sleep(FArg(1)); return 0; } - static int linear( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_LINEAR); return 0; } - static int accelerate( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_ACCELERATE); return 0; } - static int decelerate( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_DECELERATE); return 0; } - static int smooth( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_SMOOTH); return 0; } - static int bouncebegin( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_BOUNCE_BEGIN); return 0; } - static int bounceend( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_BOUNCE_END); return 0; } - static int spring( T* p, lua_State *L ) { p->BeginTweening(FArg(1),Actor::TWEEN_SPRING); return 0; } - static int stoptweening( T* p, lua_State *L ) { p->StopTweening(); p->BeginTweening( 0.0001f, Actor::TWEEN_LINEAR ); return 0; } + static int linear( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_LINEAR); return 0; } + static int accelerate( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_ACCELERATE); return 0; } + static int decelerate( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_DECELERATE); return 0; } + static int smooth( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_SMOOTH); return 0; } + static int spring( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_SPRING); return 0; } + static int tween( T* p, lua_State *L ) + { + ITween *pTween = ITween::CreateFromStack( L, 2 ); + p->BeginTweening( FArg(1), pTween ); + return 0; + } + static int stoptweening( T* p, lua_State *L ) { p->StopTweening(); p->BeginTweening( 0.0001f, TWEEN_LINEAR ); return 0; } static int finishtweening( T* p, lua_State *L ) { p->FinishTweening(); return 0; } static int hurrytweening( T* p, lua_State *L ) { p->HurryTweening(FArg(1)); return 0; } static int x( T* p, lua_State *L ) { p->SetX(FArg(1)); return 0; } @@ -1466,9 +1470,8 @@ public: ADD_METHOD( accelerate ); ADD_METHOD( decelerate ); ADD_METHOD( smooth ); - ADD_METHOD( bouncebegin ); - ADD_METHOD( bounceend ); ADD_METHOD( spring ); + ADD_METHOD( tween ); ADD_METHOD( stoptweening ); ADD_METHOD( finishtweening ); ADD_METHOD( hurrytweening ); diff --git a/stepmania/src/Actor.h b/stepmania/src/Actor.h index 18b4a184de..e28be95cb9 100644 --- a/stepmania/src/Actor.h +++ b/stepmania/src/Actor.h @@ -12,6 +12,7 @@ struct lua_State; class LuaReference; class LuaClass; #include "MessageManager.h" +#include "Tween.h" #define DRAW_ORDER_BEFORE_EVERYTHING -200 @@ -21,7 +22,6 @@ class LuaClass; #define DRAW_ORDER_TRANSITIONS +110 #define DRAW_ORDER_AFTER_EVERYTHING +200 - class Actor : public IMessageSubscriber { public: @@ -36,15 +36,6 @@ public: static void SetBGMTime( float fTime, float fBeat ); static void SetBGMLight( int iLightNumber, float fCabinetLights ); - enum TweenType { - TWEEN_LINEAR, - TWEEN_ACCELERATE, - TWEEN_DECELERATE, - TWEEN_SMOOTH, - TWEEN_BOUNCE_BEGIN, - TWEEN_BOUNCE_END, - TWEEN_SPRING, - }; enum Effect { no_effect, effect_lua, diffuse_blink, diffuse_shift, diffuse_ramp, glow_blink, glow_shift, @@ -199,6 +190,7 @@ public: void SetAux( float f ) { DestTweenState().aux = f; } float GetAux() const { return m_current.aux; } + void BeginTweening( float time, ITween *pInterp ); void BeginTweening( float time, TweenType tt = TWEEN_LINEAR ); void StopTweening(); void Sleep( float time ); @@ -376,7 +368,12 @@ protected: struct TweenInfo { // counters for tweening - TweenType m_TweenType; + TweenInfo(); + ~TweenInfo(); + TweenInfo( const TweenInfo &cpy ); + TweenInfo &operator=( const TweenInfo &rhs ); + + ITween *m_pTween; float m_fTimeLeftInTween; // how far into the tween are we? float m_fTweenTime; // seconds between Start and End positions/zooms RString m_sCommandName; // command to execute when this TweenState goes into effect diff --git a/stepmania/src/RageMath.cpp b/stepmania/src/RageMath.cpp index 3e2fb3e815..127537865c 100644 --- a/stepmania/src/RageMath.cpp +++ b/stepmania/src/RageMath.cpp @@ -646,8 +646,69 @@ float RageFastCos( float x ) return RageFastSin( x + 0.5f*PI ); } +float RageQuadradtic::Evaluate( float fT ) const +{ + // optimized (m_fA * fT*fT*fT) + (m_fB * fT*fT) + (m_fC * fT) + m_fD; + return ((m_fA*fT + m_fB)*fT + m_fC)*fT + m_fD; +} + +void RageQuadradtic::SetFromBezier( float fX1, float fX2, float fX3, float fX4 ) +{ + m_fD = fX1; + m_fC = 3.0f * (fX2 - fX1); + m_fB = 3.0f * (fX3 - fX2) - m_fC; + m_fA = fX4 - fX1 - m_fC - m_fB; +} + +void RageQuadradtic::GetBezier( float &fX1, float &fX2, float &fX3, float &fX4 ) const +{ + fX1 = m_fD; + fX2 = m_fD + m_fC/3.0f; + fX3 = m_fD + 2*m_fC/3.0f + m_fB/3.0f; + fX4 = m_fD + m_fC + m_fB + m_fA; +} + +float RageQuadradtic::GetSlope( float fT ) const +{ + return 3*m_fA*fT*fT + 2*m_fB*fT + m_fC; +} + +void RageBezier2D::Evaluate( float fT, float *pX, float *pY ) const +{ + *pX = m_X.Evaluate( fT ); + *pY = m_Y.Evaluate( fT ); +} + +float RageBezier2D::EvaluateYFromX( float fX ) const +{ + /* Quickly approximate T using Newton-Raphelson successive optimization (see + * http://www.tinaja.com/text/bezmath.html). This usually finds T within an + * acceptable error margin in a few steps. */ + float fT = SCALE( fX, m_X.GetBezierStart(), m_X.GetBezierEnd(), 0, 1 ); + while(1) + { + float fGuessedX = m_X.Evaluate( fT ); + float fError = fX-fGuessedX; + + /* If our guess is good enough, evaluate the result Y and return. */ + if( unlikely(fabsf(fError) < 0.0001f) ) + return m_Y.Evaluate( fT ); + + float fSlope = m_X.GetSlope( fT ); + fT += fError/fSlope; + } +} + +void RageBezier2D::SetFromBezier( + float fC1X, float fC1Y, float fC2X, float fC2Y, + float fC3X, float fC3Y, float fC4X, float fC4Y ) +{ + m_X.SetFromBezier( fC1X, fC2X, fC3X, fC4X ); + m_Y.SetFromBezier( fC1Y, fC2Y, fC3Y, fC4Y ); +} + /* - * Copyright (c) 2001-2003 Chris Danford + * Copyright (c) 2001-2006 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/stepmania/src/RageMath.h b/stepmania/src/RageMath.h index 6fccb00a35..17232d6c9d 100644 --- a/stepmania/src/RageMath.h +++ b/stepmania/src/RageMath.h @@ -51,10 +51,41 @@ void RageMatrixTranspose( RageMatrix* pOut, const RageMatrix* pIn ); float RageFastSin( float x ) CONST_FUNCTION; float RageFastCos( float x ) CONST_FUNCTION; +class RageQuadradtic +{ +public: + void SetFromBezier( float fC1, float fC2, float fC3, float fC4 ); + void GetBezier( float &fC1, float &fC2, float &fC3, float &fC4 ) const; + + float Evaluate( float fT ) const; + float GetSlope( float fT ) const; + + /* Equivalent to Evaluate(0.0f) and Evaluate(1.0f), but faster: */ + float GetBezierStart() const { return m_fD; } + float GetBezierEnd() const { return m_fA + m_fB + m_fC + m_fD; } + +private: + float m_fA, m_fB, m_fC, m_fD; +}; + +class RageBezier2D +{ +public: + void SetFromBezier( float fC1X, float fC2X, float fC3X, float fC4X, + float fC1Y, float fC2Y, float fC3Y, float fC4Y ); + + void Evaluate( float fT, float *pX, float *pY ) const; + float EvaluateYFromX( float fX ) const; + +private: + RageQuadradtic m_X; + RageQuadradtic m_Y; +}; + #endif /* - * Copyright (c) 2001-2003 Chris Danford + * Copyright (c) 2001-2006 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/stepmania/src/Tween.cpp b/stepmania/src/Tween.cpp new file mode 100644 index 0000000000..41504bd7cb --- /dev/null +++ b/stepmania/src/Tween.cpp @@ -0,0 +1,192 @@ +#include "global.h" +#include "Tween.h" +#include "RageUtil.h" +#include "RageMath.h" +#include "LuaManager.h" +#include "EnumHelper.h" + +static const char *TweenTypeNames[] = { + "Linear", + "Accelerate", + "Decelerate", + "Smooth", + "Spring", + "Bezier" +}; +XToString( TweenType, NUM_TweenType ); + +static void LuaTweenType(lua_State* L) +{ + FOREACH_TweenType( tt ) + { + RString s = TweenTypeToString( tt ); + LUA->SetGlobal( "Tween"+s, tt ); + } +} +REGISTER_WITH_LUA_FUNCTION( LuaTweenType ); + + +struct TweenLinear: public ITween +{ + float Tween( float f ) const { return f; } + ITween *Copy() const { return new TweenLinear(*this); } +}; +struct TweenAccelerate: public ITween +{ + float Tween( float f ) const { return f*f; } + ITween *Copy() const { return new TweenAccelerate(*this); } +}; +struct TweenDecelerate: public ITween +{ + float Tween( float f ) const { return 1 - (1-f) * (1-f); } + ITween *Copy() const { return new TweenDecelerate(*this); } +}; +struct TweenSmooth: public ITween +{ + float Tween( float f ) const + { + /* Accelerate, reaching full speed at fShift, then decelerate the rest of + * the way. fShift = 1 degrades to TWEEN_ACCELERATE. fShift = 0 degrades + * to TWEEN_DECELERATE. (This is a rough approximation of a sigmoid.) */ + const float fShift = 0.5f; + if( f < fShift ) + { + f = SCALE( f, 0.0f, fShift, 0.0f, 1.0f ); + f = f * f; + f = SCALE( f, 0.0f, 1.0f, 0.0f, fShift ); + } + else + { + f = SCALE( f, fShift, 1.0f, 0.0f, 1.0f ); + f = 1 - (1-f) * (1-f); + f = SCALE( f, 0.0f, 1.0f, fShift, 1.0f ); + } + + return f; + } + + ITween *Copy() const { return new TweenSmooth(*this); } +}; + +struct TweenSpring: public ITween +{ + float Tween( float f ) const { return 1 - RageFastCos( f*PI*2.5f )/(1+f*3); } + ITween *Copy() const { return new TweenSpring(*this); } +}; + + +/* + * Interpolation with 1-dimensional cubic Bezier curves. + */ +struct InterpolateBezier1D: public ITween +{ + float Tween( float f ) const; + ITween *Copy() const { return new InterpolateBezier1D(*this); } + + RageQuadradtic m_Bezier; +}; + +float InterpolateBezier1D::Tween( float f ) const +{ + return m_Bezier.Evaluate( f ); +} + +/* + * Interpolation with 2-dimensional cubic Bezier curves. + */ +struct InterpolateBezier2D: public ITween +{ + float Tween( float f ) const; + ITween *Copy() const { return new InterpolateBezier2D(*this); } + + RageBezier2D m_Bezier; +}; + +float InterpolateBezier2D::Tween( float f ) const +{ + return m_Bezier.EvaluateYFromX( f ); +} + +/* This interpolator combines multiple other interpolators, to allow + * generating more complex tweens. For example, "compound,10,linear,0.25,accelerate,0.75" + * means 25% of the tween is linear, and the rest is accelerate. This can be + * used with Bezier to create spline tweens. */ +// InterpolateCompound + +ITween *ITween::CreateFromType( TweenType tt ) +{ + switch( tt ) + { + case TWEEN_LINEAR: return new TweenLinear; + case TWEEN_ACCELERATE: return new TweenAccelerate; + case TWEEN_DECELERATE: return new TweenDecelerate; + case TWEEN_SMOOTH: return new TweenSmooth; + + /* + * These may actually be faster than InterpolateBounceBegin/InterpolateBounceEnd, since + * they don't use RageFastSin/RageFastCos. + */ + case TWEEN_SPRING: return new TweenSpring; + default: ASSERT(0); + } +} + +ITween *ITween::CreateFromStack( Lua *L, int iStackPos ) +{ + TweenType iType = (TweenType) IArg(iStackPos); + if( iType == TWEEN_BEZIER ) + { + luaL_checktype( L, iStackPos+1, LUA_TTABLE ); + int iArgs = luaL_getn( L, iStackPos+1 ); + if( iArgs != 4 && iArgs != 8 ) + RageException::Throw( "CreateFromStack: table argument must have 4 or 8 entries" ); + + float fC[8]; + for( int i = 0; i < iArgs; ++i ) + { + lua_rawgeti( L, iStackPos+1, i+1 ); + fC[i] = (float) lua_tonumber( L, -1 ); + } + + lua_pop( L, iArgs ); + if( iArgs == 4 ) + { + InterpolateBezier1D *pBezier = new InterpolateBezier1D; + pBezier->m_Bezier.SetFromBezier( fC[0], fC[1], fC[2], fC[3] ); + return pBezier; + } + else if( iArgs == 8 ) + { + InterpolateBezier2D *pBezier = new InterpolateBezier2D; + pBezier->m_Bezier.SetFromBezier( fC[0], fC[1], fC[2], fC[3], fC[4], fC[5], fC[6], fC[7] ); + return pBezier; + } + } + + return CreateFromType( iType ); +} + +/* + * (c) 2001-2006 Glenn Maynard, Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/Tween.h b/stepmania/src/Tween.h new file mode 100644 index 0000000000..02249605a4 --- /dev/null +++ b/stepmania/src/Tween.h @@ -0,0 +1,54 @@ +#ifndef INTERPOLATE_H +#define INTERPOLATE_H + +struct lua_State; +typedef lua_State Lua; + +enum TweenType +{ + TWEEN_LINEAR, + TWEEN_ACCELERATE, + TWEEN_DECELERATE, + TWEEN_SMOOTH, + TWEEN_SPRING, + TWEEN_BEZIER, + NUM_TweenType +}; +#define FOREACH_TweenType( tt ) FOREACH_ENUM( TweenType, NUM_TweenType, tt ) + +class ITween +{ +public: + virtual float Tween( float f ) const = 0; + virtual ITween *Copy() const = 0; + + static ITween *CreateFromType( TweenType iType ); + static ITween *CreateFromStack( Lua *L, int iStackPos ); +}; + +#endif + +/* + * (c) 2006 Glenn Maynard + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */