fix long hold + boomerang artifacts: didn't draw if neither the head nor the tail were on-screen, tail bottom would be positioned above the head causing the hold to magically grow once the tail comes on screen
This commit is contained in:
@@ -25,8 +25,13 @@ static float GetNoteFieldHeight( const PlayerState* pPlayerState )
|
|||||||
|
|
||||||
/* For visibility testing: if bAbsolute is false, random modifiers must return the
|
/* For visibility testing: if bAbsolute is false, random modifiers must return the
|
||||||
* minimum possible scroll speed. */
|
* minimum possible scroll speed. */
|
||||||
float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, bool bAbsolute )
|
float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute )
|
||||||
{
|
{
|
||||||
|
// Default values that are returned if boomerang is off.
|
||||||
|
fPeakYOffsetOut = FLT_MAX;
|
||||||
|
bIsPastPeakOut = true;
|
||||||
|
|
||||||
|
|
||||||
float fYOffset = 0;
|
float fYOffset = 0;
|
||||||
|
|
||||||
/* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or
|
/* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or
|
||||||
@@ -84,9 +89,22 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
|||||||
|
|
||||||
fYOffset += fYAdjust;
|
fYOffset += fYAdjust;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Factor in boomerang
|
||||||
|
//
|
||||||
if( fAccels[PlayerOptions::ACCEL_BOOMERANG] > 0 )
|
if( fAccels[PlayerOptions::ACCEL_BOOMERANG] > 0 )
|
||||||
fYOffset += fAccels[PlayerOptions::ACCEL_BOOMERANG] * (fYOffset * SCALE( fYOffset, 0.f, SCREEN_HEIGHT, 1.5f, 0.5f )- fYOffset);
|
{
|
||||||
|
float fOriginalYOffset = fYOffset;
|
||||||
|
|
||||||
|
fYOffset = (-1*fOriginalYOffset*fOriginalYOffset/SCREEN_HEIGHT) + 1.5*fOriginalYOffset;
|
||||||
|
float fPeakAtYOffset = SCREEN_HEIGHT * 0.75; // zero point of function above
|
||||||
|
fPeakYOffsetOut = (-1*fPeakAtYOffset*fPeakAtYOffset/SCREEN_HEIGHT) + 1.5*fPeakAtYOffset;
|
||||||
|
bIsPastPeakOut = fOriginalYOffset < fPeakAtYOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Factor in scroll speed
|
||||||
|
//
|
||||||
float fScrollSpeed = pPlayerState->m_CurrentPlayerOptions.m_fScrollSpeed;
|
float fScrollSpeed = pPlayerState->m_CurrentPlayerOptions.m_fScrollSpeed;
|
||||||
if( pPlayerState->m_CurrentPlayerOptions.m_fRandomSpeed > 0 && !bAbsolute )
|
if( pPlayerState->m_CurrentPlayerOptions.m_fRandomSpeed > 0 && !bAbsolute )
|
||||||
{
|
{
|
||||||
@@ -119,11 +137,11 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float
|
|||||||
}
|
}
|
||||||
|
|
||||||
fYOffset *= fScrollSpeed;
|
fYOffset *= fScrollSpeed;
|
||||||
|
fPeakYOffsetOut *= fScrollSpeed;
|
||||||
|
|
||||||
return fYOffset;
|
return fYOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ArrowGetReverseShiftAndScale( const PlayerState* pPlayerState, int iCol, float fYReverseOffsetPixels, float &fShiftOut, float &fScaleOut )
|
void ArrowGetReverseShiftAndScale( const PlayerState* pPlayerState, int iCol, float fYReverseOffsetPixels, float &fShiftOut, float &fScaleOut )
|
||||||
{
|
{
|
||||||
/* XXX: Hack: we need to scale the reverse shift by the zoom. */
|
/* XXX: Hack: we need to scale the reverse shift by the zoom. */
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ public:
|
|||||||
// fYOffset is a vertical position in pixels relative to the center.
|
// fYOffset is a vertical position in pixels relative to the center.
|
||||||
// (positive if has not yet been stepped on, negative if has already passed).
|
// (positive if has not yet been stepped on, negative if has already passed).
|
||||||
// The ArrowEffect and ScrollSpeed is applied in this stage.
|
// The ArrowEffect and ScrollSpeed is applied in this stage.
|
||||||
static float GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, bool bAbsolute=false );
|
static float GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakYOffset, bool bAbsolute=false );
|
||||||
|
static float GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, bool bAbsolute=false )
|
||||||
|
{
|
||||||
|
float fThrowAway;
|
||||||
|
bool bThrowAway;
|
||||||
|
return GetYOffset( pPlayerState, iCol, fNoteBeat, fThrowAway, bThrowAway, bAbsolute );
|
||||||
|
}
|
||||||
|
|
||||||
/* Actual display position, with reverse and post-reverse-effects factored in
|
/* Actual display position, with reverse and post-reverse-effects factored in
|
||||||
* (fYOffset -> YPos). */
|
* (fYOffset -> YPos). */
|
||||||
|
|||||||
@@ -986,20 +986,34 @@ void NoteDisplay::DrawHoldHead( const TapNote& tn, int iCol, int iRow, const boo
|
|||||||
|
|
||||||
void NoteDisplay::DrawHold( const TapNote &tn, int iCol, int iRow, bool bIsBeingHeld, bool bIsActive, const HoldNoteResult &Result, float fPercentFadeToFail, bool bDrawGlowOnly, float fReverseOffsetPixels, float fYStartOffset, float fYEndOffset )
|
void NoteDisplay::DrawHold( const TapNote &tn, int iCol, int iRow, bool bIsBeingHeld, bool bIsActive, const HoldNoteResult &Result, float fPercentFadeToFail, bool bDrawGlowOnly, float fReverseOffsetPixels, float fYStartOffset, float fYEndOffset )
|
||||||
{
|
{
|
||||||
int iEndBeat = iRow + tn.iDuration;
|
int iEndRow = iRow + tn.iDuration;
|
||||||
|
|
||||||
// bDrawGlowOnly is a little hacky. We need to draw the diffuse part and the glow part one pass at a time to minimize state changes
|
// bDrawGlowOnly is a little hacky. We need to draw the diffuse part and the glow part one pass at a time to minimize state changes
|
||||||
|
|
||||||
bool bReverse = m_pPlayerState->m_CurrentPlayerOptions.GetReversePercentForColumn(iCol) > 0.5f;
|
bool bReverse = m_pPlayerState->m_CurrentPlayerOptions.GetReversePercentForColumn(iCol) > 0.5f;
|
||||||
float fStartBeat = NoteRowToBeat( max(tn.HoldResult.iLastHeldRow, iRow) );
|
float fStartBeat = NoteRowToBeat( max(tn.HoldResult.iLastHeldRow, iRow) );
|
||||||
float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fStartBeat );
|
float fThrowAway = 0;
|
||||||
|
|
||||||
// HACK: If active, don't allow the top of the hold to go above the receptor
|
// HACK: If active, don't set YOffset to 0 so that it doesn't jiggle around the receptor.
|
||||||
|
bool bStartIsPastPeak = true;
|
||||||
|
float fStartYOffset = 0;
|
||||||
if( bIsActive )
|
if( bIsActive )
|
||||||
fStartYOffset = 0;
|
; // use the default values filled in above
|
||||||
|
else
|
||||||
|
fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fStartBeat, fThrowAway, bStartIsPastPeak );
|
||||||
|
|
||||||
float fStartYPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fStartYOffset, fReverseOffsetPixels );
|
float fStartYPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fStartYOffset, fReverseOffsetPixels );
|
||||||
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, NoteRowToBeat(iEndBeat) );
|
float fEndPeakYOffset = 0;
|
||||||
|
bool bEndIsPastPeak = false;
|
||||||
|
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, NoteRowToBeat(iEndRow), fEndPeakYOffset, bEndIsPastPeak );
|
||||||
|
|
||||||
|
// In boomerang, the arrows reverse direction at Y offset value fPeakAtYOffset.
|
||||||
|
// If fPeakAtYOffset lies inside of the hold we're drawing, then the we
|
||||||
|
// want to draw the tail at that max Y offset, or else the hold will appear
|
||||||
|
// to magically grow as the tail approaches the max Y offset.
|
||||||
|
if( bStartIsPastPeak && !bEndIsPastPeak )
|
||||||
|
fEndYOffset = fEndPeakYOffset; // use the calculated PeakYOffset so that long holds don't appear to grow
|
||||||
|
|
||||||
float fEndYPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fEndYOffset, fReverseOffsetPixels );
|
float fEndYPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fEndYOffset, fReverseOffsetPixels );
|
||||||
|
|
||||||
const float fYHead = bReverse ? fEndYPos : fStartYPos; // the center of the head
|
const float fYHead = bReverse ? fEndYPos : fStartYPos; // the center of the head
|
||||||
|
|||||||
@@ -572,12 +572,18 @@ void NoteField::DrawPrimitives()
|
|||||||
// TRICKY: If boomerang is on, then all notes in the range
|
// TRICKY: If boomerang is on, then all notes in the range
|
||||||
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
|
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
|
||||||
// Test every note to make sure it's on screen before drawing
|
// Test every note to make sure it's on screen before drawing
|
||||||
float fYStartOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iStartRow) );
|
float fThrowAway;
|
||||||
float fYEndOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iEndRow) );
|
bool bStartIsPastPeak = false;
|
||||||
if( !( iFirstPixelToDraw <= fYEndOffset && fYEndOffset <= iLastPixelToDraw ||
|
bool bEndIsPastPeak = false;
|
||||||
iFirstPixelToDraw <= fYStartOffset && fYStartOffset <= iLastPixelToDraw ||
|
float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iStartRow), fThrowAway, bStartIsPastPeak );
|
||||||
fYStartOffset < iFirstPixelToDraw && fYEndOffset > iLastPixelToDraw ) )
|
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iEndRow), fThrowAway, bEndIsPastPeak );
|
||||||
|
|
||||||
|
bool bTailIsOnVisible = iFirstPixelToDraw <= fEndYOffset && fEndYOffset <= iLastPixelToDraw;
|
||||||
|
bool bHeadIsVisible = iFirstPixelToDraw <= fStartYOffset && fStartYOffset <= iLastPixelToDraw;
|
||||||
|
bool bStraddlingVisible = bStartIsPastPeak && !bEndIsPastPeak;
|
||||||
|
if( !(bTailIsOnVisible || bHeadIsVisible || bStraddlingVisible) )
|
||||||
{
|
{
|
||||||
|
//LOG->Trace( "skip drawing this hold." );
|
||||||
continue; // skip
|
continue; // skip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user