Changes to CDirectInputSystem:

- Added parameters for DirectInput and XInput force feedback effects (although still need to add code to persist them in configuration file)
- Added extra friction feedback effect and tidied up existing effects
This commit is contained in:
Nik Henson 2011-09-06 19:05:22 +00:00
parent 48e98ff75d
commit c5d585440c
2 changed files with 119 additions and 73 deletions

View file

@ -316,7 +316,9 @@ CDirectInputSystem::CDirectInputSystem(bool useRawInput, bool useXInput, bool en
m_useRawInput(useRawInput), m_useXInput(useXInput), m_enableFFeedback(enableFFeedback), m_useRawInput(useRawInput), m_useXInput(useXInput), m_enableFFeedback(enableFFeedback),
m_initializedCOM(false), m_activated(false), m_hwnd(NULL), m_screenW(0), m_screenH(0), m_initializedCOM(false), m_activated(false), m_hwnd(NULL), m_screenW(0), m_screenH(0),
m_getRIDevListPtr(NULL), m_getRIDevInfoPtr(NULL), m_regRIDevsPtr(NULL), m_getRIDataPtr(NULL), m_getRIDevListPtr(NULL), m_getRIDevInfoPtr(NULL), m_regRIDevsPtr(NULL), m_getRIDataPtr(NULL),
m_xiGetCapabilitiesPtr(NULL), m_xiGetStatePtr(NULL), m_xiSetStatePtr(NULL), m_di8(NULL), m_di8Keyboard(NULL), m_di8Mouse(NULL) m_xiGetCapabilitiesPtr(NULL), m_xiGetStatePtr(NULL), m_xiSetStatePtr(NULL), m_di8(NULL), m_di8Keyboard(NULL), m_di8Mouse(NULL),
m_diEffectsGain(DI_FFNOMINALMAX), m_diConstForceMax(DI_FFNOMINALMAX), m_diSelfCenterMax(DI_FFNOMINALMAX), m_diFrictionMax(DI_FFNOMINALMAX), m_diVibrateMax(DI_FFNOMINALMAX),
m_xiConstForceThreshold(0.75f), m_xiConstForceMax(65535), m_xiVibrateMax(65535)
{ {
// Reset initial states // Reset initial states
memset(&m_combRawMseState, 0, sizeof(RawMseState)); memset(&m_combRawMseState, 0, sizeof(RawMseState));
@ -1285,8 +1287,8 @@ HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 joystick,
default: return E_FAIL; default: return E_FAIL;
} }
DWORD rgdwAxes[1] = { axisOfs }; DWORD dwAxis = axisOfs;
LONG rglDirection[1] = { 0 }; LONG lDirection = 0;
DICONSTANTFORCE dicf; DICONSTANTFORCE dicf;
DICONDITION dic; DICONDITION dic;
DIPERIODIC dip; DIPERIODIC dip;
@ -1300,59 +1302,65 @@ HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 joystick,
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0; eff.dwTriggerRepeatInterval = 0;
eff.dwGain = DI_FFNOMINALMAX; eff.dwGain = m_diEffectsGain;
eff.cAxes = 1; eff.cAxes = 1;
eff.rgdwAxes = rgdwAxes; eff.rgdwAxes = &dwAxis;
eff.rglDirection = rglDirection; eff.rglDirection = &lDirection;
eff.dwDuration = INFINITE; eff.dwDuration = INFINITE;
eff.dwStartDelay = 0; eff.dwStartDelay = 0;
eff.lpEnvelope = NULL;
// Set specific effects parameters // Set specific effects parameters
switch (ffCmd.id) switch (ffCmd.id)
{ {
case FFStop: case FFStop:
return E_FAIL; return E_FAIL;
case FFSelfCenter:
guid = GUID_Spring;
dic.lOffset = 0;
dic.lPositiveCoefficient = 0;
dic.lNegativeCoefficient = 0;
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)0.05 * DI_FFNOMINALMAX;
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFConstantForce: case FFConstantForce:
guid = GUID_ConstantForce; guid = GUID_ConstantForce;
dicf.lMagnitude = 0; dicf.lMagnitude = 0;
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &dicf; eff.lpvTypeSpecificParams = &dicf;
break; break;
case FFSelfCenter:
guid = GUID_Spring;
dic.lOffset = 0; // offset is +ve/-ve bias, 0 = evenly spread in both directions
dic.lPositiveCoefficient = 0;
dic.lNegativeCoefficient = 0;
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)(0.05 * DI_FFNOMINALMAX); // 5% deadband
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFFriction:
guid = GUID_Friction;
dic.lOffset = 0;
dic.lPositiveCoefficient = 0;
dic.lNegativeCoefficient = 0;
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = 0; // 0% deadband
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFVibrate: case FFVibrate:
guid = GUID_Sine; guid = GUID_Sine;
dip.dwMagnitude = 0; dip.dwMagnitude = 0;
dip.lOffset = 0; dip.lOffset = 0;
dip.dwPhase = 0; dip.dwPhase = 0;
dip.dwPeriod = (DWORD)0.1 * DI_SECONDS; // 1/10th second dip.dwPeriod = (DWORD)(0.05 * DI_SECONDS); // 1/20th second
die.dwSize = sizeof(die);
die.dwAttackLevel = 0;
die.dwAttackTime = (DWORD)0.5 * DI_SECONDS;
die.dwFadeLevel = 0;
die.dwFadeTime = (DWORD)0.5 * DI_SECONDS;
eff.lpEnvelope = ¨
eff.cbTypeSpecificParams = sizeof(DIPERIODIC); eff.cbTypeSpecificParams = sizeof(DIPERIODIC);
eff.lpvTypeSpecificParams = &dip; eff.lpvTypeSpecificParams = &dip;
break; break;
@ -1598,29 +1606,45 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
HRESULT hr; HRESULT hr;
if (pInfo->isXInput) if (pInfo->isXInput)
{ {
if (axisNum != AXIS_X && axisNum != AXIS_Y && axisNum != AXIS_RX && axisNum != AXIS_RY)
return false;
XINPUT_VIBRATION vibration; XINPUT_VIBRATION vibration;
bool negForce;
float absForce;
switch (ffCmd.id) switch (ffCmd.id)
{ {
case FFStop: case FFStop:
if (axisNum == AXIS_X || axisNum == AXIS_Y) // Stop command halts all vibration
vibration.wLeftMotorSpeed = 0; vibration.wLeftMotorSpeed = 0;
else if (axisNum == AXIS_RX || axisNum == AXIS_RY) vibration.wRightMotorSpeed = 0;
vibration.wRightMotorSpeed = 0;
else
return false;
return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration)); return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration));
case FFConstantForce: case FFConstantForce:
if (axisNum == AXIS_X || axisNum == AXIS_Y) // Constant force effect is mapped to either left or right vibration motor depending on its direction and providing it's magnitude
vibration.wLeftMotorSpeed = ffCmd.data; // TODO - scale data to 0-65535 // is above threshold setting
else if (axisNum == AXIS_RX || axisNum == AXIS_RY) negForce = ffCmd.force < 0.0f;
vibration.wRightMotorSpeed = ffCmd.data; // TODO - scale data to 0-65535 absForce = (negForce ? -ffCmd.force : ffCmd.force);
else if (absForce < m_xiConstForceThreshold)
return false; return false;
if (negForce)
vibration.wLeftMotorSpeed = (WORD)(ffCmd.force * (float)m_xiConstForceMax);
else
vibration.wRightMotorSpeed = (WORD)(ffCmd.force * (float)m_xiConstForceMax);
return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration));
case FFSelfCenter:
case FFFriction:
// Self center and friction effects are not mapped
return false;
case FFVibrate:
// Vibration effect is mapped to both vibration motors
vibration.wLeftMotorSpeed = (WORD)(ffCmd.force * (float)m_xiVibrateMax);
vibration.wRightMotorSpeed = (WORD)(ffCmd.force * (float)m_xiVibrateMax);
return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration)); return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration));
default: default:
// TODO - other force feedback types // Unknown feedback command
return false; return false;
} }
} }
@ -1628,8 +1652,8 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
{ {
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[pInfo->dInputNum]; LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[pInfo->dInputNum];
// See if command is to stop all force feedback // See if command is to stop all force feedback, if so send appropriate command
if (ffCmd.id == -1) if (ffCmd.id == FFStop)
return SUCCEEDED(hr = joystick->SendForceFeedbackCommand(DISFFC_STOPALL)); return SUCCEEDED(hr = joystick->SendForceFeedbackCommand(DISFFC_STOPALL));
// Create effect for given axis if has not already been created // Create effect for given axis if has not already been created
@ -1639,10 +1663,9 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
{ {
if (FAILED(hr = CreateJoystickEffect(joystick, axisNum, ffCmd, pEffect))) if (FAILED(hr = CreateJoystickEffect(joystick, axisNum, ffCmd, pEffect)))
return false; return false;
} }
LONG rglDirection[1] = { 0 }; LONG lDirection = 0;
DICONSTANTFORCE dicf; DICONSTANTFORCE dicf;
DICONDITION dic; DICONDITION dic;
DIPERIODIC dip; DIPERIODIC dip;
@ -1654,53 +1677,64 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
eff.dwSize = sizeof(DIEFFECT); eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.cAxes = 1; eff.cAxes = 1;
eff.rglDirection = rglDirection; eff.rglDirection = &lDirection;
eff.dwStartDelay = 0; eff.dwStartDelay = 0;
eff.lpEnvelope = NULL;
// Set command specific parameters
switch (ffCmd.id) switch (ffCmd.id)
{ {
case FFSelfCenter:
dic.lPositiveCoefficient = ffCmd.data;
dic.lNegativeCoefficient = ffCmd.data;
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)0.05 * DI_FFNOMINALMAX;
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFConstantForce: case FFConstantForce:
dicf.lMagnitude = ffCmd.data; // TODO - scale & cap at +/- DI_FFNOMINALMAX //printf("FFConstantForce %0.2f\n", 100.0f * ffCmd.force);
dicf.lMagnitude = (LONG)(-ffCmd.force * (float)m_diConstForceMax); // Invert sign for DirectInput effect
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &dicf; eff.lpvTypeSpecificParams = &dicf;
break; break;
case FFSelfCenter:
//printf("FFSelfCenter %0.2f\n", 100.0f * ffCmd.force);
dic.lOffset = 0;
dic.lPositiveCoefficient = (LONG)(ffCmd.force * (float)m_diSelfCenterMax);
dic.lNegativeCoefficient = (LONG)(ffCmd.force * (float)m_diSelfCenterMax);
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)(0.05 * DI_FFNOMINALMAX);
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFFriction:
//printf("FFFriction %0.2f\n", 100.0f * ffCmd.force);
dic.lOffset = 0;
dic.lPositiveCoefficient = (LONG)(ffCmd.force * (float)m_diFrictionMax);
dic.lNegativeCoefficient = (LONG)(ffCmd.force * (float)m_diFrictionMax);
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = 0;
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFVibrate: case FFVibrate:
dip.dwMagnitude = ffCmd.data; //printf("FFVibrate %0.2f\n", 100.0f * ffCmd.force);
dip.dwMagnitude = (DWORD)(ffCmd.force * (float)m_diVibrateMax);
dip.lOffset = 0; dip.lOffset = 0;
dip.dwPhase = 0; dip.dwPhase = 0;
dip.dwPeriod = (DWORD)0.1 * DI_SECONDS; // 1/10th second dip.dwPeriod = (DWORD)(0.05 * DI_SECONDS); // 1/20th second
die.dwSize = sizeof(die);
die.dwAttackLevel = 0;
die.dwAttackTime = (DWORD)0.5 * DI_SECONDS;
die.dwFadeLevel = 0;
die.dwFadeTime = (DWORD)0.5 * DI_SECONDS;
eff.lpEnvelope = &die;
eff.cbTypeSpecificParams = sizeof(DIPERIODIC); eff.cbTypeSpecificParams = sizeof(DIPERIODIC);
eff.lpvTypeSpecificParams = &dip; eff.lpvTypeSpecificParams = &dip;
break; break;
default: default:
// Unknown feedback command
return false; return false;
} }
// Now set the new parameters and start the effect immediately. // Set the new parameters and start effect immediately
return SUCCEEDED(hr = (*pEffect)->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START)); return SUCCEEDED(hr = (*pEffect)->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START));
} }
} }

View file

@ -135,6 +135,18 @@ private:
vector<DIJoyInfo> m_diJoyInfos; vector<DIJoyInfo> m_diJoyInfos;
vector<DIJOYSTATE2> m_diJoyStates; vector<DIJOYSTATE2> m_diJoyStates;
// DirectInput force feedback parameters
LONG m_diEffectsGain;
LONG m_diConstForceMax;
LONG m_diSelfCenterMax;
LONG m_diFrictionMax;
LONG m_diVibrateMax;
// XInput force feedback parameters
float m_xiConstForceThreshold;
WORD m_xiConstForceMax;
WORD m_xiVibrateMax;
bool GetRegString(HKEY regKey, const char *regPath, string &str); bool GetRegString(HKEY regKey, const char *regPath, string &str);
bool GetRegDeviceName(const char *rawDevName, char *name); bool GetRegDeviceName(const char *rawDevName, char *name);