From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/client/c_rumble.cpp | 1652 +++++++++++++++++++-------------------- 1 file changed, 826 insertions(+), 826 deletions(-) (limited to 'mp/src/game/client/c_rumble.cpp') diff --git a/mp/src/game/client/c_rumble.cpp b/mp/src/game/client/c_rumble.cpp index a54858b6..a22ef884 100644 --- a/mp/src/game/client/c_rumble.cpp +++ b/mp/src/game/client/c_rumble.cpp @@ -1,826 +1,826 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Rumble effects mixer for XBox -// -// $NoKeywords: $ -// -//=============================================================================// -#include "cbase.h" -#include "c_rumble.h" -#include "rumble_shared.h" -#include "inputsystem/iinputsystem.h" - -ConVar cl_rumblescale( "cl_rumblescale", "1.0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Scale sensitivity of rumble effects (0 to 1.0)" ); -ConVar cl_debugrumble( "cl_debugrumble", "0", FCVAR_ARCHIVE, "Turn on rumble debugging spew" ); - -#define MAX_RUMBLE_CHANNELS 3 // Max concurrent rumble effects - -#define NUM_WAVE_SAMPLES 30 // Effects play at 10hz - -typedef struct -{ - float amplitude_left[NUM_WAVE_SAMPLES]; - float amplitude_right[NUM_WAVE_SAMPLES]; - int numSamples; -} RumbleWaveform_t; - -//========================================================= -// Structure for a rumble effect channel. This is akin to -// a sound channel that is playing a sound. -//========================================================= -typedef struct -{ - float starttime; // When did this effect start playing? (gpGlobals->curtime) - int waveformIndex; // Type of effect waveform used (an enum from rumble_shared.h) - int priority; // How important this effect is (for making replacement decisions) - bool in_use; // Is this channel in use?? (true if effect is currently playing, false if done or otherwise available) - unsigned char rumbleFlags; // Flags pertaining to the effect currently playing on this channel. - float scale; // Some effects are updated while they are running. -} RumbleChannel_t; - -//========================================================= -// This structure contains parameters necessary to generate -// a sine or sawtooth waveform. -//========================================================= -typedef struct tagWaveGenParams -{ - float cycles; // AKA frequency - float amplitudescale; - bool leftChannel; // If false, generating for the right channel - - float maxAmplitude; // Clamping - float minAmplitude; - - void Set( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude ) - { - cycles = c_cycles; - amplitudescale = c_amplitudescale; - leftChannel = c_leftChannel; - minAmplitude = c_minAmplitude; - maxAmplitude = c_maxAmplitude; - } - - // CTOR - tagWaveGenParams( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude ) - { - Set( c_cycles, c_amplitudescale, c_leftChannel, c_minAmplitude, c_maxAmplitude ); - } - -} WaveGenParams_t; - -//--------------------------------------------------------- -//--------------------------------------------------------- -void TerminateWaveform( RumbleWaveform_t *pWaveform, int samples ) -{ - if( samples <= NUM_WAVE_SAMPLES ) - { - pWaveform->numSamples = samples; - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void EaseInWaveform( RumbleWaveform_t *pWaveform, int samples, bool left ) -{ - float step = 1.0f / ((float)samples); - float factor = 0.0f; - - for( int i = 0 ; i < samples ; i++ ) - { - if( left ) - { - pWaveform->amplitude_left[i] *= factor; - } - else - { - pWaveform->amplitude_right[i] *= factor; - } - - factor += step; - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void EaseOutWaveform( RumbleWaveform_t *pWaveform, int samples, bool left ) -{ - float step = 1.0f / ((float)samples); - float factor = 0.0f; - - int i = NUM_WAVE_SAMPLES - 1; - - for( int j = 0 ; j < samples ; j++ ) - { - if( left ) - { - pWaveform->amplitude_left[i] *= factor; - } - else - { - pWaveform->amplitude_right[i] *= factor; - } - - factor += step; - i--; - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void GenerateSawtoothEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) -{ - float delta = params.maxAmplitude - params.minAmplitude; - int waveLength = NUM_WAVE_SAMPLES / params.cycles; - float vstep = (delta / waveLength); - - float amplitude = params.minAmplitude; - - for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) - { - if( params.leftChannel ) - { - pWaveform->amplitude_left[i] = amplitude; - } - else - { - pWaveform->amplitude_right[i] = amplitude; - } - - amplitude += vstep; - - if( amplitude > params.maxAmplitude ) - { - amplitude = params.minAmplitude; - } - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void GenerateSquareWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) -{ - int i = 0; - int j; - - int steps = ((float)NUM_WAVE_SAMPLES) / (params.cycles*2.0f); - - while( i < NUM_WAVE_SAMPLES ) - { - for( j = 0 ; j < steps ; j++ ) - { - if( params.leftChannel ) - { - pWaveform->amplitude_left[i++] = params.minAmplitude; - } - else - { - pWaveform->amplitude_right[i++] = params.minAmplitude; - } - } - for( j = 0 ; j < steps ; j++ ) - { - if( params.leftChannel ) - { - pWaveform->amplitude_left[i++] = params.maxAmplitude; - } - else - { - pWaveform->amplitude_right[i++] = params.maxAmplitude; - } - } - } -} - -//--------------------------------------------------------- -// If you pass a numSamples, this wave will only be that many -// samples long. -//--------------------------------------------------------- -void GenerateFlatEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) -{ - for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) - { - if( params.leftChannel ) - { - pWaveform->amplitude_left[i] = params.maxAmplitude; - } - else - { - pWaveform->amplitude_right[i] = params.maxAmplitude; - } - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void GenerateSineWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) -{ - float step = (360.0f * (params.cycles * 0.5f) ) / ((float)NUM_WAVE_SAMPLES); - float degrees = 180.0f + step; // 180 to start at 0 - - for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) - { - float radians = DEG2RAD(degrees); - float value = fabs( sin(radians) ); - - value *= params.amplitudescale; - - if( value < params.minAmplitude ) - value = params.minAmplitude; - - if( value > params.maxAmplitude ) - value = params.maxAmplitude; - - if( params.leftChannel ) - { - pWaveform->amplitude_left[i] = value; - } - else - { - pWaveform->amplitude_right[i] = value; - } - - degrees += step; - } -} - -//========================================================= -//========================================================= -class CRumbleEffects -{ -public: - CRumbleEffects() - { - Init(); - } - - void Init(); - void SetOutputEnabled( bool bEnable ); - void StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ); - void StopEffect( int effectIndex ); - void StopAllEffects(); - void ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ); - void UpdateEffects( float curtime ); - void UpdateScreenShakeRumble( float shake, float balance ); - - RumbleChannel_t *FindExistingChannel( int index ); - RumbleChannel_t *FindAvailableChannel( int priority ); - -public: - RumbleChannel_t m_Channels[ MAX_RUMBLE_CHANNELS ]; - - RumbleWaveform_t m_Waveforms[ NUM_RUMBLE_EFFECTS ]; - - float m_flScreenShake; - bool m_bOutputEnabled; -}; - - -CRumbleEffects g_RumbleEffects; - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::Init() -{ - SetOutputEnabled( true ); - - int i; - - for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - m_Channels[i].in_use = false; - m_Channels[i].priority = 0; - } - - // Every effect defaults to this many samples. Call TerminateWaveform() to trim these. - for ( i = 0 ; i < NUM_RUMBLE_EFFECTS ; i++ ) - { - m_Waveforms[i].numSamples = NUM_WAVE_SAMPLES; - } - - // Jeep Idle - WaveGenParams_t params( 1, 1.0f, false, 0.0f, 0.15f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_JEEP_ENGINE_LOOP], params ); - - // Pistol - params.Set( 1, 1.0f, false, 0.0f, 0.5f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_PISTOL], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_PISTOL], 3 ); - - // SMG1 - params.Set( 1, 1.0f, true, 0.0f, 0.2f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); - params.Set( 1, 1.0f, false, 0.0f, 0.4f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_SMG1], 3 ); - - // AR2 - params.Set( 1, 1.0f, true, 0.0f, 0.5f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params ); - params.Set( 1, 1.0f, false, 0.0f, 0.3f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_AR2], 3 ); - - // AR2 Alt - params.Set( 1, 1.0f, true, 0.0, 0.5f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params ); - EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, true ); - params.Set( 1, 1.0f, false, 0.0, 0.7f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params ); - EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, false ); - TerminateWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 7 ); - - // 357 - params.Set( 1, 1.0f, true, 0.0f, 0.75f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params ); - params.Set( 1, 1.0f, false, 0.0f, 0.75f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_357], 2 ); - - // Shotgun - params.Set( 1, 1.0f, true, 0.0f, 0.8f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); - params.Set( 1, 1.0f, false, 0.0f, 0.8f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], 3 ); - - params.Set( 1, 1.0f, true, 0.0f, 1.0f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params ); - params.Set( 1, 1.0f, false, 0.0f, 1.0f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], 3 ); - - // RPG Missile - params.Set( 1, 1.0f, false, 0.0f, 0.3f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_RPG_MISSILE], params ); - EaseOutWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 30, false ); - TerminateWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 6 ); - - // Physcannon open forks - params.Set( 1, 1.0f, false, 0.0f, 0.25f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], 4 ); - - // Physcannon holding something - params.Set( 1, 1.0f, true, 0.0f, 0.2f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params ); - params.Set( 6, 1.0f, false, 0.0f, 0.25f ); - GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params ); - - // Crowbar - params.Set( 1, 1.0f, false, 0.0f, 0.35f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_CROWBAR_SWING], params ); - EaseOutWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 30, false ); - TerminateWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 4 ); - - // Airboat gun - params.Set( 1, 1.0f, false, 0.0f, 0.4f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params ); - params.Set( 12, 1.0f, true, 0.0f, 0.5f ); - GenerateSawtoothEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params ); - - // Generic flat effects. - params.Set( 1, 1.0f, true, 0.0f, 1.0f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_LEFT], params ); - - params.Set( 1, 1.0f, false, 0.0f, 1.0f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_RIGHT], params ); - - params.Set( 1, 1.0f, true, 0.0f, 1.0f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params ); - params.Set( 1, 1.0f, false, 0.0f, 1.0f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params ); - - // Impact from a long fall - params.Set( 1, 1.0f, false, 0.0f, 0.5f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params ); - params.Set( 1, 1.0f, true, 0.0f, 0.5f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_FALL_LONG], 3 ); - - // Impact from a short fall - params.Set( 1, 1.0f, false, 0.0f, 0.3f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params ); - params.Set( 1, 1.0f, true, 0.0f, 0.3f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_FALL_SHORT], 2 ); - - // Portalgun left (blue) shot - params.Set( 1, 1.0f, true, 0.0f, 0.3f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], 2 ); - - // Portalgun right (red) shot - params.Set( 1, 1.0f, false, 0.0f, 0.3f ); - GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], 2 ); - - // Portal failed to place feedback - params.Set( 12, 1.0f, true, 0.0f, 1.0f ); - GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); - params.Set( 12, 1.0f, false, 0.0f, 1.0f ); - GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); - TerminateWaveform( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], 6 ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -RumbleChannel_t *CRumbleEffects::FindExistingChannel( int index ) -{ - RumbleChannel_t *pChannel; - - for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - pChannel = &m_Channels[i]; - - if( pChannel->in_use && pChannel->waveformIndex == index ) - { - // This effect is already playing. Provide this channel for the - // effect to be re-started on. - return pChannel; - } - } - - return NULL; -} - -//--------------------------------------------------------- -// priority - the priority of the effect we want to play. -//--------------------------------------------------------- -RumbleChannel_t *CRumbleEffects::FindAvailableChannel( int priority ) -{ - RumbleChannel_t *pChannel; - int i; - - for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - pChannel = &m_Channels[i]; - - if( !pChannel->in_use ) - { - return pChannel; - } - } - - int lowestPriority = priority; - RumbleChannel_t *pBestChannel = NULL; - float oldestChannel = FLT_MAX; - - // All channels already in use. Find a channel to slam. - for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - pChannel = &m_Channels[i]; - - if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) - continue; - - if( pChannel->priority < lowestPriority ) - { - // Always happily slam a lower priority sound. - pBestChannel = pChannel; - lowestPriority = pChannel->priority; - } - else if( pChannel->priority == lowestPriority ) - { - // Priority is the same, so replace the oldest. - if( pBestChannel ) - { - // If we already have a channel of the same priority to discard, make sure we discard the oldest. - float age = gpGlobals->curtime - pChannel->starttime; - - if( age > oldestChannel ) - { - pBestChannel = pChannel; - oldestChannel = age; - } - } - else - { - // Take it. - pBestChannel = pChannel; - oldestChannel = gpGlobals->curtime - pChannel->starttime; - } - } - } - - return pBestChannel; // Can still be NULL if we couldn't find a channel to slam. -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::SetOutputEnabled( bool bEnable ) -{ - m_bOutputEnabled = bEnable; - - if( !bEnable ) - { - // Tell the hardware to shut down motors right now, in case this gets called - // and some other process blocks us before the next rumble system update. - m_flScreenShake = 0.0f; - - inputsystem->StopRumble(); - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) -{ - if( effectIndex == RUMBLE_STOP_ALL ) - { - StopAllEffects(); - return; - } - - if( rumbleFlags & RUMBLE_FLAG_STOP ) - { - StopEffect( effectIndex ); - return; - } - - int priority = 1; - RumbleChannel_t *pChannel = NULL; - - if( (rumbleFlags & RUMBLE_FLAG_RESTART) ) - { - // Try to find any active instance of this effect and replace it. - pChannel = FindExistingChannel( effectIndex ); - } - - if( (rumbleFlags & RUMBLE_FLAG_ONLYONE) ) - { - pChannel = FindExistingChannel( effectIndex ); - - if( pChannel ) - { - // Bail out. An instance of this effect is already playing. - return; - } - } - - if( (rumbleFlags & RUMBLE_FLAG_UPDATE_SCALE) ) - { - pChannel = FindExistingChannel( effectIndex ); - if( pChannel ) - { - pChannel->scale = ((float)rumbleData) / 100.0f; - } - - // It's possible to return without finding a rumble to update. - // This means you tried to update a rumble you never started. - return; - } - - if( !pChannel ) - { - pChannel = FindAvailableChannel( priority ); - } - - if( pChannel ) - { - pChannel->waveformIndex = effectIndex; - pChannel->priority = 1; - pChannel->starttime = gpGlobals->curtime; - pChannel->in_use = true; - pChannel->rumbleFlags = rumbleFlags; - - if( rumbleFlags & RUMBLE_FLAG_INITIAL_SCALE ) - { - pChannel->scale = ((float)rumbleData) / 100.0f; - } - else - { - pChannel->scale = 1.0f; - } - } - - if( (rumbleFlags & RUMBLE_FLAG_RANDOM_AMPLITUDE) ) - { - pChannel->scale = random->RandomFloat( 0.1f, 1.0f ); - } -} - -//--------------------------------------------------------- -// Find all playing effects of this type and stop them. -//--------------------------------------------------------- -void CRumbleEffects::StopEffect( int effectIndex ) -{ - for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - if( m_Channels[i].in_use && m_Channels[i].waveformIndex == effectIndex ) - { - m_Channels[i].in_use = false; - } - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::StopAllEffects() -{ - for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - m_Channels[i].in_use = false; - } - - m_flScreenShake = 0.0f; -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ) -{ - // How long has this waveform been playing? - float elapsed = curtime - pChannel->starttime; - - if( elapsed >= (NUM_WAVE_SAMPLES/10) ) - { - if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) - { - // This effect loops. Just fixup the start time and recompute elapsed. - pChannel->starttime = curtime; - elapsed = curtime - pChannel->starttime; - } - else - { - // This effect is done! Should it loop? - *pLeft = 0; - *pRight = 0; - pChannel->in_use = false; - return; - } - } - - // Figure out which sample we're playing FROM. - int seconds = ((int) elapsed); - int sample = (int)(elapsed*10.0f); - - // Get the fraction bit. - float fraction = elapsed - seconds; - - float left, right; - - if( sample == m_Waveforms[pChannel->waveformIndex].numSamples ) - { - // This effect is done. Send zeroes to the mixer for this - // final frame and then turn the channel off. (Unless it loops!) - - if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) - { - // Loop this effect - pChannel->starttime = gpGlobals->curtime; - - // Send the first sample. - left = m_Waveforms[pChannel->waveformIndex].amplitude_left[0]; - right = m_Waveforms[pChannel->waveformIndex].amplitude_right[0]; - } - else - { - left = 0.0f; - right = 0.0f; - pChannel->in_use = false; - } - } - else - { - // Use values for the last sample that we have passed - left = m_Waveforms[pChannel->waveformIndex].amplitude_left[sample]; - right = m_Waveforms[pChannel->waveformIndex].amplitude_right[sample]; - } - - left *= pChannel->scale; - right *= pChannel->scale; - - if( cl_debugrumble.GetBool() ) - { - Msg("Seconds:%d Fraction:%f Sample:%d L:%f R:%f\n", seconds, fraction, sample, left, right ); - } - - if( !m_bOutputEnabled ) - { - // Send zeroes to stop any current rumbling, and to keep it silenced. - left = 0; - right = 0; - } - - *pLeft = left; - *pRight = right; -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::UpdateScreenShakeRumble( float shake, float balance ) -{ - if( m_bOutputEnabled ) - { - m_flScreenShake = shake; - } - else - { - // Silence - m_flScreenShake = 0.0f; - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CRumbleEffects::UpdateEffects( float curtime ) -{ - float fLeftMotor = 0.0f; - float fRightMotor = 0.0f; - - for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) - { - // Expire old channels - RumbleChannel_t *pChannel = & m_Channels[i]; - - if( pChannel->in_use ) - { - float left, right; - - ComputeAmplitudes( pChannel, curtime, &left, &right ); - - fLeftMotor += left; - fRightMotor += right; - } - } - - // Add in any screenshake - float shakeLeft = 0.0f; - float shakeRight = 0.0f; - if( m_flScreenShake != 0.0f ) - { - if( m_flScreenShake < 0.0f ) - { - shakeLeft = fabs( m_flScreenShake ); - } - else - { - shakeRight = m_flScreenShake; - } - } - - fLeftMotor += shakeLeft; - fRightMotor += shakeRight; - - fLeftMotor *= cl_rumblescale.GetFloat(); - fRightMotor *= cl_rumblescale.GetFloat(); - - if( engine->IsPaused() ) - { - // Send nothing when paused. - fLeftMotor = 0.0f; - fRightMotor = 0.0f; - } - - inputsystem->SetRumble( fLeftMotor, fRightMotor ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void StopAllRumbleEffects( void ) -{ - g_RumbleEffects.StopAllEffects(); - - inputsystem->StopRumble(); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void RumbleEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) -{ - g_RumbleEffects.StartEffect( effectIndex, rumbleData, rumbleFlags ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void UpdateRumbleEffects() -{ - C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); - if( !localPlayer || !localPlayer->IsAlive() ) - { - StopAllRumbleEffects(); - return; - } - - g_RumbleEffects.UpdateEffects( gpGlobals->curtime ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void UpdateScreenShakeRumble( float shake, float balance ) -{ - C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); - if( !localPlayer || !localPlayer->IsAlive() ) - { - return; - } - - g_RumbleEffects.UpdateScreenShakeRumble( shake, balance ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void EnableRumbleOutput( bool bEnable ) -{ - g_RumbleEffects.SetOutputEnabled( bEnable ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Rumble effects mixer for XBox +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "c_rumble.h" +#include "rumble_shared.h" +#include "inputsystem/iinputsystem.h" + +ConVar cl_rumblescale( "cl_rumblescale", "1.0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Scale sensitivity of rumble effects (0 to 1.0)" ); +ConVar cl_debugrumble( "cl_debugrumble", "0", FCVAR_ARCHIVE, "Turn on rumble debugging spew" ); + +#define MAX_RUMBLE_CHANNELS 3 // Max concurrent rumble effects + +#define NUM_WAVE_SAMPLES 30 // Effects play at 10hz + +typedef struct +{ + float amplitude_left[NUM_WAVE_SAMPLES]; + float amplitude_right[NUM_WAVE_SAMPLES]; + int numSamples; +} RumbleWaveform_t; + +//========================================================= +// Structure for a rumble effect channel. This is akin to +// a sound channel that is playing a sound. +//========================================================= +typedef struct +{ + float starttime; // When did this effect start playing? (gpGlobals->curtime) + int waveformIndex; // Type of effect waveform used (an enum from rumble_shared.h) + int priority; // How important this effect is (for making replacement decisions) + bool in_use; // Is this channel in use?? (true if effect is currently playing, false if done or otherwise available) + unsigned char rumbleFlags; // Flags pertaining to the effect currently playing on this channel. + float scale; // Some effects are updated while they are running. +} RumbleChannel_t; + +//========================================================= +// This structure contains parameters necessary to generate +// a sine or sawtooth waveform. +//========================================================= +typedef struct tagWaveGenParams +{ + float cycles; // AKA frequency + float amplitudescale; + bool leftChannel; // If false, generating for the right channel + + float maxAmplitude; // Clamping + float minAmplitude; + + void Set( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude ) + { + cycles = c_cycles; + amplitudescale = c_amplitudescale; + leftChannel = c_leftChannel; + minAmplitude = c_minAmplitude; + maxAmplitude = c_maxAmplitude; + } + + // CTOR + tagWaveGenParams( float c_cycles, float c_amplitudescale, bool c_leftChannel, float c_minAmplitude, float c_maxAmplitude ) + { + Set( c_cycles, c_amplitudescale, c_leftChannel, c_minAmplitude, c_maxAmplitude ); + } + +} WaveGenParams_t; + +//--------------------------------------------------------- +//--------------------------------------------------------- +void TerminateWaveform( RumbleWaveform_t *pWaveform, int samples ) +{ + if( samples <= NUM_WAVE_SAMPLES ) + { + pWaveform->numSamples = samples; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void EaseInWaveform( RumbleWaveform_t *pWaveform, int samples, bool left ) +{ + float step = 1.0f / ((float)samples); + float factor = 0.0f; + + for( int i = 0 ; i < samples ; i++ ) + { + if( left ) + { + pWaveform->amplitude_left[i] *= factor; + } + else + { + pWaveform->amplitude_right[i] *= factor; + } + + factor += step; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void EaseOutWaveform( RumbleWaveform_t *pWaveform, int samples, bool left ) +{ + float step = 1.0f / ((float)samples); + float factor = 0.0f; + + int i = NUM_WAVE_SAMPLES - 1; + + for( int j = 0 ; j < samples ; j++ ) + { + if( left ) + { + pWaveform->amplitude_left[i] *= factor; + } + else + { + pWaveform->amplitude_right[i] *= factor; + } + + factor += step; + i--; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void GenerateSawtoothEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + float delta = params.maxAmplitude - params.minAmplitude; + int waveLength = NUM_WAVE_SAMPLES / params.cycles; + float vstep = (delta / waveLength); + + float amplitude = params.minAmplitude; + + for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i] = amplitude; + } + else + { + pWaveform->amplitude_right[i] = amplitude; + } + + amplitude += vstep; + + if( amplitude > params.maxAmplitude ) + { + amplitude = params.minAmplitude; + } + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void GenerateSquareWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + int i = 0; + int j; + + int steps = ((float)NUM_WAVE_SAMPLES) / (params.cycles*2.0f); + + while( i < NUM_WAVE_SAMPLES ) + { + for( j = 0 ; j < steps ; j++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i++] = params.minAmplitude; + } + else + { + pWaveform->amplitude_right[i++] = params.minAmplitude; + } + } + for( j = 0 ; j < steps ; j++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i++] = params.maxAmplitude; + } + else + { + pWaveform->amplitude_right[i++] = params.maxAmplitude; + } + } + } +} + +//--------------------------------------------------------- +// If you pass a numSamples, this wave will only be that many +// samples long. +//--------------------------------------------------------- +void GenerateFlatEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) + { + if( params.leftChannel ) + { + pWaveform->amplitude_left[i] = params.maxAmplitude; + } + else + { + pWaveform->amplitude_right[i] = params.maxAmplitude; + } + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void GenerateSineWaveEffect( RumbleWaveform_t *pWaveform, const WaveGenParams_t ¶ms ) +{ + float step = (360.0f * (params.cycles * 0.5f) ) / ((float)NUM_WAVE_SAMPLES); + float degrees = 180.0f + step; // 180 to start at 0 + + for( int i = 0 ; i < NUM_WAVE_SAMPLES ; i++ ) + { + float radians = DEG2RAD(degrees); + float value = fabs( sin(radians) ); + + value *= params.amplitudescale; + + if( value < params.minAmplitude ) + value = params.minAmplitude; + + if( value > params.maxAmplitude ) + value = params.maxAmplitude; + + if( params.leftChannel ) + { + pWaveform->amplitude_left[i] = value; + } + else + { + pWaveform->amplitude_right[i] = value; + } + + degrees += step; + } +} + +//========================================================= +//========================================================= +class CRumbleEffects +{ +public: + CRumbleEffects() + { + Init(); + } + + void Init(); + void SetOutputEnabled( bool bEnable ); + void StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ); + void StopEffect( int effectIndex ); + void StopAllEffects(); + void ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ); + void UpdateEffects( float curtime ); + void UpdateScreenShakeRumble( float shake, float balance ); + + RumbleChannel_t *FindExistingChannel( int index ); + RumbleChannel_t *FindAvailableChannel( int priority ); + +public: + RumbleChannel_t m_Channels[ MAX_RUMBLE_CHANNELS ]; + + RumbleWaveform_t m_Waveforms[ NUM_RUMBLE_EFFECTS ]; + + float m_flScreenShake; + bool m_bOutputEnabled; +}; + + +CRumbleEffects g_RumbleEffects; + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::Init() +{ + SetOutputEnabled( true ); + + int i; + + for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + m_Channels[i].in_use = false; + m_Channels[i].priority = 0; + } + + // Every effect defaults to this many samples. Call TerminateWaveform() to trim these. + for ( i = 0 ; i < NUM_RUMBLE_EFFECTS ; i++ ) + { + m_Waveforms[i].numSamples = NUM_WAVE_SAMPLES; + } + + // Jeep Idle + WaveGenParams_t params( 1, 1.0f, false, 0.0f, 0.15f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_JEEP_ENGINE_LOOP], params ); + + // Pistol + params.Set( 1, 1.0f, false, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PISTOL], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PISTOL], 3 ); + + // SMG1 + params.Set( 1, 1.0f, true, 0.0f, 0.2f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.4f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SMG1], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_SMG1], 3 ); + + // AR2 + params.Set( 1, 1.0f, true, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_AR2], 3 ); + + // AR2 Alt + params.Set( 1, 1.0f, true, 0.0, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params ); + EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, true ); + params.Set( 1, 1.0f, false, 0.0, 0.7f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], params ); + EaseInWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 5, false ); + TerminateWaveform( &m_Waveforms[RUMBLE_AR2_ALT_FIRE], 7 ); + + // 357 + params.Set( 1, 1.0f, true, 0.0f, 0.75f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.75f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_357], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_357], 2 ); + + // Shotgun + params.Set( 1, 1.0f, true, 0.0f, 0.8f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); + params.Set( 1, 1.0f, false, 0.0f, 0.8f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_SINGLE], 3 ); + + params.Set( 1, 1.0f, true, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params ); + params.Set( 1, 1.0f, false, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_SHOTGUN_DOUBLE], 3 ); + + // RPG Missile + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_RPG_MISSILE], params ); + EaseOutWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 30, false ); + TerminateWaveform( &m_Waveforms[RUMBLE_RPG_MISSILE], 6 ); + + // Physcannon open forks + params.Set( 1, 1.0f, false, 0.0f, 0.25f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PHYSCANNON_OPEN], 4 ); + + // Physcannon holding something + params.Set( 1, 1.0f, true, 0.0f, 0.2f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params ); + params.Set( 6, 1.0f, false, 0.0f, 0.25f ); + GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PHYSCANNON_LOW], params ); + + // Crowbar + params.Set( 1, 1.0f, false, 0.0f, 0.35f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_CROWBAR_SWING], params ); + EaseOutWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 30, false ); + TerminateWaveform( &m_Waveforms[RUMBLE_CROWBAR_SWING], 4 ); + + // Airboat gun + params.Set( 1, 1.0f, false, 0.0f, 0.4f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params ); + params.Set( 12, 1.0f, true, 0.0f, 0.5f ); + GenerateSawtoothEffect( &m_Waveforms[RUMBLE_AIRBOAT_GUN], params ); + + // Generic flat effects. + params.Set( 1, 1.0f, true, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_LEFT], params ); + + params.Set( 1, 1.0f, false, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_RIGHT], params ); + + params.Set( 1, 1.0f, true, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params ); + params.Set( 1, 1.0f, false, 0.0f, 1.0f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FLAT_BOTH], params ); + + // Impact from a long fall + params.Set( 1, 1.0f, false, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params ); + params.Set( 1, 1.0f, true, 0.0f, 0.5f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_LONG], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_FALL_LONG], 3 ); + + // Impact from a short fall + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params ); + params.Set( 1, 1.0f, true, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_FALL_SHORT], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_FALL_SHORT], 2 ); + + // Portalgun left (blue) shot + params.Set( 1, 1.0f, true, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_LEFT], 2 ); + + // Portalgun right (red) shot + params.Set( 1, 1.0f, false, 0.0f, 0.3f ); + GenerateFlatEffect( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PORTALGUN_RIGHT], 2 ); + + // Portal failed to place feedback + params.Set( 12, 1.0f, true, 0.0f, 1.0f ); + GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); + params.Set( 12, 1.0f, false, 0.0f, 1.0f ); + GenerateSquareWaveEffect( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], params ); + TerminateWaveform( &m_Waveforms[RUMBLE_PORTAL_PLACEMENT_FAILURE], 6 ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +RumbleChannel_t *CRumbleEffects::FindExistingChannel( int index ) +{ + RumbleChannel_t *pChannel; + + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + pChannel = &m_Channels[i]; + + if( pChannel->in_use && pChannel->waveformIndex == index ) + { + // This effect is already playing. Provide this channel for the + // effect to be re-started on. + return pChannel; + } + } + + return NULL; +} + +//--------------------------------------------------------- +// priority - the priority of the effect we want to play. +//--------------------------------------------------------- +RumbleChannel_t *CRumbleEffects::FindAvailableChannel( int priority ) +{ + RumbleChannel_t *pChannel; + int i; + + for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + pChannel = &m_Channels[i]; + + if( !pChannel->in_use ) + { + return pChannel; + } + } + + int lowestPriority = priority; + RumbleChannel_t *pBestChannel = NULL; + float oldestChannel = FLT_MAX; + + // All channels already in use. Find a channel to slam. + for( i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + pChannel = &m_Channels[i]; + + if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) + continue; + + if( pChannel->priority < lowestPriority ) + { + // Always happily slam a lower priority sound. + pBestChannel = pChannel; + lowestPriority = pChannel->priority; + } + else if( pChannel->priority == lowestPriority ) + { + // Priority is the same, so replace the oldest. + if( pBestChannel ) + { + // If we already have a channel of the same priority to discard, make sure we discard the oldest. + float age = gpGlobals->curtime - pChannel->starttime; + + if( age > oldestChannel ) + { + pBestChannel = pChannel; + oldestChannel = age; + } + } + else + { + // Take it. + pBestChannel = pChannel; + oldestChannel = gpGlobals->curtime - pChannel->starttime; + } + } + } + + return pBestChannel; // Can still be NULL if we couldn't find a channel to slam. +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::SetOutputEnabled( bool bEnable ) +{ + m_bOutputEnabled = bEnable; + + if( !bEnable ) + { + // Tell the hardware to shut down motors right now, in case this gets called + // and some other process blocks us before the next rumble system update. + m_flScreenShake = 0.0f; + + inputsystem->StopRumble(); + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::StartEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) +{ + if( effectIndex == RUMBLE_STOP_ALL ) + { + StopAllEffects(); + return; + } + + if( rumbleFlags & RUMBLE_FLAG_STOP ) + { + StopEffect( effectIndex ); + return; + } + + int priority = 1; + RumbleChannel_t *pChannel = NULL; + + if( (rumbleFlags & RUMBLE_FLAG_RESTART) ) + { + // Try to find any active instance of this effect and replace it. + pChannel = FindExistingChannel( effectIndex ); + } + + if( (rumbleFlags & RUMBLE_FLAG_ONLYONE) ) + { + pChannel = FindExistingChannel( effectIndex ); + + if( pChannel ) + { + // Bail out. An instance of this effect is already playing. + return; + } + } + + if( (rumbleFlags & RUMBLE_FLAG_UPDATE_SCALE) ) + { + pChannel = FindExistingChannel( effectIndex ); + if( pChannel ) + { + pChannel->scale = ((float)rumbleData) / 100.0f; + } + + // It's possible to return without finding a rumble to update. + // This means you tried to update a rumble you never started. + return; + } + + if( !pChannel ) + { + pChannel = FindAvailableChannel( priority ); + } + + if( pChannel ) + { + pChannel->waveformIndex = effectIndex; + pChannel->priority = 1; + pChannel->starttime = gpGlobals->curtime; + pChannel->in_use = true; + pChannel->rumbleFlags = rumbleFlags; + + if( rumbleFlags & RUMBLE_FLAG_INITIAL_SCALE ) + { + pChannel->scale = ((float)rumbleData) / 100.0f; + } + else + { + pChannel->scale = 1.0f; + } + } + + if( (rumbleFlags & RUMBLE_FLAG_RANDOM_AMPLITUDE) ) + { + pChannel->scale = random->RandomFloat( 0.1f, 1.0f ); + } +} + +//--------------------------------------------------------- +// Find all playing effects of this type and stop them. +//--------------------------------------------------------- +void CRumbleEffects::StopEffect( int effectIndex ) +{ + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + if( m_Channels[i].in_use && m_Channels[i].waveformIndex == effectIndex ) + { + m_Channels[i].in_use = false; + } + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::StopAllEffects() +{ + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + m_Channels[i].in_use = false; + } + + m_flScreenShake = 0.0f; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::ComputeAmplitudes( RumbleChannel_t *pChannel, float curtime, float *pLeft, float *pRight ) +{ + // How long has this waveform been playing? + float elapsed = curtime - pChannel->starttime; + + if( elapsed >= (NUM_WAVE_SAMPLES/10) ) + { + if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) + { + // This effect loops. Just fixup the start time and recompute elapsed. + pChannel->starttime = curtime; + elapsed = curtime - pChannel->starttime; + } + else + { + // This effect is done! Should it loop? + *pLeft = 0; + *pRight = 0; + pChannel->in_use = false; + return; + } + } + + // Figure out which sample we're playing FROM. + int seconds = ((int) elapsed); + int sample = (int)(elapsed*10.0f); + + // Get the fraction bit. + float fraction = elapsed - seconds; + + float left, right; + + if( sample == m_Waveforms[pChannel->waveformIndex].numSamples ) + { + // This effect is done. Send zeroes to the mixer for this + // final frame and then turn the channel off. (Unless it loops!) + + if( (pChannel->rumbleFlags & RUMBLE_FLAG_LOOP) ) + { + // Loop this effect + pChannel->starttime = gpGlobals->curtime; + + // Send the first sample. + left = m_Waveforms[pChannel->waveformIndex].amplitude_left[0]; + right = m_Waveforms[pChannel->waveformIndex].amplitude_right[0]; + } + else + { + left = 0.0f; + right = 0.0f; + pChannel->in_use = false; + } + } + else + { + // Use values for the last sample that we have passed + left = m_Waveforms[pChannel->waveformIndex].amplitude_left[sample]; + right = m_Waveforms[pChannel->waveformIndex].amplitude_right[sample]; + } + + left *= pChannel->scale; + right *= pChannel->scale; + + if( cl_debugrumble.GetBool() ) + { + Msg("Seconds:%d Fraction:%f Sample:%d L:%f R:%f\n", seconds, fraction, sample, left, right ); + } + + if( !m_bOutputEnabled ) + { + // Send zeroes to stop any current rumbling, and to keep it silenced. + left = 0; + right = 0; + } + + *pLeft = left; + *pRight = right; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::UpdateScreenShakeRumble( float shake, float balance ) +{ + if( m_bOutputEnabled ) + { + m_flScreenShake = shake; + } + else + { + // Silence + m_flScreenShake = 0.0f; + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CRumbleEffects::UpdateEffects( float curtime ) +{ + float fLeftMotor = 0.0f; + float fRightMotor = 0.0f; + + for( int i = 0 ; i < MAX_RUMBLE_CHANNELS ; i++ ) + { + // Expire old channels + RumbleChannel_t *pChannel = & m_Channels[i]; + + if( pChannel->in_use ) + { + float left, right; + + ComputeAmplitudes( pChannel, curtime, &left, &right ); + + fLeftMotor += left; + fRightMotor += right; + } + } + + // Add in any screenshake + float shakeLeft = 0.0f; + float shakeRight = 0.0f; + if( m_flScreenShake != 0.0f ) + { + if( m_flScreenShake < 0.0f ) + { + shakeLeft = fabs( m_flScreenShake ); + } + else + { + shakeRight = m_flScreenShake; + } + } + + fLeftMotor += shakeLeft; + fRightMotor += shakeRight; + + fLeftMotor *= cl_rumblescale.GetFloat(); + fRightMotor *= cl_rumblescale.GetFloat(); + + if( engine->IsPaused() ) + { + // Send nothing when paused. + fLeftMotor = 0.0f; + fRightMotor = 0.0f; + } + + inputsystem->SetRumble( fLeftMotor, fRightMotor ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void StopAllRumbleEffects( void ) +{ + g_RumbleEffects.StopAllEffects(); + + inputsystem->StopRumble(); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void RumbleEffect( unsigned char effectIndex, unsigned char rumbleData, unsigned char rumbleFlags ) +{ + g_RumbleEffects.StartEffect( effectIndex, rumbleData, rumbleFlags ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void UpdateRumbleEffects() +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( !localPlayer || !localPlayer->IsAlive() ) + { + StopAllRumbleEffects(); + return; + } + + g_RumbleEffects.UpdateEffects( gpGlobals->curtime ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void UpdateScreenShakeRumble( float shake, float balance ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( !localPlayer || !localPlayer->IsAlive() ) + { + return; + } + + g_RumbleEffects.UpdateScreenShakeRumble( shake, balance ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void EnableRumbleOutput( bool bEnable ) +{ + g_RumbleEffects.SetOutputEnabled( bEnable ); +} -- cgit v1.2.3