Beispiel #1
0
void NoteDisplay::DrawHoldHead( const TapNote& tn, int iCol, int iRow, bool bIsBeingHeld, float fYHead, float fPercentFadeToFail, float fColorScale, bool bGlow, float fYStartOffset, float fYEndOffset )
{
	//
	// Draw the head
	//
	Actor* pActor = GetHoldHeadActor( NoteRowToBeat(iRow), tn.subType == TapNote::hold_head_roll, bIsBeingHeld );

	pActor->SetZoom( ArrowEffects::GetZoom( m_pPlayerState ) );

	// draw with normal Sprite
	const float fY				= fYHead;
	const float fYOffset		= ArrowEffects::GetYOffsetFromYPos( m_pPlayerState, iCol, fY, m_fYReverseOffsetPixels );
	if( fYOffset < fYStartOffset || fYOffset > fYEndOffset )
			return;

	// TRICKY: skew the rotation by a few pixels so this lines up with the start of the twirly hold.
	const float fRotationY		= ArrowEffects::GetRotationY( m_pPlayerState, fYOffset+16 );
	const float fX				= ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset );
	const float fZ				= ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset );
	const float	fAlpha			= ArrowEffects::GetAlpha( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels );
	const float	fGlow			= ArrowEffects::GetGlow( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels );
	const RageColor colorDiffuse= RageColor(fColorScale,fColorScale,fColorScale,fAlpha);
	const RageColor colorGlow	= RageColor(1,1,1,fGlow);

	pActor->SetRotationY( fRotationY );
	pActor->SetRotationZ( 0 );
	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 );
	}
}
Beispiel #2
0
void NoteDisplay::DrawHoldTail( const TapNote& tn, int iCol, int iRow, bool bIsBeingHeld, float fYTail, float fPercentFadeToFail, float fColorScale, bool bGlow, float fYStartOffset, float fYEndOffset )
{
	//
	// Draw the tail
	//
	Actor* pSprTail = GetHoldTailActor( NoteRowToBeat(iRow), tn.subType == TapNote::hold_head_roll, bIsBeingHeld );

	pSprTail->SetZoom( ArrowEffects::GetZoom( m_pPlayerState ) );

	const float fY				= fYTail;
	const float fYOffset		= ArrowEffects::GetYOffsetFromYPos( m_pPlayerState, iCol, fY, m_fYReverseOffsetPixels );
	if( fYOffset < fYStartOffset || fYOffset > fYEndOffset )
			return;

	const float fRotationY		= ArrowEffects::GetRotationY( m_pPlayerState, fYOffset );
	const float fX				= ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset );
	const float fZ				= ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset );
	const float	fAlpha			= ArrowEffects::GetAlpha( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels );
	const float	fGlow			= ArrowEffects::GetGlow( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels );
	const RageColor colorDiffuse= RageColor(fColorScale,fColorScale,fColorScale,fAlpha);
	const RageColor colorGlow	= RageColor(1,1,1,fGlow);

	pSprTail->SetRotationY( fRotationY );
	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 );
	}
}
Beispiel #3
0
static void Serialize( const NoteData &o, Json::Value &root )
{
	root = Json::Value(Json::arrayValue);
	for(int t=0; t < o.GetNumTracks(); t++ )
	{
		NoteData::TrackMap::const_iterator begin, end;
		o.GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
		//NoteData::TrackMap tm = o.GetTrack(t);
		//FOREACHM_CONST( int, TapNote, tm, iter )
		for( ; begin != end; ++begin )
		{
			int iRow = begin->first;
			TapNote tn = begin->second;
			root.resize( root.size()+1 );
			Json::Value &root2 = root[ root.size()-1 ];
			root2 = Json::Value(Json::arrayValue);
			root2.resize(3);
			root2[(unsigned)0] = NoteRowToBeat(iRow);
			root2[1] = t;
			Serialize( tn, root2[2] );
		}
	}
}
Beispiel #4
0
void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const BackgroundChange &change )
{
	int iStartRow = BeatToNoteRow(fFirstBeat);
	int iEndRow = BeatToNoteRow(fEndBeat);

	const TimingData &timing = m_pSong->m_SongTiming;

	// change BG every time signature change or 4 measures
	const vector<TimingSegment *> &tSigs = timing.GetTimingSegments(SEGMENT_TIME_SIG);

	for (unsigned i = 0; i < tSigs.size(); i++)
	{
		TimeSignatureSegment *ts = static_cast<TimeSignatureSegment *>(tSigs[i]);
		int iSegmentEndRow = (i + 1 == tSigs.size()) ? iEndRow : tSigs[i+1]->GetRow();


		for(int j=max(ts->GetRow(),iStartRow);
			j<min(iEndRow,iSegmentEndRow);
			j+=4*ts->GetNoteRowsPerMeasure())
		{
			// Don't fade. It causes frame rate dip, especially on slower machines.
			BackgroundDef bd = m_Layer[0].CreateRandomBGA(m_pSong,
														  change.m_def.m_sEffect,
														  m_RandomBGAnimations, this);
			if( !bd.IsEmpty() )
			{
				BackgroundChange c = change;
				c.m_def = bd;
				c.m_fStartBeat = NoteRowToBeat(j);
				m_Layer[0].m_aBGChanges.push_back( c );
			}
		}
	}

	// change BG every BPM change that is at the beginning of a measure
	const vector<TimingSegment *> &bpms = timing.GetTimingSegments(SEGMENT_BPM);
	for( unsigned i=0; i<bpms.size(); i++ )
	{
		bool bAtBeginningOfMeasure = false;
		for (unsigned j=0; j<tSigs.size(); j++)
		{
			TimeSignatureSegment *ts = static_cast<TimeSignatureSegment *>(tSigs[j]);
			if ((bpms[i]->GetRow() - ts->GetRow()) % ts->GetNoteRowsPerMeasure() == 0)
			{
				bAtBeginningOfMeasure = true;
				break;
			}
		}

		if( !bAtBeginningOfMeasure )
			continue; // skip

		// start so that we don't create a BGChange right on top of fEndBeat
		bool bInRange = bpms[i]->GetRow() >= iStartRow && bpms[i]->GetRow() < iEndRow;
		if( !bInRange )
			continue; // skip

		BackgroundDef bd = m_Layer[0].CreateRandomBGA( m_pSong, change.m_def.m_sEffect, m_RandomBGAnimations, this );
		if( !bd.IsEmpty() )
		{
			BackgroundChange c = change;
			c.m_def.m_sFile1 = bd.m_sFile1;
			c.m_def.m_sFile2 = bd.m_sFile2;
			c.m_fStartBeat = bpms[i]->GetBeat();
			m_Layer[0].m_aBGChanges.push_back( c );
		}
	}
}
Beispiel #5
0
float NoteData::GetLastBeat() const
{ 
	return NoteRowToBeat( GetLastRow() );
}
void AutoKeysounds::LoadAutoplaySoundsInto( RageSoundReader_Chain *pChain )
{
	//
	// Load sounds.
	//
	Song* pSong = GAMESTATE->m_pCurSong;
	RString sSongDir = pSong->GetSongDir();

	/*
	 * Add all current autoplay sounds in both players to the chain.
	 */
	int iNumTracks = m_ndAutoKeysoundsOnly[GAMESTATE->GetMasterPlayerNumber()].GetNumTracks();
	for( int t = 0; t < iNumTracks; t++ )
	{
		int iRow = -1;
		while(1)
		{
			/* Find the next row that either player has a note on. */
			int iNextRow = INT_MAX;
			FOREACH_EnabledPlayer(pn)
			{
				// XXX Hack. Enabled players need not have their own note data.
				if( t >= m_ndAutoKeysoundsOnly[pn].GetNumTracks() )
					continue;
				int iNextRowForPlayer = iRow;
				/* XXX: If a BMS file only has one tap note per track,
				 * this will prevent any keysounds from loading.
				 * This leads to failure later on.
				 * We need a better way to prevent this. */
				if( m_ndAutoKeysoundsOnly[pn].GetNextTapNoteRowForTrack( t, iNextRowForPlayer ) )
					iNextRow = min( iNextRow, iNextRowForPlayer );
			}

			if( iNextRow == INT_MAX )
				break;
			iRow = iNextRow;

			TapNote tn[NUM_PLAYERS];
			FOREACH_EnabledPlayer(pn)
				tn[pn] = m_ndAutoKeysoundsOnly[pn].GetTapNote( t, iRow );

			FOREACH_EnabledPlayer(pn)
			{
				if( tn[pn] == TAP_EMPTY )
					continue;

				ASSERT( tn[pn].type == TapNoteType_AutoKeysound );
				if( tn[pn].iKeysoundIndex >= 0 )
				{
					RString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[tn[pn].iKeysoundIndex];
					float fSeconds = GAMESTATE->m_pCurSteps[pn]->GetTimingData()->GetElapsedTimeFromBeatNoOffset( NoteRowToBeat(iRow) ) + SOUNDMAN->GetPlayLatency();

					float fPan = 0;
					// If two players are playing, pan the keysounds to each player's respective side
					if( GAMESTATE->GetNumPlayersEnabled() == 2 )
						fPan = (pn == PLAYER_1)? -1.0f:+1.0f;
					int iIndex = pChain->LoadSound( sKeysoundFilePath );
					pChain->AddSound( iIndex, fSeconds, fPan );
				}
			}
		}
	}
}
void NoteDataWithScoring::GetActualRadarValues(const NoteData &in,
	const PlayerStageStats &pss, float song_seconds, RadarValues& out)
{
	// Anybody editing this function should also examine
	// NoteDataUtil::CalculateRadarValues to make sure it handles things the
	// same way.
	// Some of this logic is similar or identical to
	// NoteDataUtil::CalculateRadarValues because I couldn't figure out a good
	// way to combine them into one. -Kyz
	PlayerNumber pn= pss.m_player_number;
	garv_state state;

	NoteData::all_tracks_const_iterator curr_note=
		in.GetTapNoteRangeAllTracks(0, MAX_NOTE_ROW);
	TimingData* timing= GAMESTATE->GetProcessedTimingData();
	// first_hittable_row and last_hittable_row exist so that
	// GetActualVoltageRadarValue can be passed the correct song length.
	// GetActualVoltageRadarValue scores based on the max combo, a full combo
	// is a full voltage score.  The song length is used instead of trying to
	// figure out the max combo for the song because rolls mean there isn't a
	// limit to the max combo. -Kyz
	int first_hittable_row= -1;
	int last_hittable_row= -1;
	bool tick_holds= GAMESTATE->GetCurrentGame()->m_bTickHolds;

	while(!curr_note.IsAtEnd())
	{
		if(curr_note.Row() != state.curr_row)
		{
			DoRowEndRadarActualCalc(state, out);
			state.curr_row= curr_note.Row();
			state.num_notes_on_curr_row= 0;
			state.num_holds_on_curr_row= 0;
			state.judgable= timing->IsJudgableAtRow(state.curr_row);
			for(size_t n= 0; n < state.hold_ends.size(); ++n)
			{
				if(state.hold_ends[n].end_row < state.curr_row)
				{
					state.hold_ends.erase(state.hold_ends.begin() + n);
					--n;
				}
			}
			state.last_tns_on_row= TapNoteScore_Invalid;
			state.last_time_on_row= -9999;
			state.worst_tns_on_row= TapNoteScore_Invalid;
		}
		bool for_this_player= curr_note->pn == pn || pn == PLAYER_INVALID ||
			curr_note->pn == PLAYER_INVALID;
		if(state.judgable && for_this_player)
		{
			switch(curr_note->type)
			{
				case TapNoteType_HoldTail:
					// If there are tick holds, then the hold tail needs to be counted
					// in last_hittable_row because that's where the combo will end.
					// -Kyz
					if(tick_holds)
					{
						UpdateHittable(state.curr_row, first_hittable_row, last_hittable_row);
					}
					break;
				case TapNoteType_Tap:
				case TapNoteType_HoldHead:
					// HoldTails and Attacks are counted by IsTap.  But it doesn't
					// make sense to count HoldTails as hittable notes. -Kyz
				case TapNoteType_Attack:
				case TapNoteType_Lift:
					UpdateHittable(state.curr_row, first_hittable_row, last_hittable_row);
					++state.num_notes_on_curr_row;
					state.notes_hit_for_stream+= (curr_note->result.tns >= state.stream_tns);
					state.notes_hit+= (curr_note->result.tns >= state.taps_tns);
					if(curr_note->result.tns < state.worst_tns_on_row)
					{
						state.worst_tns_on_row= curr_note->result.tns;
					}
					if(curr_note->result.fTapNoteOffset > state.last_time_on_row)
					{
						state.last_time_on_row= curr_note->result.fTapNoteOffset;
						state.last_tns_on_row= curr_note->result.tns;
					}
					if(curr_note->type == TapNoteType_HoldHead)
					{
						if(curr_note->subType == TapNoteSubType_Hold)
						{
							state.holds_held+= (curr_note->HoldResult.hns == HNS_Held);
						}
						else if(curr_note->subType == TapNoteSubType_Roll)
						{
							state.rolls_held+= (curr_note->HoldResult.hns == HNS_Held);
						}
						state.hold_ends.push_back(
							hold_status(state.curr_row + curr_note->iDuration,
								curr_note->HoldResult.iLastHeldRow));
						++state.num_holds_on_curr_row;
					}
					else if(curr_note->type == TapNoteType_Lift)
					{
						state.lifts_hit+= (curr_note->result.tns >= state.lifts_tns);
					}
					break;
				case TapNoteType_Mine:
					state.mines_avoided+= (curr_note->result.tns == TNS_AvoidMine);
					break;
				case TapNoteType_Fake:
				default:
					break;
			}
		}
		++curr_note;
	}
	DoRowEndRadarActualCalc(state, out);

	// ScreenGameplay passes in the RadarValues that were calculated by
	// NoteDataUtil::CalculateRadarValues, so those are reused here. -Kyz
	int note_count= out[RadarCategory_Notes];
	int jump_count= out[RadarCategory_Jumps];
	int hold_count= out[RadarCategory_Holds];
	int tap_count= out[RadarCategory_TapsAndHolds];
	float hittable_steps_length= max(0, 
		timing->GetElapsedTimeFromBeat(NoteRowToBeat(last_hittable_row)) -
		timing->GetElapsedTimeFromBeat(NoteRowToBeat(first_hittable_row)));
	// The for loop and the assert are used to ensure that all fields of 
	// RadarValue get set in here.
	FOREACH_ENUM(RadarCategory, rc)
	{
		switch(rc)
		{
			case RadarCategory_Stream:
				out[rc]= clamp(float(state.notes_hit_for_stream) / note_count, 0.0f, 1.0f);
				break;
			case RadarCategory_Voltage:
				out[rc]= GetActualVoltageRadarValue(in, hittable_steps_length, pss);
				break;
			case RadarCategory_Air:
				out[rc]= clamp(float(state.jumps_hit_for_air) / jump_count, 0.0f, 1.0f);
				break;
			case RadarCategory_Freeze:
				out[rc]= clamp(float(state.holds_held) / hold_count, 0.0f, 1.0f);
				break;
			case RadarCategory_Chaos:
				out[rc]= GetActualChaosRadarValue(in, song_seconds, pss);
				break;
			case RadarCategory_TapsAndHolds:
				out[rc]= state.taps_hit;
				break;
			case RadarCategory_Jumps:
				out[rc]= state.jumps_hit;
				break;
			case RadarCategory_Holds:
				out[rc]= state.holds_held;
				break;
			case RadarCategory_Mines:
				out[rc]= state.mines_avoided;
				break;
			case RadarCategory_Hands:
				out[rc]= state.hands_hit;
				break;
			case RadarCategory_Rolls:
				out[rc]= state.rolls_held;
				break;
			case RadarCategory_Lifts:
				out[rc]= state.lifts_hit;
				break;
			case RadarCategory_Fakes:
				out[rc]= out[rc];
				break;
			case RadarCategory_Notes:
				out[rc]= state.notes_hit;
				break;
			DEFAULT_FAIL(rc);
		}
	}
}
Beispiel #8
0
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 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

	bool bReverse = m_pPlayerState->m_CurrentPlayerOptions.GetReversePercentForColumn(iCol) > 0.5f;
	float fStartBeat = NoteRowToBeat( max(tn.HoldResult.iLastHeldRow, iRow) );
	float fThrowAway = 0;

	// 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 )
		;	// 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 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 );

	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 = ArrowEffects::NeedZBuffer( m_pPlayerState );
	/* 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		= tn.HoldResult.fLife + (1-tn.HoldResult.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( tn, iCol, iRow, bIsBeingHeld, bFlipHeadAndTail ? fYTail : fYHead, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );
	if( !cache->m_bHoldTailIsAboveWavyParts )
		DrawHoldTail( tn, iCol, iRow, bIsBeingHeld, bFlipHeadAndTail ? fYHead : fYTail, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );

	if( bDrawGlowOnly )
		DISPLAY->SetTextureModeGlow();
	else
		DISPLAY->SetTextureModeModulate();
	DISPLAY->SetZTestMode( WavyPartsNeedZBuffer?ZTEST_WRITE_ON_PASS:ZTEST_OFF );
	DISPLAY->SetZWrite( WavyPartsNeedZBuffer );
	
	if( !bFlipHeadAndTail )
		DrawHoldBottomCap( tn, iCol, iRow, bIsBeingHeld, fYHead, fYTail, fYStep, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );
	DrawHoldBody( tn, iCol, iRow, bIsBeingHeld, fYHead, fYTail, fYStep, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );
	if( bFlipHeadAndTail )
		DrawHoldTopCap( tn, iCol, iRow, bIsBeingHeld, fYHead, fYTail, fYStep, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );

	/* These set the texture mode themselves. */
	if( cache->m_bHoldTailIsAboveWavyParts )
		DrawHoldTail( tn, iCol, iRow, bIsBeingHeld, bFlipHeadAndTail ? fYHead : fYTail, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );
	if( cache->m_bHoldHeadIsAboveWavyParts )
		DrawHoldHead( tn, iCol, iRow, bIsBeingHeld, bFlipHeadAndTail ? fYTail : fYHead, fPercentFadeToFail, fColorScale, bDrawGlowOnly, fYStartOffset, fYEndOffset );

	// now, draw the glow pass
	if( !bDrawGlowOnly )
		DrawHold( tn, iCol, iRow, bIsBeingHeld, bIsActive, Result, fPercentFadeToFail, true, fReverseOffsetPixels, fYStartOffset, fYEndOffset );
}
Beispiel #9
0
void NoteDisplay::DrawHoldBottomCap( const TapNote& tn, int iCol, int iRow, bool bIsBeingHeld, float fYHead, float fYTail, int	fYStep, float fPercentFadeToFail, float fColorScale, bool bGlow, float fYStartOffset, float fYEndOffset )
{
	//
	// Draw the bottom cap (always wavy)
	//
	StripBuffer queue;

	Sprite* pBottomCap = GetHoldBottomCapSprite( NoteRowToBeat(iRow), tn.subType == TapNote::hold_head_roll, bIsBeingHeld );

	pBottomCap->SetZoom( ArrowEffects::GetZoom( m_pPlayerState ) );

	// draw manually in small segments
	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->GetZoomedWidth();
	const float fFrameHeight	= pBottomCap->GetZoomedHeight();
	const float fYCapTop		= fYTail+cache->m_iStopDrawingHoldBodyOffsetFromTail;
	const float fYCapBottom		= fYTail+cache->m_iStopDrawingHoldBodyOffsetFromTail+fFrameHeight;

	bool bReverse = m_pPlayerState->m_CurrentPlayerOptions.GetReversePercentForColumn(iCol) > 0.5f;

	if( bGlow )
		fColorScale = 1;

	float fDrawYCapTop;
	float fDrawYCapBottom;
	{
		float fYStartPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYStartOffset, m_fYReverseOffsetPixels );
		float fYEndPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYEndOffset, m_fYReverseOffsetPixels );
		fDrawYCapTop = max( fYCapTop, bReverse ? fYEndPos : fYStartPos );
		fDrawYCapBottom = min( fYCapBottom, bReverse ? fYStartPos : fYEndPos );
	}

	bool bAllAreTransparent = true;
	bool bLast = false;
	// don't draw any part of the tail that is before the middle of the head
	float fY=max( fDrawYCapTop, fYHead );
	for( ; !bLast; fY += fYStep )
	{
		if( fY >= fDrawYCapBottom )
		{
			fY = fDrawYCapBottom;
			bLast = true;
		}

		const float fYOffset		= ArrowEffects::GetYOffsetFromYPos( m_pPlayerState, iCol, fY, m_fYReverseOffsetPixels );
		const float fX			= ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset );
		const float fZ			= ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset );

		// XXX: Actor rotations use degrees, RageFastCos/Sin use radians. Convert here.
		const float fRotationY		= ArrowEffects::GetRotationY( m_pPlayerState, fYOffset ) * PI/180;

		// if we're rotating, we need to modify the X and Z coords for the outer edges.
		const float fRotOffsetX		= fFrameWidth/2 * RageFastCos(fRotationY);
		const float fRotOffsetZ		= fFrameWidth/2 * RageFastSin(fRotationY);
		const float fXLeft		= fX - fRotOffsetX;
		const float fXRight		= fX + fRotOffsetX;
		const float fZLeft		= fZ - fRotOffsetZ;
		const float fZRight		= fZ + fRotOffsetZ;

		const float fTopDistFromTail	= fY - fYCapTop;
		const float fTexCoordTop	= SCALE( fTopDistFromTail,    0, fFrameHeight, pRect->top, pRect->bottom );
		const float fTexCoordLeft	= pRect->left;
		const float fTexCoordRight	= pRect->right;
		const float	fAlpha		= ArrowGetAlphaOrGlow( bGlow, m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels );
		const RageColor color		= RageColor(fColorScale,fColorScale,fColorScale,fAlpha);

		if( fAlpha > 0 )
			bAllAreTransparent = false;

		queue.v[0].p = RageVector3(fXLeft,  fY, fZLeft);	queue.v[0].c = color; queue.v[0].t = RageVector2(fTexCoordLeft,  fTexCoordTop);
		queue.v[1].p = RageVector3(fXRight, fY, fZRight);	queue.v[1].c = color; queue.v[1].t = RageVector2(fTexCoordRight, fTexCoordTop);

		queue.v+=2;
		if( queue.Free() < 2 )
		{
			/* The queue is full.  Render it, clear the buffer, and move back a step to
			 * start off the quad strip again. */
			if( !bAllAreTransparent )
				queue.Draw();
			queue.Init();
			bAllAreTransparent = true;
			fY -= fYStep;
		}
	}
	if( !bAllAreTransparent )
		queue.Draw();
}
Beispiel #10
0
void NoteDisplay::DrawHoldBody( const TapNote& tn, int iCol, int iRow, bool bIsBeingHeld, float fYHead, float fYTail, int fYStep, float fPercentFadeToFail, float fColorScale, bool bGlow,
							   float fYStartOffset, float fYEndOffset )
{
	//
	// Draw the body (always wavy)
	//
	StripBuffer queue;

	Sprite* pSprBody = GetHoldBodySprite( NoteRowToBeat(iRow), tn.subType == TapNote::hold_head_roll, bIsBeingHeld );

	pSprBody->SetZoom( ArrowEffects::GetZoom( m_pPlayerState ) );

	// draw manually in small segments
	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->GetZoomedWidth();
	const float fFrameHeight = pSprBody->GetZoomedHeight();
	const float fYBodyTop = fYHead + cache->m_iStartDrawingHoldBodyOffsetFromHead;
	const float fYBodyBottom = fYTail + cache->m_iStopDrawingHoldBodyOffsetFromTail;

	const bool bReverse = m_pPlayerState->m_CurrentPlayerOptions.GetReversePercentForColumn(iCol) > 0.5f;
	bool bAnchorToBottom = bReverse && cache->m_bFlipHeadAndTailWhenReverse;

	if( bGlow )
		fColorScale = 1;

	/* Only draw the section that's within the range specified.  If a hold note is
	 * very long, don't process or draw the part outside of the range.  Don't change
	 * fYBodyTop or fYBodyBottom; they need to be left alone to calculate texture
	 * coordinates. */
	float fDrawYBodyTop;
	float fDrawYBodyBottom;
	{
		float fYStartPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYStartOffset, m_fYReverseOffsetPixels );
		float fYEndPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYEndOffset, m_fYReverseOffsetPixels );

		fDrawYBodyTop = max( fYBodyTop, bReverse ? fYEndPos : fYStartPos );
		fDrawYBodyBottom = min( fYBodyBottom, bReverse ? fYStartPos : fYEndPos );
	}

	// top to bottom
	bool bAllAreTransparent = true;
	bool bLast = false;
	float fVertTexCoordOffset = 0;
	for( float fY = fDrawYBodyTop; !bLast; fY += fYStep )
	{
		if( fY >= fDrawYBodyBottom )
		{
			fY = fDrawYBodyBottom;
			bLast = true;
		}

		const float fYOffset		= ArrowEffects::GetYOffsetFromYPos( m_pPlayerState, iCol, fY, m_fYReverseOffsetPixels );
		const float fX			= ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset );
		const float fZ			= ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset );

		// XXX: Actor rotations use degrees, RageFastCos/Sin use radians. Convert here.
		const float fRotationY		= ArrowEffects::GetRotationY( m_pPlayerState, fYOffset ) * PI/180;

		// if we're rotating, we need to modify the X and Z coords for the outer edges.
		const float fRotOffsetX		= fFrameWidth/2 * RageFastCos(fRotationY);
		const float fRotOffsetZ		= fFrameWidth/2 * RageFastSin(fRotationY);
		const float fXLeft		= fX - fRotOffsetX;
		const float fXRight		= fX + fRotOffsetX;
		const float fZLeft		= fZ - fRotOffsetZ;
		const float fZRight		= fZ + fRotOffsetZ;

		const float fDistFromBodyBottom	= fYBodyBottom - fY;
		const float fDistFromBodyTop	= fY - fYBodyTop;
		float fTexCoordTop		= SCALE( bAnchorToBottom ? fDistFromBodyTop : fDistFromBodyBottom,    0, fFrameHeight, pRect->bottom, pRect->top );
		/* For very large hold notes, shift the texture coordinates to be near 0, so we
		 * don't send very large values to the renderer. */
		if( fY == fDrawYBodyTop ) // first
				fVertTexCoordOffset = floorf( fTexCoordTop );
		fTexCoordTop -= fVertTexCoordOffset;
		const float fTexCoordLeft		= pRect->left;
		const float fTexCoordRight		= pRect->right;
		const float	fAlpha				= ArrowGetAlphaOrGlow( bGlow, m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels );
		const RageColor color			= RageColor(fColorScale,fColorScale,fColorScale,fAlpha);

		if( fAlpha > 0 )
			bAllAreTransparent = false;

		queue.v[0].p = RageVector3(fXLeft,  fY, fZLeft);	queue.v[0].c = color; queue.v[0].t = RageVector2(fTexCoordLeft,  fTexCoordTop);
		queue.v[1].p = RageVector3(fXRight, fY, fZRight);	queue.v[1].c = color; queue.v[1].t = RageVector2(fTexCoordRight, fTexCoordTop);
		queue.v+=2;
		if( queue.Free() < 2 )
		{
			/* The queue is full.  Render it, clear the buffer, and move back a step to
			 * start off the quad strip again. */
			if( !bAllAreTransparent )
				queue.Draw();
			queue.Init();
			bAllAreTransparent = true;
			fY -= fYStep;
		}
	}

	if( !bAllAreTransparent )
		queue.Draw();
}
Beispiel #11
0
	static int GetHoldDuration( T* p, lua_State* L )		{ lua_pushnumber(L, NoteRowToBeat(p->iDuration)); return 1; }
Beispiel #12
0
float HoldNoteResult::GetLastHeldBeat() const
{
	return NoteRowToBeat(iLastHeldRow);
}