#include "global.h" /* ----------------------------------------------------------------------------- Class: NoteDisplay Desc: See header. Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. Brian Bugh Ben Nordstrom Chris Danford ----------------------------------------------------------------------------- */ #include "NoteDisplay.h" #include "Steps.h" #include "PrefsManager.h" #include "GameState.h" #include "NoteSkinManager.h" #include "RageException.h" #include "ArrowEffects.h" #include "RageLog.h" #include #include "RageDisplay.h" #include "NoteTypes.h" #include "NoteFieldPositioning.h" #include "ActorUtil.h" #define DRAW_HOLD_HEAD_FOR_TAPS_ON_SAME_ROW NOTESKIN->GetMetricB(skin,name,"DrawHoldHeadForTapsOnSameRow") #define TAP_NOTE_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"TapNoteAnimationLengthInBeats") #define TAP_ADDITION_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"TapAdditionAnimationLengthInBeats") #define TAP_MINE_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"TapMineAnimationLengthInBeats") #define HOLD_HEAD_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"HoldHeadAnimationLengthInBeats") #define HOLD_TOPCAP_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"HoldTopCapAnimationLengthInBeats") #define HOLD_BODY_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"HoldBodyAnimationLengthInBeats") #define HOLD_BOTTOMCAP_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"HoldBottomCapAnimationLengthInBeats") #define HOLD_TAIL_ANIMATION_LENGTH_IN_BEATS NOTESKIN->GetMetricF(skin,name,"HoldTailAnimationLengthInBeats") #define TAP_NOTE_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"TapNoteAnimationIsVivid") #define TAP_ADDITION_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"TapAdditionAnimationIsVivid") #define TAP_MINE_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"TapMineAnimationIsVivid") #define HOLD_HEAD_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"HoldHeadAnimationIsVivid") #define HOLD_TOPCAP_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"HoldTopCapAnimationIsVivid") #define HOLD_BODY_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"HoldBodyAnimationIsVivid") #define HOLD_BOTTOMCAP_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"HoldBottomCapAnimationIsVivid") #define HOLD_TAIL_ANIMATION_IS_VIVID NOTESKIN->GetMetricB(skin,name,"HoldTailAnimationIsVivid") #define TAP_NOTE_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"TapNoteAnimationIsNoteColor") #define TAP_ADDITION_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"TapAdditionAnimationIsNoteColor") #define HOLD_HEAD_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"HoldHeadAnimationIsNoteColor") #define HOLD_TOPCAP_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"HoldTopCapAnimationIsNoteColor") #define HOLD_BODY_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"HoldBodyAnimationIsNoteColor") #define HOLD_BOTTOMCAP_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"HoldBottomCapAnimationIsNoteColor") #define HOLD_TAIL_ANIMATION_IS_NOTE_COLOR NOTESKIN->GetMetricB(skin,name,"HoldTailAnimationIsNoteColor") #define HOLD_HEAD_IS_ABOVE_WAVY_PARTS NOTESKIN->GetMetricB(skin,name,"HoldHeadIsAboveWavyParts") #define HOLD_TAIL_IS_ABOVE_WAVY_PARTS NOTESKIN->GetMetricB(skin,name,"HoldTailIsAboveWavyParts") #define START_DRAWING_HOLD_BODY_OFFSET_FROM_HEAD NOTESKIN->GetMetricI(skin,name,"StartDrawingHoldBodyOffsetFromHead") #define STOP_DRAWING_HOLD_BODY_OFFSET_FROM_TAIL NOTESKIN->GetMetricI(skin,name,"StopDrawingHoldBodyOffsetFromTail") #define HOLD_NG_GRAY_PERCENT NOTESKIN->GetMetricF(skin,name,"HoldNGGrayPercent") #define TAP_NOTE_USE_LIGHTING NOTESKIN->GetMetricB(skin,name,"TapNoteUseLighting") #define TAP_ADDITION_USE_LIGHTING NOTESKIN->GetMetricB(skin,name,"TapAdditionUseLighting") #define TAP_MINE_USE_LIGHTING NOTESKIN->GetMetricB(skin,name,"TapMineUseLighting") #define HOLD_HEAD_USE_LIGHTING NOTESKIN->GetMetricB(skin,name,"HoldHeadUseLighting") #define HOLD_TAIL_USE_LIGHTING NOTESKIN->GetMetricB(skin,name,"HoldTailUseLighting") #define FLIP_HEAD_AND_TAIL_WHEN_REVERSE NOTESKIN->GetMetricB(skin,name,"FlipHeadAndTailWhenReverse") // cache struct NoteMetricCache_t { bool m_bDrawHoldHeadForTapsOnSameRow; float m_fTapNoteAnimationLengthInBeats; float m_fTapAdditionAnimationLengthInBeats; float m_fTapMineAnimationLengthInBeats; float m_fHoldHeadAnimationLengthInBeats; float m_fHoldTopCapAnimationLengthInBeats; float m_fHoldBodyAnimationLengthInBeats; float m_fHoldBottomCapAnimationLengthInBeats; float m_fHoldTailAnimationLengthInBeats; bool m_bTapNoteAnimationIsVivid; bool m_bTapAdditionAnimationIsVivid; bool m_bTapMineAnimationIsVivid; bool m_bHoldHeadAnimationIsVivid; bool m_bHoldTopCapAnimationIsVivid; bool m_bHoldBodyAnimationIsVivid; bool m_bHoldBottomCapAnimationIsVivid; bool m_bHoldTailAnimationIsVivid; bool m_bTapNoteAnimationIsNoteColor; bool m_bTapAdditionAnimationIsNoteColor; bool m_bHoldHeadAnimationIsNoteColor; bool m_bHoldTopCapAnimationIsNoteColor; bool m_bHoldBodyAnimationIsNoteColor; bool m_bHoldBottomCapAnimationIsNoteColor; bool m_bHoldTailAnimationIsNoteColor; bool m_bHoldHeadIsAboveWavyParts; bool m_bHoldTailIsAboveWavyParts; int m_iStartDrawingHoldBodyOffsetFromHead; int m_iStopDrawingHoldBodyOffsetFromTail; float m_fHoldNGGrayPercent; bool m_bTapNoteUseLighting; bool m_bTapAdditionUseLighting; bool m_bTapMineUseLighting; bool m_bHoldHeadUseLighting; bool m_bHoldTailUseLighting; bool m_bFlipHeadAndTailWhenReverse; void Load(CString skin, const CString &name); } *NoteMetricCache; void NoteMetricCache_t::Load(CString skin, const CString &name) { m_bDrawHoldHeadForTapsOnSameRow = DRAW_HOLD_HEAD_FOR_TAPS_ON_SAME_ROW; m_fTapNoteAnimationLengthInBeats = TAP_NOTE_ANIMATION_LENGTH_IN_BEATS; m_fTapAdditionAnimationLengthInBeats = TAP_ADDITION_ANIMATION_LENGTH_IN_BEATS; m_fTapMineAnimationLengthInBeats = TAP_MINE_ANIMATION_LENGTH_IN_BEATS; m_fHoldHeadAnimationLengthInBeats = HOLD_HEAD_ANIMATION_LENGTH_IN_BEATS; m_fHoldTopCapAnimationLengthInBeats = HOLD_TOPCAP_ANIMATION_LENGTH_IN_BEATS; m_fHoldBodyAnimationLengthInBeats = HOLD_BODY_ANIMATION_LENGTH_IN_BEATS; m_fHoldBottomCapAnimationLengthInBeats = HOLD_BOTTOMCAP_ANIMATION_LENGTH_IN_BEATS; m_fHoldTailAnimationLengthInBeats = HOLD_TAIL_ANIMATION_LENGTH_IN_BEATS; m_bTapNoteAnimationIsVivid = TAP_NOTE_ANIMATION_IS_VIVID; m_bTapAdditionAnimationIsVivid = TAP_ADDITION_ANIMATION_IS_VIVID; m_bTapMineAnimationIsVivid = TAP_MINE_ANIMATION_IS_VIVID; m_bHoldHeadAnimationIsVivid = HOLD_HEAD_ANIMATION_IS_VIVID; m_bHoldTopCapAnimationIsVivid = HOLD_TOPCAP_ANIMATION_IS_VIVID; m_bHoldBodyAnimationIsVivid = HOLD_BODY_ANIMATION_IS_VIVID; m_bHoldBottomCapAnimationIsVivid = HOLD_BOTTOMCAP_ANIMATION_IS_VIVID; m_bHoldTailAnimationIsVivid = HOLD_TAIL_ANIMATION_IS_VIVID; m_bTapNoteAnimationIsNoteColor = TAP_NOTE_ANIMATION_IS_NOTE_COLOR; m_bTapAdditionAnimationIsNoteColor = TAP_ADDITION_ANIMATION_IS_NOTE_COLOR; m_bHoldHeadAnimationIsNoteColor = HOLD_HEAD_ANIMATION_IS_NOTE_COLOR; m_bHoldTopCapAnimationIsNoteColor = HOLD_TOPCAP_ANIMATION_IS_NOTE_COLOR; m_bHoldBodyAnimationIsNoteColor = HOLD_BODY_ANIMATION_IS_NOTE_COLOR; m_bHoldBottomCapAnimationIsNoteColor = HOLD_BOTTOMCAP_ANIMATION_IS_NOTE_COLOR; m_bHoldTailAnimationIsNoteColor = HOLD_TAIL_ANIMATION_IS_NOTE_COLOR; m_bHoldHeadIsAboveWavyParts = HOLD_HEAD_IS_ABOVE_WAVY_PARTS; m_bHoldTailIsAboveWavyParts = HOLD_TAIL_IS_ABOVE_WAVY_PARTS; m_iStartDrawingHoldBodyOffsetFromHead = START_DRAWING_HOLD_BODY_OFFSET_FROM_HEAD; m_iStopDrawingHoldBodyOffsetFromTail = STOP_DRAWING_HOLD_BODY_OFFSET_FROM_TAIL; m_fHoldNGGrayPercent = HOLD_NG_GRAY_PERCENT; m_bTapNoteUseLighting = TAP_NOTE_USE_LIGHTING; m_bTapAdditionUseLighting = TAP_ADDITION_USE_LIGHTING; m_bTapMineUseLighting = TAP_MINE_USE_LIGHTING; m_bHoldHeadUseLighting = HOLD_HEAD_USE_LIGHTING; m_bHoldTailUseLighting = HOLD_TAIL_USE_LIGHTING; m_bFlipHeadAndTailWhenReverse = FLIP_HEAD_AND_TAIL_WHEN_REVERSE; } NoteDisplay::NoteDisplay() { for( int i=0; iLoad( NoteSkin, Button ); // Look up note names once and store them here. CString sNoteType[ NOTE_COLOR_IMAGES ]; for( int i=0; im_bTapNoteAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "tap note "+sNoteType[i]) ); } else { m_pTapNote[0] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "tap note") ); } if( cache->m_bTapAdditionAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "tap addition "+sNoteType[i]) ); } else { m_pTapAddition[0] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "tap addition") ); } m_pTapMine = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "tap mine") ); if( cache->m_bHoldHeadAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold head active "+sNoteType[i]) ); m_pHoldHeadInactive[i] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold head inactive "+sNoteType[i]) ); } } else { m_pHoldHeadActive[0] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold head active") ); m_pHoldHeadInactive[0] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold head inactive") ); } if( cache->m_bHoldTopCapAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold topcap active "+sNoteType[i]) ); m_sprHoldTopCapInactive[i].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold topcap inactive "+sNoteType[i]) ); } } else { m_sprHoldTopCapActive[0].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold topcap active") ); m_sprHoldTopCapInactive[0].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold topcap inactive") ); } if( cache->m_bHoldBodyAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold body active "+sNoteType[i]) ); m_sprHoldBodyInactive[i].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold body inactive "+sNoteType[i]) ); } } else { m_sprHoldBodyActive[0].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold body active") ); m_sprHoldBodyInactive[0].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold body inactive") ); } if( cache->m_bHoldBottomCapAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold bottomcap active "+sNoteType[i]) ); m_sprHoldBottomCapInactive[i].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold bottomcap inactive "+sNoteType[i]) ); } } else { m_sprHoldBottomCapActive[0].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold bottomcap active") ); m_sprHoldBottomCapInactive[0].Load( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold bottomcap inactive") ); } if( cache->m_bHoldTailAnimationIsNoteColor ) { for( int i=0; iGetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold tail active "+sNoteType[i]) ); m_pHoldTailInactive[i] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold tail inactive "+sNoteType[i]) ); } } else { m_pHoldTailActive[0] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold tail active") ); m_pHoldTailInactive[0] = MakeActor( NOTESKIN->GetPathToFromNoteSkinAndButton(NoteSkin, Button, "hold tail inactive") ); } } void NoteDisplay::SetActiveFrame( float fNoteBeat, Actor &actorToSet, float fAnimationLengthInBeats, bool bVivid, bool bNoteColor ) { const int iNumFrames = actorToSet.GetNumStates(); if( iNumFrames == 0 ) // Model with no textures return; float fSongBeat = GAMESTATE->m_fSongBeat; float fPrecentIntoAnimation = fmodf(fSongBeat,fAnimationLengthInBeats) / fAnimationLengthInBeats; float fNoteBeatFraction = fmodf( fNoteBeat, 1.0f ); int iFrameNo = (int)(fPrecentIntoAnimation*iNumFrames); if( bVivid ) // changed to deal with the minor complaint that the color cycling is // one tick off in general iFrameNo += (int)( froundf((fNoteBeatFraction - 0.25f/fAnimationLengthInBeats),1.f/fAnimationLengthInBeats)*iNumFrames ); // just in case somehow we're majorly negative with the subtraction iFrameNo += (iNumFrames * 2); iFrameNo %= iNumFrames; ASSERT( iFrameNo>=0 && iFrameNom_bTapNoteAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Actor *pActorOut = m_pTapNote[nt]; SetActiveFrame( fNoteBeat, *pActorOut, cache->m_fTapNoteAnimationLengthInBeats, cache->m_bTapNoteAnimationIsVivid, cache->m_bTapNoteAnimationIsNoteColor ); return pActorOut; } Actor * NoteDisplay::GetTapAdditionActor( float fNoteBeat ) { NoteType nt = NoteType(0); if( cache->m_bTapAdditionAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Actor *pActorOut = m_pTapAddition[nt]; SetActiveFrame( fNoteBeat, *pActorOut, cache->m_fTapAdditionAnimationLengthInBeats, cache->m_bTapAdditionAnimationIsVivid, cache->m_bTapAdditionAnimationIsNoteColor ); return pActorOut; } Actor * NoteDisplay::GetTapMineActor( float fNoteBeat ) { SetActiveFrame( fNoteBeat, *m_pTapMine, cache->m_fTapMineAnimationLengthInBeats, cache->m_bTapMineAnimationIsVivid, false ); return m_pTapMine; } Sprite * NoteDisplay::GetHoldTopCapSprite( float fNoteBeat, bool bIsBeingHeld ) { NoteType nt = NoteType(0); if( cache->m_bHoldTopCapAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Sprite *pSpriteOut = bIsBeingHeld ? &m_sprHoldTopCapActive[nt] : &m_sprHoldTopCapInactive[nt]; SetActiveFrame( fNoteBeat, *pSpriteOut, cache->m_fHoldTopCapAnimationLengthInBeats, cache->m_bHoldTopCapAnimationIsVivid, cache->m_bHoldTopCapAnimationIsNoteColor ); return pSpriteOut; } Sprite * NoteDisplay::GetHoldBottomCapSprite( float fNoteBeat, bool bIsBeingHeld ) { NoteType nt = NoteType(0); if( cache->m_bHoldBottomCapAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Sprite *pSpriteOut = bIsBeingHeld ? &m_sprHoldBottomCapActive[nt] : &m_sprHoldBottomCapInactive[nt]; SetActiveFrame( fNoteBeat, *pSpriteOut, cache->m_fHoldBottomCapAnimationLengthInBeats, cache->m_bHoldBottomCapAnimationIsVivid, cache->m_bHoldBottomCapAnimationIsNoteColor ); return pSpriteOut; } Actor* NoteDisplay::GetHoldHeadActor( float fNoteBeat, bool bIsBeingHeld ) { NoteType nt = NoteType(0); if( cache->m_bHoldHeadAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Actor *pActorOut = bIsBeingHeld ? m_pHoldHeadActive[nt] : m_pHoldHeadInactive[nt]; SetActiveFrame( fNoteBeat, *pActorOut, cache->m_fHoldHeadAnimationLengthInBeats, cache->m_bHoldHeadAnimationIsVivid, cache->m_bHoldHeadAnimationIsNoteColor ); return pActorOut; } Sprite *NoteDisplay::GetHoldBodySprite( float fNoteBeat, bool bIsBeingHeld ) { NoteType nt = NoteType(0); if( cache->m_bHoldBodyAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Sprite *pSpriteOut = bIsBeingHeld ? &m_sprHoldBodyActive[nt] : &m_sprHoldBodyInactive[nt]; SetActiveFrame( fNoteBeat, *pSpriteOut, cache->m_fHoldBodyAnimationLengthInBeats, cache->m_bHoldBodyAnimationIsVivid, cache->m_bHoldBodyAnimationIsNoteColor ); return pSpriteOut; } Actor* NoteDisplay::GetHoldTailActor( float fNoteBeat, bool bIsBeingHeld ) { NoteType nt = NoteType(0); if( cache->m_bHoldTailAnimationIsNoteColor ) nt = BeatToNoteType( fNoteBeat ); // NOTE_TYPE_INVALID is 192nds at this point. if( nt == NOTE_TYPE_INVALID ) nt = NOTE_TYPE_192ND; nt = min( nt, (NoteType) (NOTE_COLOR_IMAGES-1) ); Actor *pActorOut = bIsBeingHeld ? m_pHoldTailActive[nt] : m_pHoldTailInactive[nt]; SetActiveFrame( fNoteBeat, *pActorOut, cache->m_fHoldTailAnimationLengthInBeats, cache->m_bHoldTailAnimationIsVivid, cache->m_bHoldTailAnimationIsNoteColor ); return pActorOut; } void NoteDisplay::DrawHoldTopCap( const HoldNote& hn, const bool bIsBeingHeld, float fYHead, float fYTail, int fYStep, int iCol, float fPercentFadeToFail, float fColorScale, bool bGlow ) { // // Draw the top cap (always wavy) // const int size = 4096; static RageSpriteVertex queue[size]; Sprite* pSprTopCap = GetHoldTopCapSprite( hn.GetStartBeat(), bIsBeingHeld ); // draw manually in small segments RageSpriteVertex *v = &queue[0]; RageTexture* pTexture = pSprTopCap->GetTexture(); const RectF *pRect = pSprTopCap->GetCurrentTextureCoordRect(); DISPLAY->ClearAllTextures(); DISPLAY->SetTexture( 0, pTexture ); DISPLAY->SetBlendMode( BLEND_NORMAL ); DISPLAY->SetCullMode( CULL_NONE ); DISPLAY->SetTextureWrapping(false); const float fFrameWidth = pSprTopCap->GetUnzoomedWidth(); const float fFrameHeight = pSprTopCap->GetUnzoomedHeight(); const float fYCapTop = fYHead+cache->m_iStartDrawingHoldBodyOffsetFromHead-fFrameHeight; const float fYCapBottom = fYHead+cache->m_iStartDrawingHoldBodyOffsetFromHead; for( float fY=fYCapTop; fYtop, pRect->bottom ); const float fTexCoordBottom = SCALE( fBottomDistFromHeadTop, 0, fFrameHeight, pRect->top, pRect->bottom ); ASSERT( fTexCoordTop>=-0.0001 && fTexCoordBottom<=1.0001 ); /* allow for rounding error */ const float fTexCoordLeft = pRect->left; const float fTexCoordRight = pRect->right; const float fAlphaTop = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffsetTop, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fAlphaBottom = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffsetBottom, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlowTop = ArrowGetGlow( m_PlayerNumber, iCol, fYOffsetTop, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlowBottom = ArrowGetGlow( m_PlayerNumber, iCol, fYOffsetBottom, fPercentFadeToFail, m_fYReverseOffsetPixels ); const RageColor colorDiffuseTop = RageColor(fColorScale,fColorScale,fColorScale,fAlphaTop); const RageColor colorDiffuseBottom = RageColor(fColorScale,fColorScale,fColorScale,fAlphaBottom); const RageColor colorGlowTop = RageColor(1,1,1,fGlowTop); const RageColor colorGlowBottom = RageColor(1,1,1,fGlowBottom); if( bGlow && colorGlowTop.a==0 && colorGlowBottom.a==0 ) continue; if( !bGlow && colorDiffuseTop.a==0 && colorDiffuseBottom.a==0 ) continue; v[0].p = RageVector3(fXTopLeft, fYTop, fZTop); v[0].c = bGlow? colorGlowTop : colorDiffuseTop; v[0].t = RageVector2(fTexCoordLeft, fTexCoordTop), v[1].p = RageVector3(fXTopRight, fYTop, fZTop); v[1].c = bGlow? colorGlowTop : colorDiffuseTop; v[1].t = RageVector2(fTexCoordRight, fTexCoordTop); v[2].p = RageVector3(fXBottomRight,fYBottom,fZBottom); v[2].c = bGlow? colorGlowBottom : colorDiffuseBottom; v[2].t = RageVector2(fTexCoordRight, fTexCoordBottom); v[3].p = RageVector3(fXBottomLeft, fYBottom,fZBottom); v[3].c = bGlow? colorGlowBottom : colorDiffuseBottom; v[3].t = RageVector2(fTexCoordLeft, fTexCoordBottom); v+=4; if( v-queue >= size ) break; } DISPLAY->DrawQuads( queue, v-queue ); } void NoteDisplay::DrawHoldBody( const HoldNote& hn, const bool bIsBeingHeld, float fYHead, float fYTail, int fYStep, int iCol, float fPercentFadeToFail, float fColorScale, bool bGlow ) { // // Draw the body (always wavy) // const int size = 4096; static RageSpriteVertex queue[size]; Sprite* pSprBody = GetHoldBodySprite( hn.GetStartBeat(), bIsBeingHeld ); // draw manually in small segments RageSpriteVertex *v = &queue[0]; RageTexture* pTexture = pSprBody->GetTexture(); const RectF *pRect = pSprBody->GetCurrentTextureCoordRect(); DISPLAY->ClearAllTextures(); DISPLAY->SetTexture( 0, pTexture ); DISPLAY->SetBlendMode( BLEND_NORMAL ); DISPLAY->SetCullMode( CULL_NONE ); DISPLAY->SetTextureWrapping( true ); const float fFrameWidth = pSprBody->GetUnzoomedWidth(); const float fFrameHeight = pSprBody->GetUnzoomedHeight(); const float fYBodyTop = fYHead + cache->m_iStartDrawingHoldBodyOffsetFromHead; const float fYBodyBottom = fYTail + cache->m_iStopDrawingHoldBodyOffsetFromTail; const bool bReverse = GAMESTATE->m_CurrentPlayerOptions[m_PlayerNumber].GetReversePercentForColumn(iCol) > 0.5; bool bAnchorToBottom = bReverse && cache->m_bFlipHeadAndTailWhenReverse; // top to bottom for( float fY=fYBodyTop; fY<=fYBodyBottom; fY+=fYStep ) { const float fYTop = fY; const float fYOffsetTop = ArrowGetYOffsetFromYPos( m_PlayerNumber, iCol, fYTop, m_fYReverseOffsetPixels ); const float fYBottom = min( fY+fYStep, fYBodyBottom ); const float fYOffsetBottom = ArrowGetYOffsetFromYPos( m_PlayerNumber, iCol, fYBottom, m_fYReverseOffsetPixels ); const float fZTop = ArrowGetZPos( m_PlayerNumber, iCol, fYOffsetTop ); const float fZBottom = ArrowGetZPos( m_PlayerNumber, iCol, fYOffsetBottom ); const float fXTop = ArrowGetXPos( m_PlayerNumber, iCol, fYOffsetTop ); const float fXBottom = ArrowGetXPos( m_PlayerNumber, iCol, fYOffsetBottom ); const float fXTopLeft = fXTop - fFrameWidth/2; const float fXTopRight = fXTop + fFrameWidth/2; const float fXBottomLeft = fXBottom - fFrameWidth/2; const float fXBottomRight = fXBottom + fFrameWidth/2; const float fTopDistFromBodyBottom = fYBodyBottom - fYTop; const float fBottomDistFromBodyBottom = fYBodyBottom - fYBottom; const float fTopDistFromBodyTop = fYTop - fYBodyTop; const float fBottomDistFromBodyTop = fYBottom - fYBodyTop; float fTexCoordTop = SCALE( bAnchorToBottom ? fTopDistFromBodyTop : fTopDistFromBodyBottom, 0, fFrameHeight, pRect->bottom, pRect->top ); float fTexCoordBottom = SCALE( bAnchorToBottom ? fBottomDistFromBodyTop : fBottomDistFromBodyBottom, 0, fFrameHeight, pRect->bottom, pRect->top ); if( fTexCoordTop < 0 ) { int iToSubtract = (int)fTexCoordTop - 1; fTexCoordTop -= iToSubtract; fTexCoordBottom -= iToSubtract; } else if( fTexCoordBottom > 2 ) { int iToSubtract = (int)fTexCoordBottom; fTexCoordTop -= iToSubtract; fTexCoordBottom -= iToSubtract; } if( fTexCoordBottom < 0 ) { int iToSubtract = (int)fTexCoordBottom - 1; fTexCoordTop -= iToSubtract; fTexCoordBottom -= iToSubtract; } else if( fTexCoordTop > 2 ) { int iToSubtract = (int)fTexCoordTop; fTexCoordTop -= iToSubtract; fTexCoordBottom -= iToSubtract; } ASSERT( fTexCoordTop>=0 && fTexCoordBottom>=0 ); ASSERT( fTexCoordTop<=2 && fTexCoordBottom<=2 ); const float fTexCoordLeft = pRect->left; const float fTexCoordRight = pRect->right; const float fAlphaTop = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffsetTop, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fAlphaBottom = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffsetBottom, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlowTop = ArrowGetGlow( m_PlayerNumber, iCol, fYOffsetTop, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlowBottom = ArrowGetGlow( m_PlayerNumber, iCol, fYOffsetBottom, fPercentFadeToFail, m_fYReverseOffsetPixels ); const RageColor colorDiffuseTop = RageColor(fColorScale,fColorScale,fColorScale,fAlphaTop); const RageColor colorDiffuseBottom = RageColor(fColorScale,fColorScale,fColorScale,fAlphaBottom); const RageColor colorGlowTop = RageColor(1,1,1,fGlowTop); const RageColor colorGlowBottom = RageColor(1,1,1,fGlowBottom); if( bGlow && colorGlowTop.a==0 && colorGlowBottom.a==0 ) continue; if( !bGlow && colorDiffuseTop.a==0 && colorDiffuseBottom.a==0 ) continue; v[0].p = RageVector3(fXTopLeft, fYTop, fZTop); v[0].c = bGlow? colorGlowTop : colorDiffuseTop; v[0].t = RageVector2(fTexCoordLeft, fTexCoordTop); v[1].p = RageVector3(fXTopRight, fYTop, fZTop); v[1].c = bGlow? colorGlowTop : colorDiffuseTop; v[1].t = RageVector2(fTexCoordRight, fTexCoordTop); v[2].p = RageVector3(fXBottomRight,fYBottom,fZBottom); v[2].c = bGlow? colorGlowBottom : colorDiffuseBottom; v[2].t = RageVector2(fTexCoordRight, fTexCoordBottom); v[3].p = RageVector3(fXBottomLeft, fYBottom,fZBottom); v[3].c = bGlow? colorGlowBottom : colorDiffuseBottom; v[3].t = RageVector2(fTexCoordLeft, fTexCoordBottom); v+=4; if( v-queue >= size ) break; } DISPLAY->DrawQuads( queue, v-queue ); } void NoteDisplay::DrawHoldBottomCap( const HoldNote& hn, const bool bIsBeingHeld, float fYHead, float fYTail, int fYStep, int iCol, float fPercentFadeToFail, float fColorScale, bool bGlow ) { // // Draw the bottom cap (always wavy) // const int size = 4096; static RageSpriteVertex queue[size]; Sprite* pBottomCap = GetHoldBottomCapSprite( hn.GetStartBeat(), bIsBeingHeld ); // draw manually in small segments RageSpriteVertex *v = &queue[0]; RageTexture* pTexture = pBottomCap->GetTexture(); const RectF *pRect = pBottomCap->GetCurrentTextureCoordRect(); DISPLAY->ClearAllTextures(); DISPLAY->SetTexture( 0, pTexture ); DISPLAY->SetBlendMode( BLEND_NORMAL ); DISPLAY->SetCullMode( CULL_NONE ); DISPLAY->SetTextureWrapping(false); const float fFrameWidth = pBottomCap->GetUnzoomedWidth(); const float fFrameHeight = pBottomCap->GetUnzoomedHeight(); const float fYCapTop = fYTail+cache->m_iStopDrawingHoldBodyOffsetFromTail; const float fYCapBottom = fYTail+cache->m_iStopDrawingHoldBodyOffsetFromTail+fFrameHeight; // don't draw any part of the tail that is before the middle of the head float fY=max( fYCapTop, fYHead ); for( ; fYtop, pRect->bottom ); const float fTexCoordBottom = SCALE( fBottomDistFromTailTop, 0, fFrameHeight, pRect->top, pRect->bottom ); const float fTexCoordLeft = pRect->left; const float fTexCoordRight = pRect->right; const float fAlphaTop = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffsetTop, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fAlphaBottom = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffsetBottom, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlowTop = ArrowGetGlow( m_PlayerNumber, iCol, fYOffsetTop, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlowBottom = ArrowGetGlow( m_PlayerNumber, iCol, fYOffsetBottom, fPercentFadeToFail, m_fYReverseOffsetPixels ); const RageColor colorDiffuseTop = RageColor(fColorScale,fColorScale,fColorScale,fAlphaTop); const RageColor colorDiffuseBottom = RageColor(fColorScale,fColorScale,fColorScale,fAlphaBottom); const RageColor colorGlowTop = RageColor(1,1,1,fGlowTop); const RageColor colorGlowBottom = RageColor(1,1,1,fGlowBottom); if( bGlow && colorGlowTop.a==0 && colorGlowBottom.a==0 ) continue; if( !bGlow && colorDiffuseTop.a==0 && colorDiffuseBottom.a==0 ) continue; v[0].p = RageVector3(fXTopLeft, fYTop, fZTop); v[0].c = bGlow ? colorGlowTop : colorDiffuseTop; v[0].t = RageVector2(fTexCoordLeft, fTexCoordTop), v[1].p = RageVector3(fXTopRight, fYTop, fZTop); v[1].c = bGlow ? colorGlowTop : colorDiffuseTop; v[1].t = RageVector2(fTexCoordRight, fTexCoordTop); v[2].p = RageVector3(fXBottomRight,fYBottom,fZBottom); v[2].c = bGlow ? colorGlowBottom : colorDiffuseBottom; v[2].t = RageVector2(fTexCoordRight, fTexCoordBottom); v[3].p = RageVector3(fXBottomLeft, fYBottom,fZBottom); v[3].c = bGlow ? colorGlowBottom : colorDiffuseBottom; v[3].t = RageVector2(fTexCoordLeft, fTexCoordBottom); v+=4; if( v-queue >= size ) break; } DISPLAY->DrawQuads( queue, v-queue ); } void NoteDisplay::DrawHoldTail( const HoldNote& hn, bool bIsBeingHeld, float fYTail, int iCol, float fPercentFadeToFail, float fColorScale, bool bGlow ) { // // Draw the tail // Actor* pSprTail = GetHoldTailActor( hn.GetStartBeat(), bIsBeingHeld ); const float fY = fYTail; const float fYOffset = ArrowGetYOffsetFromYPos( m_PlayerNumber, iCol, fY, m_fYReverseOffsetPixels ); const float fX = ArrowGetXPos( m_PlayerNumber, iCol, fYOffset ); const float fZ = ArrowGetZPos( m_PlayerNumber, iCol, fYOffset ); const float fAlpha = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlow = ArrowGetGlow( m_PlayerNumber, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels ); const RageColor colorDiffuse= RageColor(fColorScale,fColorScale,fColorScale,fAlpha); const RageColor colorGlow = RageColor(1,1,1,fGlow); pSprTail->SetXY( fX, fY ); pSprTail->SetZ( fZ ); if( bGlow ) { pSprTail->SetDiffuse( RageColor(1,1,1,0) ); pSprTail->SetGlow( colorGlow ); } else { pSprTail->SetDiffuse( colorDiffuse ); pSprTail->SetGlow( RageColor(0,0,0,0) ); } if( cache->m_bHoldTailUseLighting ) { DISPLAY->SetLighting( true ); DISPLAY->SetLightDirectional( 0, RageColor(1,1,1,1), RageColor(1,1,1,1), RageColor(1,1,1,1), RageVector3(1, 0, +1) ); } pSprTail->Draw(); if( cache->m_bHoldTailUseLighting ) { DISPLAY->SetLightOff( 0 ); DISPLAY->SetLighting( false ); } } void NoteDisplay::DrawHoldHead( const HoldNote& hn, bool bIsBeingHeld, float fYHead, int iCol, float fPercentFadeToFail, float fColorScale, bool bGlow ) { // // Draw the head // Actor* pActor = GetHoldHeadActor( hn.GetStartBeat(), bIsBeingHeld ); // draw with normal Sprite const float fY = fYHead; const float fYOffset = ArrowGetYOffsetFromYPos( m_PlayerNumber, iCol, fY, m_fYReverseOffsetPixels ); const float fX = ArrowGetXPos( m_PlayerNumber, iCol, fYOffset ); const float fZ = ArrowGetZPos( m_PlayerNumber, iCol, fYOffset ); const float fAlpha = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlow = ArrowGetGlow( m_PlayerNumber, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels ); const RageColor colorDiffuse= RageColor(fColorScale,fColorScale,fColorScale,fAlpha); const RageColor colorGlow = RageColor(1,1,1,fGlow); pActor->SetXY( fX, fY ); pActor->SetZ( fZ ); if( bGlow ) { pActor->SetDiffuse( RageColor(1,1,1,0) ); pActor->SetGlow( colorGlow ); } else { pActor->SetDiffuse( colorDiffuse ); pActor->SetGlow( RageColor(0,0,0,0) ); } if( cache->m_bHoldHeadUseLighting ) { DISPLAY->SetLighting( true ); DISPLAY->SetLightDirectional( 0, RageColor(1,1,1,1), RageColor(1,1,1,1), RageColor(1,1,1,1), RageVector3(1, 0, +1) ); } pActor->Draw(); if( cache->m_bHoldHeadUseLighting ) { DISPLAY->SetLightOff( 0 ); DISPLAY->SetLighting( false ); } } void NoteDisplay::DrawHold( const HoldNote& hn, bool bIsBeingHeld, bool bIsActive, float fLife, float fPercentFadeToFail, bool bDrawGlowOnly, float fReverseOffsetPixels ) { // 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 int iCol = hn.iTrack; bool bReverse = GAMESTATE->m_CurrentPlayerOptions[m_PlayerNumber].GetReversePercentForColumn(iCol) > 0.5; float fStartYOffset = ArrowGetYOffset( m_PlayerNumber, iCol, hn.GetStartBeat() ); // HACK: If active, don't allow the top of the hold to go above the receptor if( bIsActive ) fStartYOffset = 0; float fStartYPos = ArrowGetYPos( m_PlayerNumber, iCol, fStartYOffset, fReverseOffsetPixels ); float fEndYOffset = ArrowGetYOffset( m_PlayerNumber, iCol, hn.GetEndBeat() ); float fEndYPos = ArrowGetYPos( m_PlayerNumber, iCol, fEndYOffset, fReverseOffsetPixels ); const float fYHead = bReverse ? fEndYPos : fStartYPos; // the center of the head const float fYTail = bReverse ? fStartYPos : fEndYPos; // the center the tail // const bool bWavy = GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_fEffects[PlayerOptions::EFFECT_DRUNK] > 0; const bool WavyPartsNeedZBuffer = ArrowsNeedZBuffer( m_PlayerNumber ); /* Hack: Z effects need a finer grain step. */ const int fYStep = WavyPartsNeedZBuffer? 4: 16; //bWavy ? 16 : 128; // use small steps only if wavy const float fColorScale = 1*fLife + (1-fLife)*cache->m_fHoldNGGrayPercent; bool bFlipHeadAndTail = bReverse && cache->m_bFlipHeadAndTailWhenReverse; /* The body and caps should have no overlap, so their order doesn't matter. * Draw the head last, so it appears on top. */ if( !cache->m_bHoldHeadIsAboveWavyParts ) DrawHoldHead( hn, bIsBeingHeld, bFlipHeadAndTail ? fYTail : fYHead, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); if( !cache->m_bHoldTailIsAboveWavyParts ) DrawHoldTail( hn, bIsBeingHeld, bFlipHeadAndTail ? fYHead : fYTail, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); if( bDrawGlowOnly ) DISPLAY->SetTextureModeGlow(); else DISPLAY->SetTextureModeModulate(); DISPLAY->SetZBuffer( WavyPartsNeedZBuffer ); if( !bFlipHeadAndTail ) DrawHoldBottomCap( hn, bIsBeingHeld, fYHead, fYTail, fYStep, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); DrawHoldBody( hn, bIsBeingHeld, fYHead, fYTail, fYStep, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); if( bFlipHeadAndTail ) DrawHoldTopCap( hn, bIsBeingHeld, fYHead, fYTail, fYStep, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); /* These set the texture mode themselves. */ if( cache->m_bHoldTailIsAboveWavyParts ) DrawHoldTail( hn, bIsBeingHeld, bFlipHeadAndTail ? fYHead : fYTail, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); if( cache->m_bHoldHeadIsAboveWavyParts ) DrawHoldHead( hn, bIsBeingHeld, bFlipHeadAndTail ? fYTail : fYHead, iCol, fPercentFadeToFail, fColorScale, bDrawGlowOnly ); // now, draw the glow pass if( !bDrawGlowOnly ) DrawHold( hn, bIsBeingHeld, bIsActive, fLife, fPercentFadeToFail, true, fReverseOffsetPixels ); } void NoteDisplay::DrawActor( Actor* pActor, int iCol, float fBeat, float fPercentFadeToFail, float fLife, float fReverseOffsetPixels, bool bUseLighting ) { const float fYOffset = ArrowGetYOffset( m_PlayerNumber, iCol, fBeat ); const float fYPos = ArrowGetYPos( m_PlayerNumber, iCol, fYOffset, fReverseOffsetPixels ); const float fRotation = ArrowGetRotation( m_PlayerNumber, fBeat ); const float fXPos = ArrowGetXPos( m_PlayerNumber, iCol, fYOffset ); const float fZPos = ArrowGetZPos( m_PlayerNumber, iCol, fYOffset ); const float fAlpha = ArrowGetAlpha( m_PlayerNumber, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fGlow = ArrowGetGlow( m_PlayerNumber, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels ); const float fColorScale = ArrowGetBrightness( m_PlayerNumber, fBeat ) * SCALE(fLife,0,1,0.2f,1); RageColor diffuse = RageColor(fColorScale,fColorScale,fColorScale,fAlpha); RageColor glow = RageColor(1,1,1,fGlow); pActor->SetRotationZ( fRotation ); pActor->SetXY( fXPos, fYPos ); pActor->SetZ( fZPos ); pActor->SetDiffuse( diffuse ); pActor->SetGlow( glow ); if( bUseLighting ) { DISPLAY->SetLighting( true ); DISPLAY->SetLightDirectional( 0, RageColor(1,1,1,1), RageColor(1,1,1,1), RageColor(1,1,1,1), RageVector3(1, 0, +1) ); } pActor->Draw(); if( bUseLighting ) { DISPLAY->SetLightOff( 0 ); DISPLAY->SetLighting( false ); } } void NoteDisplay::DrawTap( int iCol, float fBeat, bool bOnSameRowAsHoldStart, bool bIsAddition, bool bIsMine, float fPercentFadeToFail, float fLife, float fReverseOffsetPixels ) { Actor* pActor = NULL; bool bUseLighting = false; if( bIsMine ) { pActor = GetTapMineActor( fBeat ); bUseLighting = cache->m_bTapMineUseLighting; } else if( bIsAddition ) { pActor = GetTapAdditionActor( fBeat ); bUseLighting = cache->m_bTapAdditionUseLighting; } else if( bOnSameRowAsHoldStart && cache->m_bDrawHoldHeadForTapsOnSameRow ) { pActor = GetHoldHeadActor( fBeat, false ); bUseLighting = cache->m_bHoldHeadUseLighting; } else { pActor = GetTapNoteActor( fBeat ); bUseLighting = cache->m_bTapNoteUseLighting; } DrawActor( pActor, iCol, fBeat, fPercentFadeToFail, fLife, fReverseOffsetPixels, bUseLighting ); }