#include "ffb.h" #include #define MAX_JOYSTICKS 32 #define kDI_MaxEffects 30 // Hard coded limit (not a DX limit) extern LPDIRECTINPUTDEVICE7 Joysticks[MAX_JOYSTICKS]; // Force Feedback Effect Data // -------------------------- REFGUID effectGUID[kMaxEffectSubTypes] = { &GUID_ConstantForce, &GUID_RampForce, &GUID_CustomForce, // period &GUID_Square, &GUID_Sine, &GUID_Triangle, &GUID_SawtoothUp, &GUID_SawtoothDown, // condition &GUID_Spring, &GUID_Damper, &GUID_Inertia, &GUID_Friction }; typedef union { DICONSTANTFORCE constant; DIRAMPFORCE ramp; DIPERIODIC period; DICONDITION condition; DICUSTOMFORCE custom; } tEffectClasses; typedef struct tEffect { DIEFFECT general; tEffectClasses specific; DIENVELOPE envelope; LONG direction[2]; } tEffect; static tEffect Effect[kDI_MaxEffects]; // DInput Data // ----------- static LPDIRECTINPUTEFFECT DIE_hEffect[kDI_MaxEffects]; static int numEffects = 0; // --------- // ffb_Pause // ------------------------------------------------------------------- // Purpose: // Pause the FFB output on the given device. Use ffb_Continue to // continue where you left off. // ------------------------------------------------------------------- void ffb_Pause(short dev) { IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_PAUSE); } // ------------ // ffb_Continue // ------------------------------------------------------------------- // Purpose: // Unpause the FFB output on the given device. Complimentary to // ffb_Pause. // ------------------------------------------------------------------- void ffb_Continue(short dev) { IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_CONTINUE); } // ---------- // ffb_Enable // ------------------------------------------------------------------- // Purpose: // Must be called after initialization in order to activate the // device. // Use ffb_Pause & ffb_Continue if you want disable forces // temporarily and resume later. // ------------------------------------------------------------------- void ffb_Enable(short dev) { IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_SETACTUATORSON); } // ----------- // ffb_Disable // ------------------------------------------------------------------- // Purpose: // Turns off FFB, but effects still play on processor. // ------------------------------------------------------------------- void ffb_Disable(short dev) { IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_SETACTUATORSOFF); } void ffb_simpleJolt(short dev) { tFFB_Effect effect; short eID; // Constant Effect (simple jolt) // ----------------------------- effect.Type = kConstant; effect.TypeInfo.Constant.Mag = 10000; effect.Duration = 1000000 >> 1; // half second effect.Period = 1000; effect.Gain = 10000; effect.Trigger = kNoButton; effect.TriggerRepeatTime = 0; effect.Direction = 0; eID = ffb_effectCreate(dev, &effect); } // ---------------- // ffb_effectCreate // ------------------------------------------------------------------- // Purpose: // Create a single effect for future playback. // Effect is given a logical ID // ------------------------------------------------------------------- int ffb_effectCreate(short dev, tFFB_Effect* eff) { HRESULT hr; // Important stuff Effect[numEffects].general.dwDuration = eff->Duration; Effect[numEffects].general.dwSamplePeriod = eff->Period; Effect[numEffects].general.dwGain = eff->Gain; Effect[numEffects].general.dwTriggerButton = (eff->Trigger == kNoButton ? DIEB_NOTRIGGER : DIJOFS_BUTTON(eff->Trigger)); Effect[numEffects].general.dwTriggerRepeatInterval = eff->TriggerRepeatTime; Effect[numEffects].direction[0] = eff->Direction; memcpy(&Effect[numEffects].specific, &eff->TypeInfo, sizeof(tEffectClasses)); // Wacky COM related overhead Effect[numEffects].general.dwSize = sizeof(DIEFFECT); Effect[numEffects].general.cAxes = 2; Effect[numEffects].general.rgdwAxes = 0; Effect[numEffects].general.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS; Effect[numEffects].general.lpvTypeSpecificParams = &Effect[numEffects].specific; Effect[numEffects].general.rglDirection = &Effect[numEffects].direction; Effect[numEffects].direction[1] = 0L; Effect[numEffects].general.lpEnvelope = NULL; Effect[numEffects].general.lpvTypeSpecificParams = &Effect[numEffects].specific; switch (eff->Type) { case kConstant: Effect[numEffects].general.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); break; case kRamp: Effect[numEffects].general.cbTypeSpecificParams = sizeof(DIRAMPFORCE); break; case kCustom: Effect[numEffects].general.cbTypeSpecificParams = sizeof(DICUSTOMFORCE); break; case kWave_Square: case kWave_Sine: case kWave_Triange: case kWave_SawUp: case kWave_SawDown: Effect[numEffects].general.cbTypeSpecificParams = sizeof(DIPERIODIC); break; case kCondition_Spring: case kCondition_Damper: case kCondition_Inertia: case kCondition_Friction: Effect[numEffects].general.cbTypeSpecificParams = sizeof(DICONDITION); break; default: return(-1); } hr = IDirectInputDevice7_CreateEffect(Joysticks[dev], effectGUID[eff->Type], (LPCDIEFFECT)&Effect[numEffects], (LPDIRECTINPUTEFFECT*)&DIE_hEffect[numEffects], 0); switch (hr) { case DI_OK: ffb_effectUnload(numEffects); ++numEffects; return(numEffects - 1); case DIERR_DEVICENOTREG: return(-1); case DIERR_DEVICEFULL: return(-1); } return(-1); } // -------------- // ffb_effectPlay // ------------------------------------------------------------------- // Purpose: // Play an effect that was previously created. // ------------------------------------------------------------------- void ffb_effectPlay(short eID) { IDirectInputEffect_Start(DIE_hEffect[eID], 1, 0); } // -------------- // ffb_effectStop // ------------------------------------------------------------------- // Purpose: // Stop a single effect. // ------------------------------------------------------------------- void ffb_effectStop(short eID) { IDirectInputEffect_Stop(DIE_hEffect[eID]); } // ----------------- // ffb_effectStopAll // ------------------------------------------------------------------- // Purpose: // Stops all forces on the given device. // ------------------------------------------------------------------- void ffb_effectStopAll(short dev) { IDirectInputDevice7_SendForceFeedbackCommand(Joysticks[dev], DISFFC_STOPALL); } // ---------------- // ffb_effectUnload // ------------------------------------------------------------------- // Purpose: // Unload a single effect... Necessary to make room for other // effects. // ------------------------------------------------------------------- void ffb_effectUnload(short eID) { IDirectInputEffect_Unload(DIE_hEffect[eID]); } // ---------------- // ffb_effectModify // ------------------------------------------------------------------- // Purpose: // Modifies a single effect, only if the given parameters are // different from what's currently loaded. // ------------------------------------------------------------------- void ffb_effectModify(short eID, int* Direction, int* Duration, int* Gain, int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope) { int flags = 0; return; if (Direction) { if (Effect[eID].direction[0] != *Direction) { Effect[eID].direction[0] = *Direction; flags |= DIEP_DIRECTION; } } if (Duration) { if (Effect[eID].general.dwDuration != *Duration) { Effect[eID].general.dwDuration = *Duration; flags |= DIEP_DURATION; } } if (Gain) { if (Effect[eID].general.dwGain != *Gain) { Effect[eID].general.dwGain = *Gain; flags |= DIEP_GAIN; } } if (Period) { if (Effect[eID].general.dwSamplePeriod != *Period) { Effect[eID].general.dwSamplePeriod = *Period; flags |= DIEP_SAMPLEPERIOD; } } if (TypeInfo) { if (!memcmp(&Effect[eID].specific, TypeInfo, Effect[eID].general.cbTypeSpecificParams)) { memcpy(&Effect[eID].specific, TypeInfo, Effect[eID].general.cbTypeSpecificParams); flags |= DIEP_TYPESPECIFICPARAMS; } } if (Envelope) { if (Effect[eID].envelope.dwAttackLevel != Envelope->AttackLevel || Effect[eID].envelope.dwAttackTime != Envelope->AttackTime || Effect[eID].envelope.dwFadeLevel != Envelope->FadeLevel || Effect[eID].envelope.dwFadeTime != Envelope->FadeTime) { Effect[eID].envelope.dwAttackLevel = Envelope->AttackLevel; Effect[eID].envelope.dwAttackTime = Envelope->AttackTime; Effect[eID].envelope.dwFadeLevel = Envelope->FadeLevel; Effect[eID].envelope.dwFadeTime = Envelope->FadeTime; flags |= DIEP_ENVELOPE; } } if (!flags) return; IDirectInputEffect_SetParameters(DIE_hEffect[eID], (LPCDIEFFECT)&Effect[eID], flags); }