//----------------------------------------------------------------------------- // Name: OnPlayEffects() // Desc: Plays all of the effects enumerated in the file //----------------------------------------------------------------------------- HRESULT OnPlayEffects( HWND hDlg ) { EFFECTS_NODE* pEffectNode = g_EffectsList.pNext; LPDIRECTINPUTEFFECT pDIEffect = NULL; HRESULT hr; // Stop all previous forces if( FAILED( hr = g_pFFDevice->SendForceFeedbackCommand( DISFFC_STOPALL ) ) ) return hr; while ( pEffectNode != &g_EffectsList ) { // Play all of the effects enumerated in the file pDIEffect = pEffectNode->pDIEffect; if( NULL != pDIEffect ) { if( FAILED( hr = pDIEffect->Start( pEffectNode->dwPlayRepeatCount, 0 ) ) ) return hr; } pEffectNode = pEffectNode->pNext; } return S_OK; }
//----------------------------------------------------------------------------- // Purpose: // Input : effectnum - //----------------------------------------------------------------------------- void CInput::ForceFeedback_Stop( int effectnum ) { if ( !m_pFF || !m_pFF->m_bForceFeedbackAvailable ) return; // look up the effect FORCEFEEDBACK_t effect = (FORCEFEEDBACK_t)effectnum; if ( effect < 0 || effect >= NUM_FORCE_FEEDBACK_PRESETS ) { Assert( !"ForceFeedback_Stop with bogus effectnum" ); return; } EffectMap_t *map = &g_EffectMap[ effectnum ]; vecEffectPtr_t *effects = map->pVecEffectPtr; // Stop the effects on the device int c = effects->Count(); for ( int i = 0; i < c; ++i ) { LPDIRECTINPUTEFFECT pEffect = (*effects)[ i ]; pEffect->Stop(); } }
//--------------------------------------------------------------// Win32ForceFeedback::~Win32ForceFeedback() { //Get the effect - if it exists for(EffectList::iterator i = mEffectList.begin(); i != mEffectList.end(); ++i ) { LPDIRECTINPUTEFFECT dxEffect = i->second; if( dxEffect ) dxEffect->Unload(); } mEffectList.clear(); }
//--------------------------------------------------------------// void Win32ForceFeedback::remove( const Effect* eff ) { //Get the effect - if it exists EffectList::iterator i = mEffectList.find(eff->_handle); if( i != mEffectList.end() ) { LPDIRECTINPUTEFFECT dxEffect = i->second; if( dxEffect ) { dxEffect->Stop(); //We care about the return value - as the effect might not //have been unlaoded if( SUCCEEDED(dxEffect->Unload()) ) mEffectList.erase(i); } else mEffectList.erase(i); } }
// release a DirectInput effect. We don't need it anymore. void ReleaseEffect( LPDIRECTINPUTEFFECT &lpDirectEffect ) { if( lpDirectEffect != NULL) { // should unload the effect on release, I hope lpDirectEffect->Release(); lpDirectEffect = NULL; } return; }
// Strength ranges from 0 to 1 void setWheelRumbleStrength(double strength) { DIPERIODIC pf = { FFWRMAX * strength * 10000,0,0,0.06 * 1000000 }; g_sWheelRumbleConfig.cbTypeSpecificParams = sizeof(DIPERIODIC); g_sWheelRumbleConfig.lpvTypeSpecificParams = &pf; if (g_pWheelRumbleHandle) { g_pWheelRumbleHandle->SetParameters(&g_sWheelRumbleConfig, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START); } }
// Magnitude ranges from -1 to 1 void setAutoCenterStrength(double magnitude) { DICONSTANTFORCE cf = { magnitude * 10000 }; g_sAutoCenterConfig.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); g_sAutoCenterConfig.lpvTypeSpecificParams = &cf; if (g_pAutoCenterHandle) { g_pAutoCenterHandle->SetParameters(&g_sAutoCenterConfig, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START); } }
//--------------------------------------------------------------// void Win32ForceFeedback::_upload( GUID guid, DIEFFECT* diEffect, const Effect* effect) { LPDIRECTINPUTEFFECT dxEffect = 0; //Get the effect - if it exists EffectList::iterator i = mEffectList.find(effect->_handle); //It has been created already if( i != mEffectList.end() ) dxEffect = i->second; else //This effect has not yet been created - generate a handle effect->_handle = mHandles++; if( dxEffect == 0 ) { //This effect has not yet been created, so create it HRESULT hr = mJoyStick->CreateEffect(guid, diEffect, &dxEffect, NULL); if(SUCCEEDED(hr)) { mEffectList[effect->_handle] = dxEffect; dxEffect->Start(INFINITE,0); } else if( hr == DIERR_DEVICEFULL ) OIS_EXCEPT(E_DeviceFull, "Remove an effect before adding more!"); else OIS_EXCEPT(E_General, "Unknown error creating effect->.."); } else { //ToDo -- Update the Effect HRESULT hr = dxEffect->SetParameters( diEffect, DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE | DIEP_STARTDELAY | DIEP_TRIGGERBUTTON | DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS | DIEP_START ); if(FAILED(hr)) OIS_EXCEPT(E_InvalidParam, "Error updating device!"); } }
HRESULT SetDeviceForcesXY(float x,float y) { // Modifying an effect is basically the same as creating a new one, except // you need only specify the parameters you are modifying LONG rglDirection[2] = { 0, 0 }; DICONSTANTFORCE cf; if( g_dwNumForceFeedbackAxis == 1 ) { // If only one force feedback axis, then apply only one direction and // keep the direction at zero cf.lMagnitude = x; rglDirection[0] = 0; } else { // If two force feedback axis, then apply magnitude from both directions rglDirection[0] = x; rglDirection[1] = y; cf.lMagnitude = ( DWORD )sqrt( x * x + y * y ); } DIEFFECT eff; ZeroMemory( &eff, sizeof( eff ) ); eff.dwSize = sizeof( DIEFFECT ); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.cAxes = g_dwNumForceFeedbackAxis; eff.rglDirection = rglDirection; eff.lpEnvelope = 0; eff.cbTypeSpecificParams = sizeof( DICONSTANTFORCE ); eff.lpvTypeSpecificParams = &cf; eff.dwStartDelay = 0; // Now set the new parameters and start the effect immediately. HRESULT hr = S_OK; hr = g_pEffect->SetParameters( &eff, DIEP_DIRECTION |DIEP_TYPESPECIFICPARAMS |DIEP_START ); HRESULT a1 = DIERR_INVALIDPARAM; return hr; }
//----------------------------------------------------------------------------- // Purpose: // Input : effectnum - // params - //----------------------------------------------------------------------------- void CInput::ForceFeedback_Start( int effectnum, const FFBaseParams_t& params ) { if ( !m_pFF || !m_pFF->m_bForceFeedbackAvailable ) return; // Unpause system... if ( m_pFF->m_bPaused ) { ForceFeedback_Resume(); } // look up the effect FORCEFEEDBACK_t effect = (FORCEFEEDBACK_t)effectnum; if ( effect < 0 || effect >= NUM_FORCE_FEEDBACK_PRESETS ) { Assert( !"ForceFeedback_Start with bogus effectnum" ); return; } EffectMap_t *map = &g_EffectMap[ effectnum ]; vecEffectPtr_t *effects = map->pVecEffectPtr; // Play the effects on the device int c = effects->Count(); for ( int i = 0; i < c; ++i ) { LPDIRECTINPUTEFFECT pEffect = (*effects)[ i ]; if ( !map->m_bDownloaded ) { pEffect->Download(); map->m_bDownloaded = true; } DWORD flags = DIEP_DIRECTION | DIEP_GAIN | DIEP_DURATION; LONG rglDirection[2] = { 0, 100 }; // Fill in parameters DIEFFECT effect; Q_memset( &effect, 0, sizeof( effect ) ); effect.dwSize = sizeof( effect ); effect.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS; effect.rglDirection = rglDirection; effect.cAxes = 2; HRESULT hr = pEffect->GetParameters( &effect, flags ); if ( !FAILED( hr ) ) { // If params.m_flDuration == 0.0f then that means use the duration in the file if ( params.m_flDuration <= -0.999f ) { effect.dwDuration = INFINITE; } else if( params.m_flDuration >= 0.001f ) { // Convert to microsseconds effect.dwDuration = (DWORD)( params.m_flDuration * 1000000.0f ); } effect.dwGain = params.m_flGain * 10000.0f; effect.rglDirection[ 0 ] = 100.0f * params.m_flDirection; effect.rglDirection[ 1 ] = 0; hr = pEffect->SetParameters( &effect, flags ); if ( !FAILED( hr ) ) { pEffect->Start( 1, params.m_bSolo ? DIES_SOLO : 0 ); } } } }
HRESULT InitForceFeedback() { HRESULT hr; DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0, 0 }; if (FAILED(hr = g_pJoystick->SetCooperativeLevel(GetActiveWindow(), DISCL_EXCLUSIVE | DISCL_BACKGROUND))) return hr; if (FAILED(hr = g_pJoystick->Acquire())) return hr; // Autocenter ZeroMemory(&g_sAutoCenterConfig, sizeof(g_sAutoCenterConfig)); g_sAutoCenterConfig.dwStartDelay = 0; g_sAutoCenterConfig.dwSize = sizeof(DIEFFECT); g_sAutoCenterConfig.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; g_sAutoCenterConfig.dwDuration = INFINITE; g_sAutoCenterConfig.dwSamplePeriod = 0; g_sAutoCenterConfig.dwGain = DI_FFNOMINALMAX; g_sAutoCenterConfig.dwTriggerButton = DIEB_NOTRIGGER; g_sAutoCenterConfig.dwTriggerRepeatInterval = 0; g_sAutoCenterConfig.cAxes = 1; g_sAutoCenterConfig.rgdwAxes = rgdwAxes; g_sAutoCenterConfig.rglDirection = rglDirection; g_sAutoCenterConfig.lpEnvelope = 0; g_sAutoCenterConfig.dwStartDelay = 0; DICONSTANTFORCE cf = { 0 }; g_sAutoCenterConfig.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); g_sAutoCenterConfig.lpvTypeSpecificParams = &cf; if (FAILED(hr = g_pJoystick->CreateEffect(GUID_ConstantForce, &g_sAutoCenterConfig, &g_pAutoCenterHandle, nullptr))) return hr; if (FAILED(hr = g_pAutoCenterHandle->Start(INFINITE, 0))) return hr; // Rumble ZeroMemory(&g_sWheelRumbleConfig, sizeof(g_sWheelRumbleConfig)); g_sWheelRumbleConfig.dwStartDelay = 0; g_sWheelRumbleConfig.dwSize = sizeof(DIEFFECT); g_sWheelRumbleConfig.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; g_sWheelRumbleConfig.dwDuration = INFINITE; g_sWheelRumbleConfig.dwSamplePeriod = 0; g_sWheelRumbleConfig.dwGain = DI_FFNOMINALMAX; g_sWheelRumbleConfig.dwTriggerButton = DIEB_NOTRIGGER; g_sWheelRumbleConfig.dwTriggerRepeatInterval = 0; g_sWheelRumbleConfig.cAxes = 1; g_sWheelRumbleConfig.rgdwAxes = rgdwAxes; g_sWheelRumbleConfig.rglDirection = rglDirection; g_sWheelRumbleConfig.lpEnvelope = 0; g_sWheelRumbleConfig.dwStartDelay = 0; DIPERIODIC pf = { 0,0,0,0.08 }; g_sWheelRumbleConfig.cbTypeSpecificParams = sizeof(DIPERIODIC); g_sWheelRumbleConfig.lpvTypeSpecificParams = &pf; if (FAILED(hr = g_pJoystick->CreateEffect(GUID_Sine, &g_sWheelRumbleConfig, &g_pWheelRumbleHandle, nullptr))) return hr; if (FAILED(hr = g_pWheelRumbleHandle->Start(INFINITE, 0))) return hr; return S_OK; }
// Create a force feedback effect handle and downloads the effect. Parameters are self-explanatory. bool CreateEffectHandle( HWND hWnd, LPDIRECTINPUTDEVICE8 lpDirectInputDevice, LPDIRECTINPUTEFFECT &pDIEffect, BYTE bRumbleTyp, long lStrength ) { if( pDIEffect ) ReleaseEffect( pDIEffect ); if( !lpDirectInputDevice || bRumbleTyp == RUMBLE_DIRECT ) return false; DWORD nAxes = 0; DWORD rgdwAxes[] = { DIJOFS_X, DIJOFS_Y }; HRESULT hResult; // count the FF - axes of the joystick lpDirectInputDevice->EnumObjects( EnumCountFFAxes, &nAxes, DIDFT_FFACTUATOR | DIDFT_AXIS ); if( nAxes == 0 ) return false; nAxes = min( nAxes, 2 ); // Must be unaquired for setting stuff like Co-op Level hResult = lpDirectInputDevice->Unacquire(); //FF Requires EXCLUSIVE LEVEL, took me hours to find the reason why it wasnt working hResult = lpDirectInputDevice->SetCooperativeLevel( hWnd, DIB_FF ); // fail if we can't set coop level --rabid if (hResult != DI_OK) { DebugWriteA("CreateEffectHandle: couldn't set coop level: %08X\n", hResult); return false; } // Since we will be playing force feedback effects, we should disable the // auto-centering spring. DIPROPDWORD dipdw; dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = FALSE; hResult = lpDirectInputDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ); long rglDirection[] = { 1, 1 }; LPGUID EffectGuid; DIEFFECT eff; ZeroMemory( &eff, sizeof(eff) ); eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwGain = lStrength * 100; eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerRepeatInterval = 0; eff.cAxes = nAxes; //Number of Axes eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; eff.lpEnvelope = NULL; eff.dwStartDelay = 0; DICONSTANTFORCE cf; DIRAMPFORCE rf; DIPERIODIC pf; switch( bRumbleTyp ) { case RUMBLE_CONSTANT: EffectGuid = (GUID*)&GUID_ConstantForce; eff.dwDuration = 150000; // microseconds eff.dwSamplePeriod = 0; eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.lpvTypeSpecificParams = &cf; cf.lMagnitude = 10000; break; case RUMBLE_RAMP: EffectGuid = (GUID*)&GUID_RampForce; eff.dwDuration = 300000; // microseconds eff.dwSamplePeriod = 0; eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); eff.lpvTypeSpecificParams = &rf; rf.lStart = 10000; rf.lEnd = 2000; break; case RUMBLE_CONDITION: case RUMBLE_PERIODIC: EffectGuid = (GUID*)&GUID_Sine; eff.dwDuration = 150000; // microseconds eff.dwSamplePeriod = 0; eff.cbTypeSpecificParams = sizeof(DIPERIODIC); eff.lpvTypeSpecificParams = &pf; pf.dwMagnitude = 10000; pf.lOffset = 0; pf.dwPhase = 0; pf.dwPeriod = 2000; break; case RUMBLE_NONE: case RUMBLE_CUSTOM: default: return false; } hResult = lpDirectInputDevice->CreateEffect( *EffectGuid, &eff, &pDIEffect, NULL ); if (hResult == DI_OK) { hResult = lpDirectInputDevice->Acquire(); hResult = pDIEffect->Download(); } else { DebugWriteA("CreateEffectHandle: didn't CreateEffect: %08X\n", hResult); } return SUCCEEDED( hResult ); }