/* The zlib/libpng License Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "Win32/Win32ForceFeedback.h" #include "OISException.h" #include #if defined (_DEBUG) #include #endif using namespace OIS; //--------------------------------------------------------------// Win32ForceFeedback::Win32ForceFeedback(IDirectInputDevice8* joy) : mHandles(0), mJoyStick(joy) { } //--------------------------------------------------------------// 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::upload( const Effect* effect ) { switch( effect->force ) { case OIS::Effect::ConstantForce: _updateConstantEffect(effect); break; case OIS::Effect::RampForce: _updateRampEffect(effect); break; case OIS::Effect::PeriodicForce: _updatePeriodicEffect(effect); break; case OIS::Effect::ConditionalForce: _updateConditionalEffect(effect); break; //case OIS::Effect::CustomForce: _updateCustomEffect(effect); break; default: OIS_EXCEPT(E_NotImplemented, "Requested Force not Implemented yet, sorry!"); break; } } //--------------------------------------------------------------// void Win32ForceFeedback::modify( const Effect* eff ) { //Modifying is essentially the same as an upload, so, just reuse that function upload(eff); } //--------------------------------------------------------------// 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); } } //--------------------------------------------------------------// void Win32ForceFeedback::setMasterGain( float level ) { //Between 0 - 10,000 int gain_level = (int)(10000.0f * level); if( gain_level > 10000 ) gain_level = 10000; else if( gain_level < 0 ) gain_level = 0; DIPROPDWORD DIPropGain; DIPropGain.diph.dwSize = sizeof(DIPropGain); DIPropGain.diph.dwHeaderSize = sizeof(DIPROPHEADER); DIPropGain.diph.dwObj = 0; DIPropGain.diph.dwHow = DIPH_DEVICE; DIPropGain.dwData = gain_level; mJoyStick->SetProperty(DIPROP_FFGAIN, &DIPropGain.diph); } //--------------------------------------------------------------// void Win32ForceFeedback::setAutoCenterMode( bool auto_on ) { //DI Property DIPROPAUTOCENTER_OFF = 0, 1 is on DIPROPDWORD DIPropAutoCenter; DIPropAutoCenter.diph.dwSize = sizeof(DIPropAutoCenter); DIPropAutoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER); DIPropAutoCenter.diph.dwObj = 0; DIPropAutoCenter.diph.dwHow = DIPH_DEVICE; DIPropAutoCenter.dwData = auto_on; //hr = mJoyStick->SetProperty(DIPROP_AUTOCENTER, &DIPropAutoCenter.diph); } //--------------------------------------------------------------// void Win32ForceFeedback::_updateConstantEffect( const Effect* effect ) { DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0, 0 }; DICONSTANTFORCE cf; DIEFFECT diEffect; //Currently only support 1 axis //if( effect->getNumAxes() == 1 ) cf.lMagnitude = static_cast(effect->getForceEffect())->level; _setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DICONSTANTFORCE), &cf, effect); _upload(GUID_ConstantForce, &diEffect, effect); } //--------------------------------------------------------------// void Win32ForceFeedback::_updateRampEffect( const Effect* effect ) { DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0, 0 }; DIRAMPFORCE rf; DIEFFECT diEffect; //Currently only support 1 axis rf.lStart = static_cast(effect->getForceEffect())->startLevel; rf.lEnd = static_cast(effect->getForceEffect())->endLevel; _setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DIRAMPFORCE), &rf, effect); _upload(GUID_RampForce, &diEffect, effect); } //--------------------------------------------------------------// void Win32ForceFeedback::_updatePeriodicEffect( const Effect* effect ) { DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0, 0 }; DIPERIODIC pf; DIEFFECT diEffect; //Currently only support 1 axis pf.dwMagnitude = static_cast(effect->getForceEffect())->magnitude; pf.lOffset = static_cast(effect->getForceEffect())->offset; pf.dwPhase = static_cast(effect->getForceEffect())->phase; pf.dwPeriod = static_cast(effect->getForceEffect())->period; _setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DIPERIODIC), &pf, effect); switch( effect->type ) { case OIS::Effect::Square: _upload(GUID_Square, &diEffect, effect); break; case OIS::Effect::Triangle: _upload(GUID_Triangle, &diEffect, effect); break; case OIS::Effect::Sine: _upload(GUID_Sine, &diEffect, effect); break; case OIS::Effect::SawToothUp: _upload(GUID_SawtoothUp, &diEffect, effect); break; case OIS::Effect::SawToothDown: _upload(GUID_SawtoothDown, &diEffect, effect); break; default: break; } } //--------------------------------------------------------------// void Win32ForceFeedback::_updateConditionalEffect( const Effect* effect ) { DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0, 0 }; DICONDITION cf; DIEFFECT diEffect; cf.lOffset = static_cast(effect->getForceEffect())->deadband; cf.lPositiveCoefficient = static_cast(effect->getForceEffect())->rightCoeff; cf.lNegativeCoefficient = static_cast(effect->getForceEffect())->leftCoeff; cf.dwPositiveSaturation = static_cast(effect->getForceEffect())->rightSaturation; cf.dwNegativeSaturation = static_cast(effect->getForceEffect())->leftSaturation; cf.lDeadBand = static_cast(effect->getForceEffect())->deadband; _setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DICONDITION), &cf, effect); switch( effect->type ) { case OIS::Effect::Friction: _upload(GUID_Friction, &diEffect, effect); break; case OIS::Effect::Damper: _upload(GUID_Damper, &diEffect, effect); break; case OIS::Effect::Inertia: _upload(GUID_Inertia, &diEffect, effect); break; case OIS::Effect::Spring: _upload(GUID_Spring, &diEffect, effect); break; default: break; } } //--------------------------------------------------------------// void Win32ForceFeedback::_updateCustomEffect( const Effect* /*effect*/ ) { //DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; //LONG rglDirection[2] = { 0, 0 }; //DICUSTOMFORCE cf; //DIEFFECT diEffect; //cf.cChannels = 0; //cf.dwSamplePeriod = 0; //cf.cSamples = 0; //cf.rglForceData = 0; //_setCommonProperties(&diEffect, rgdwAxes, rglDirection, sizeof(DICUSTOMFORCE), &cf, effect); //_upload(GUID_CustomForce, &diEffect, effect); } //--------------------------------------------------------------// void Win32ForceFeedback::_setCommonProperties( DIEFFECT* diEffect, DWORD* rgdwAxes, LONG* rglDirection, DWORD struct_size, LPVOID struct_type, const Effect* effect ) { ZeroMemory(diEffect, sizeof(DIEFFECT)); diEffect->dwSize = sizeof(DIEFFECT); diEffect->dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; diEffect->dwDuration = effect->replay_length; diEffect->dwSamplePeriod = 0; diEffect->dwGain = DI_FFNOMINALMAX; diEffect->dwTriggerButton = DIEB_NOTRIGGER; diEffect->dwTriggerRepeatInterval = 0; diEffect->cAxes = effect->getNumAxes(); diEffect->rgdwAxes = rgdwAxes; diEffect->rglDirection = rglDirection; diEffect->lpEnvelope = 0; diEffect->cbTypeSpecificParams = struct_size; diEffect->lpvTypeSpecificParams = struct_type; diEffect->dwStartDelay = effect->replay_delay; } //--------------------------------------------------------------// 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!"); } } //--------------------------------------------------------------// void Win32ForceFeedback::_addEffectSupport( LPCDIEFFECTINFO pdei ) { //Determine what the effect is and how it corresponds to our OIS's Enums //We could save the GUIDs too, however, we will just use the predefined //ones later if( pdei->guid == GUID_ConstantForce ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Constant ); else if( pdei->guid == GUID_Triangle ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Triangle ); else if( pdei->guid == GUID_Spring ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Spring ); else if( pdei->guid == GUID_Friction ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Friction ); else if( pdei->guid == GUID_Square ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Square ); else if( pdei->guid == GUID_Sine ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Sine ); else if( pdei->guid == GUID_SawtoothUp ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::SawToothUp ); else if( pdei->guid == GUID_SawtoothDown ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::SawToothDown ); else if( pdei->guid == GUID_Damper ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Damper ); else if( pdei->guid == GUID_Inertia ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Inertia ); else if( pdei->guid == GUID_CustomForce ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Custom ); else if( pdei->guid == GUID_RampForce ) _addEffectTypes((Effect::EForce)DIEFT_GETTYPE(pdei->dwEffType), Effect::Ramp ); #if defined (_DEBUG) //Only care about this for Debugging Purposes //else //{ // std::ostringstream ss; // ss << "Win32ForceFeedback, DirectInput8 Effect not found. Reported as: " // << pdei->tszName; // OIS_EXCEPT( E_General, ss.str().c_str()); //} #endif }