//----------------------------------------------------------------------------- // Name: EnumAndCreateEffectsCallback() // Desc: Create the effects as they are enumerated and add them to the // linked list, g_EffectsList //----------------------------------------------------------------------------- BOOL CALLBACK EnumAndCreateEffectsCallback( LPCDIFILEEFFECT pDIFileEffect, VOID* pvRef ) { HRESULT hr; LPDIRECTINPUTEFFECT pDIEffect = NULL; // Create the file effect if( FAILED( hr = g_pFFDevice->CreateEffect( pDIFileEffect->GuidEffect, pDIFileEffect->lpDiEffect, &pDIEffect, NULL ) ) ) { OutputDebugString( TEXT("Could not create force feedback effect on this device.\n") ); return DIENUM_CONTINUE; } // Create a new effect node EFFECTS_NODE* pEffectNode = new EFFECTS_NODE; if( NULL == pEffectNode ) return DIENUM_STOP; // Fill the pEffectNode up ZeroMemory( pEffectNode, sizeof( EFFECTS_NODE ) ); pEffectNode->pDIEffect = pDIEffect; pEffectNode->dwPlayRepeatCount = 1; // Add pEffectNode to the circular linked list, g_EffectsList pEffectNode->pNext = g_EffectsList.pNext; g_EffectsList.pNext = pEffectNode; return DIENUM_CONTINUE; }
void Visit( CConstantForceFeedbackEffectDesc& desc ) { DICONSTANTFORCE cf = { 0 }; cf.lMagnitude = (LONG)desc.magnitude;// 5000; // [-10000,10000] m_pDIEffect->cbTypeSpecificParams = sizeof( DICONSTANTFORCE ); m_pDIEffect->lpvTypeSpecificParams = &cf; m_hr = m_pInputDevice->CreateEffect( GUID_ConstantForce, m_pDIEffect, &m_pCreatedEffect, NULL ); }
void Visit( CRampForceFeedbackEffectDesc& desc ) { DIRAMPFORCE rf; rf.lStart = (LONG)desc.start; rf.lEnd = (LONG)desc.end; m_pDIEffect->cbTypeSpecificParams = sizeof( DIRAMPFORCE ); m_pDIEffect->lpvTypeSpecificParams = &rf; m_hr = m_pInputDevice->CreateEffect( GUID_RampForce, m_pDIEffect, &m_pCreatedEffect, NULL ); }
HRESULT InitDirectInput2( HWND hDlg ) { HRESULT hr; DIPROPDWORD dipdw; // Setup the g_EffectsList circular linked list ZeroMemory( &g_EffectsList, sizeof( EFFECTS_NODE ) ); g_EffectsList.pNext = &g_EffectsList; // Create a DInput object if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) ) { ctx->OutputToConsole("PlayFFE :: DirectInput8Create"); return hr; } // Get the first enumerated force feedback device if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumFFDevicesCallback2, 0, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK ) ) ) { ctx->OutputToConsole("PlayFFE :: EnumDevices failed"); return hr; } if( g_pFFDevice == NULL ) { ctx->OutputToConsole("PlayFFE :: No force feedback device found."); return -1; } // Set the data format if( FAILED( hr = g_pFFDevice->SetDataFormat( &c_dfDIJoystick ) ) ) return hr; // Set the coop level //hr = g_pFFDevice->SetCooperativeLevel( hDlg , DISCL_EXCLUSIVE | DISCL_FOREGROUND) ; hr = g_pFFDevice->SetCooperativeLevel( hDlg , DISCL_EXCLUSIVE | DISCL_BACKGROUND) ; //DISCL_NONEXCLUSIVE // Since we will be playing force feedback effects, we should disable the // auto-centering spring. dipdw.diph.dwSize = sizeof( DIPROPDWORD ); dipdw.diph.dwHeaderSize = sizeof( DIPROPHEADER ); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = FALSE; if( FAILED( hr = g_pFFDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ) ) ) return hr; // Enumerate and count the axes of the joystick if( FAILED( hr = g_pFFDevice->EnumObjects( EnumAxesCallback, ( VOID* )&g_dwNumForceFeedbackAxis, DIDFT_AXIS ) ) ) return hr; // This simple sample only supports one or two axis joysticks if( g_dwNumForceFeedbackAxis > 2 ) g_dwNumForceFeedbackAxis = 2; // This application needs only one effect: Applying raw forces. DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG rglDirection[2] = { 0,0 }; DICONSTANTFORCE cf = { 0 }; cf.lMagnitude = 0; DIEFFECT eff; ZeroMemory( &eff, sizeof( eff ) ); eff.dwSize = sizeof( DIEFFECT ); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwDuration = INFINITE; eff.dwSamplePeriod = 0; eff.dwGain = DI_FFNOMINALMAX; eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerRepeatInterval = 0; eff.cAxes = g_dwNumForceFeedbackAxis; eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; eff.lpEnvelope = 0; eff.cbTypeSpecificParams = sizeof( DICONSTANTFORCE ); eff.lpvTypeSpecificParams = &cf; eff.dwStartDelay = 0; // Create the prepared effect if( FAILED( hr = g_pFFDevice->CreateEffect( GUID_ConstantForce, &eff, &g_pEffect, NULL ) ) ) { return hr; } if( NULL == g_pEffect ) return E_FAIL; return S_OK; }
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; }
bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, int axis_count) { if (axis_count == 0) return false; // We just use the X axis (for wheel left/right). // Gamepads seem to not care which axis you use. // These are temporary for creating the effect: std::array<DWORD, 1> rgdwAxes = {DIJOFS_X}; std::array<LONG, 1> rglDirection = {-200}; DIEFFECT eff{}; eff.dwSize = sizeof(eff); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwDuration = DI_SECONDS / 1000 * RUMBLE_LENGTH_MS; eff.dwSamplePeriod = 0; eff.dwGain = DI_FFNOMINALMAX; eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerRepeatInterval = 0; eff.cAxes = DWORD(rgdwAxes.size()); eff.rgdwAxes = rgdwAxes.data(); eff.rglDirection = rglDirection.data(); eff.dwStartDelay = 0; // Initialize parameters with zero force (their current state). DICONSTANTFORCE diCF{}; diCF.lMagnitude = 0; DIRAMPFORCE diRF{}; diRF.lStart = diRF.lEnd = 0; DIPERIODIC diPE{}; diPE.dwMagnitude = 0; diPE.lOffset = 0; diPE.dwPhase = 0; diPE.dwPeriod = DI_SECONDS / 1000 * RUMBLE_PERIOD_MS; for (auto& f : force_type_names) { if (f.guid == GUID_ConstantForce) { eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); eff.lpvTypeSpecificParams = &diCF; } else if (f.guid == GUID_RampForce) { eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); eff.lpvTypeSpecificParams = &diRF; } else { // All other forces need periodic parameters: eff.cbTypeSpecificParams = sizeof(DIPERIODIC); eff.lpvTypeSpecificParams = &diPE; } LPDIRECTINPUTEFFECT pEffect; if (SUCCEEDED(device->CreateEffect(f.guid, &eff, &pEffect, nullptr))) { if (f.guid == GUID_ConstantForce) AddOutput(new ForceConstant(this, f.name, pEffect, diCF)); else if (f.guid == GUID_RampForce) AddOutput(new ForceRamp(this, f.name, pEffect, diRF)); else AddOutput(new ForcePeriodic(this, f.name, pEffect, diPE)); } } // Disable autocentering: if (Outputs().size()) { DIPROPDWORD dipdw; dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = DIPROPAUTOCENTER_OFF; device->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph); m_run_thread.Set(); m_update_thread = std::thread(&ForceFeedbackDevice::ThreadFunc, this); } return true; }
int Game_Init(void *parms, int num_parms) { // this function is where you do all the initialization // for your game int index; // looping var char filename[80]; // used to build up files names // start up DirectDraw (replace the parms as you desire) DDraw_Init(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP); // joystick creation section //////////////////////////////// // first create the direct input object if (DirectInput8Create(main_instance,DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&lpdi,NULL)!=DI_OK) return(0); // first find the fucking GUID of your particular joystick lpdi->EnumDevices(DI8DEVCLASS_GAMECTRL, DI_Enum_Joysticks, &joystickGUID, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK); if (lpdi->CreateDevice(joystickGUID, &lpdijoy, NULL)!=DI_OK) return(0); // set cooperation level if (lpdijoy->SetCooperativeLevel(main_window_handle, DISCL_EXCLUSIVE | DISCL_BACKGROUND)!=DI_OK) return(0); // set data format if (lpdijoy->SetDataFormat(&c_dfDIJoystick2)!=DI_OK) return(0); // set the range of the joystick DIPROPRANGE joy_axis_range; // first x axis joy_axis_range.lMin = -32; joy_axis_range.lMax = 32; joy_axis_range.diph.dwSize = sizeof(DIPROPRANGE); joy_axis_range.diph.dwHeaderSize = sizeof(DIPROPHEADER); joy_axis_range.diph.dwObj = DIJOFS_X; joy_axis_range.diph.dwHow = DIPH_BYOFFSET; lpdijoy->SetProperty(DIPROP_RANGE,&joy_axis_range.diph); // now y-axis joy_axis_range.lMin = -32; joy_axis_range.lMax = 32; joy_axis_range.diph.dwSize = sizeof(DIPROPRANGE); joy_axis_range.diph.dwHeaderSize = sizeof(DIPROPHEADER); joy_axis_range.diph.dwObj = DIJOFS_Y; joy_axis_range.diph.dwHow = DIPH_BYOFFSET; lpdijoy->SetProperty(DIPROP_RANGE,&joy_axis_range.diph); // and now the dead band DIPROPDWORD dead_band; // here's our property word dead_band.diph.dwSize = sizeof(dead_band); dead_band.diph.dwHeaderSize = sizeof(dead_band.diph); dead_band.diph.dwObj = DIJOFS_X; dead_band.diph.dwHow = DIPH_BYOFFSET; // 4 will be used on both sides of the range +/- dead_band.dwData = 1000; // finally set the property lpdijoy->SetProperty(DIPROP_DEADZONE,&dead_band.diph); dead_band.diph.dwSize = sizeof(dead_band); dead_band.diph.dwHeaderSize = sizeof(dead_band.diph); dead_band.diph.dwObj = DIJOFS_Y; dead_band.diph.dwHow = DIPH_BYOFFSET; // 4 will be used on both sides of the range +/- dead_band.dwData = 1000; // finally set the property lpdijoy->SetProperty(DIPROP_DEADZONE,&dead_band.diph); // acquire the joystick if (lpdijoy->Acquire()!=DI_OK) return(0); // force feedback setup DWORD dwAxes[2] = { DIJOFS_X, DIJOFS_Y }; LONG lDirection[2] = { 0, 0 }; DIPERIODIC diPeriodic; // type-specific parameters DIENVELOPE diEnvelope; // envelope DIEFFECT diEffect; // general parameters // setup the periodic structure diPeriodic.dwMagnitude = DI_FFNOMINALMAX; diPeriodic.lOffset = 0; diPeriodic.dwPhase = 0; diPeriodic.dwPeriod = (DWORD) (0.05 * DI_SECONDS); // set the modulation envelope diEnvelope.dwSize = sizeof(DIENVELOPE); diEnvelope.dwAttackLevel = 0; diEnvelope.dwAttackTime = (DWORD) (0.01 * DI_SECONDS); diEnvelope.dwFadeLevel = 0; diEnvelope.dwFadeTime = (DWORD) (3.0 * DI_SECONDS); // set up the effect structure itself diEffect.dwSize = sizeof(DIEFFECT); diEffect.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS; diEffect.dwDuration = (DWORD) INFINITE; // (1 * DI_SECONDS); // set up details of effect diEffect.dwSamplePeriod = 0; // = default diEffect.dwGain = DI_FFNOMINALMAX; // no scaling diEffect.dwTriggerButton = DIJOFS_BUTTON0; // connect effect to trigger button diEffect.dwTriggerRepeatInterval = 0; diEffect.cAxes = 2; diEffect.rgdwAxes = dwAxes; diEffect.rglDirection = &lDirection[0]; diEffect.lpEnvelope = &diEnvelope; diEffect.cbTypeSpecificParams = sizeof(diPeriodic); diEffect.lpvTypeSpecificParams = &diPeriodic; // create the effect and get the interface to it lpdijoy->CreateEffect(GUID_Square, // standard GUID &diEffect, // where the data is &lpdieffect, // where to put interface pointer NULL); // no aggregation /////////////////////////////////////////////////////////// // load the background Load_Bitmap_File(&bitmap16bit, "MUSH_24.BMP"); // load in the four frames of the mushroom for (index=0; index<4; index++) { // create mushroom bitmaps Create_Bitmap(&mushrooms[index],0,0,32,32,16); Load_Image_Bitmap16(&mushrooms[index],&bitmap16bit,index,0,BITMAP_EXTRACT_MODE_CELL); } // end for index // now create the bug blaster bob Create_BOB(&blaster,0,0,32,32,3, BOB_ATTR_VISIBLE | BOB_ATTR_MULTI_ANIM | BOB_ATTR_ANIM_ONE_SHOT, DDSCAPS_SYSTEMMEMORY,0,16); // load in the four frames of the mushroom for (index=0; index<3; index++) Load_Frame_BOB16(&blaster,&bitmap16bit,index,index,1,BITMAP_EXTRACT_MODE_CELL); // unload the bitmap file Unload_Bitmap_File(&bitmap16bit); // set the animation sequences for bug blaster Load_Animation_BOB(&blaster,0,5,blaster_anim); // set up stating state of bug blaster Set_Pos_BOB(&blaster,320, 400); Set_Anim_Speed_BOB(&blaster,3); // create mushroom playfield bitmap Create_Bitmap(&playfield,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,16); playfield.attr |= BITMAP_ATTR_LOADED; // fill in the background Load_Bitmap_File(&bitmap16bit, "GRASS_24.BMP"); // load the grass bitmap image Load_Image_Bitmap16(&playfield,&bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); // seed random number generator srand(Start_Clock()); // create the random mushroom patch for (index=0; index<50; index++) { // select a mushroom int mush = rand()%4; // set mushroom to random position mushrooms[mush].x = rand()%(SCREEN_WIDTH-32); mushrooms[mush].y = rand()%(SCREEN_HEIGHT-128); // now draw the mushroom into playfield Draw_Bitmap16(&mushrooms[mush], playfield.buffer, playfield.width*2,1); } // end for // hide the mouse if (!WINDOWED_APP) ShowCursor(FALSE); // return success return(1); } // end Game_Init
HRESULT _stdcall CreateEffect(REFGUID a,LPCDIEFFECT b,LPDIRECTINPUTEFFECT *c,LPUNKNOWN d) { return RealDevice->CreateEffect(a,b,c,d); }
// 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 ); }