i
i
i
i
i
i
i
i
482 17. Programming Input and Force Feedback
// this is the function called to enumerate the joystick devices
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE
*
pdidInstance,
VOID
*
pContext ){
HRESULT hr;
LPDIRECTINPUTDEVICE8 pJoystick;
// create the joystick device
hr = g_pDI->CreateDevice( pdidInstance->guidInstance,&pJoystick,NULL);
if( FAILED(hr) ) return DIENUM_CONTINUE;
nJoys++; // we will stop when we have two
if(nJoys == 1)g_pJoystick1=pJoystick;
else g_pJoystick2=pJoystick;
if(nJoys == 2)return DIENUM_STOP; // only need 2
return DIENUM_CONTINUE;
}
// this function will see if the joystick has more than 2 axes;
// some do, some don’t
BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE
*
pdidoi,
VOID
*
pContext ){
DIPROPRANGE diprg; // a structure to pass data to a DI range property
DIPROPDWORD diprw; // a structure to pass data to a WORD size DI property
LPDIRECTINPUTDEVICE8 pJoystick = (LPDIRECTINPUTDEVICE8)pContext;
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYOFFSET;
diprg.diph.dwObj = pdidoi->dwOfs; // Specify the enumerated axis
diprg.lMin = -1000; // these are integer units and give
// the min and max range of the joystick
diprg.lMax = +1000;
if( pdidoi->dwType & DIDFT_AXIS ){
if(FAILED(pJoystick->SetProperty(DIPROP_RANGE,
&diprg.diph)))return DIENUM_STOP;
// If this is an x, y, or z axis set the dead zone
if(pdidoi->guidType == GUID_XAxis ||
pdidoi->guidType == GUID_YAxis ||
pdidoi->guidType == GUID_ZAxis){
diprw.diph.dwSize = sizeof(DIPROPDWORD);
diprw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprw.diph.dwHow = DIPH_BYOFFSET;
diprw.diph.dwObj = pdidoi->dwOfs;
diprw.dwData = 100;
switch( pdidoi->dwOfs ){
case DIJOFS_X:
if(FAILED(pJoystick->SetProperty(DIPROP_DEADZONE,
&diprw.diph )))return DIENUM_STOP;
break;
Listing 17.2. For joystick devices, the number of available device axes has to be deter-
mined and configured so that values returned to the program when movement occurs
along an axis fall within a certain range and have known properties.
i
i
i
i
i
i
i
i
17.1. DirectInput 483
// repeat for Y and Z axes
}
}
return DIENUM_CONTINUE;
}
// this function checks for sliders on the joystick, sometimes used
// instead of twist axis
BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE
*
pdidoi,
VOID
*
pContext ){
// Enumerate for other device attributes
if (pdidoi->guidType == GUID_Slider){
// apart from axes - we may wish to use a slider
bSlider=TRUE;
}
return DIENUM_CONTINUE;
}
Listing 17.2. (continued).
in a joystick which is supposed to return it to dead center may actually return
it to + 20 the first time and 50 the second time. Thus, by setting up a
dead zone, our application can ensure that if the user releases the handle, the
joystick will always report an axis coordinate of zero.
17.1.2 Acquiring Data
Function UpdateInputState(..) (see Listing 17.3) is used to obtain
the current state from two of the attached joystick devices. This informa-
tion includes position, button presses etc. and requires a call to the
Poll()
#define SCALE 0.0001f // Scale the positions returned in the global
float x_rjoy_pos; // variables so that the full range of motion
float y_rjoy_pos; // on all axis is in the range [-1.000, +1.000]
float z_rjoy_pos;
float x_ljoy_pos; // and the global variables for joystick2
float y_ljoy_pos;
float z_ljoy_pos;
long nJoys=0; // number of suitable joysticks found
Listing 17.3. Function to obtain the current axis positions and state of buttons pressed.
Before we can obtain any information about a joystick, it must be acquired by the
program. Otherwise, there could be a conflict with another application also wishing
to use a joystick device.
i
i
i
i
i
i
i
i
484 17. Programming Input and Force Feedback
HRESULT UpdateInputState( HWND hDlg ){
HRESULT hr;
// joystick state information is provided in this strucure
DIJOYSTATE2 js;
if( g_pJoystick1 ) { // do the first joystick
hr = g_pJoystick1->Poll(); // see if we can access the joystick state
if( FAILED(hr) ){ // if not - try to get the joystick back
hr = g_pJoystick1->Acquire();
while(hr == DIERR_INPUTLOST )hr = g_pJoystick1->Acquire();
return S_OK; // got it!
}
// GetDeviceState fills the state information structure
if(FAILED(hr = g_pJoystick1->GetDeviceState(sizeof(DIJOYSTATE2)
,&js)))return hr;
x_rjoy_pos=(float)(js.lX)
*
SCALE; // scale it into range [-1.0 , 1.0]
y_rjoy_pos=(float)(js.lY)
*
SCALE; // This matches the integer range set
z_rjoy_pos=(float)(js.lZ)
*
SCALE; // during enumeration to [-1000,+1000].
if(js.rgbButtons[0] & 0x80).. // do someting with button 0
if(js.rgbButtons[1] & 0x80).. // button 1 etc.
}
// Repeat the code for JOYSTICK 2
return S_OK;
}
Listing 17.3. (continued).
method on the joystick object followed by a call to GetDeviceState() on
the same object. All the details are returned in a DIJOYSTATE2 structure
(variable js). Structure members are scaled and assigned to global variables
recording axis position etc. Note the use of the Acquire() method if the
Poll() method returns an error code. An error will occur if another applica-
tion has grabbed the use of the joystick. Before our application can again get
data from the joystick, it must us the
Acquire() method to claim exclusive
use of it.
17.2 Force Feedback with DirectInput
Normally, one associates force feedback with highly specialized and expensive
equipment, but it can be achieved in even modestly priced joystick devices be-
cause they are targeted at the game market and are in mass production. These
devices cannot deliver the same accurate haptic response as the professional
devices we discussed in Chapter 4, but they are able to provide a reasonable
degree of user satisfaction. Since DirectInput was designed with the com-
i
i
i
i
i
i
i
i
17.2. Force Feedback with DirectInput 485
puter game developer in mind, it offers a useful set of functions for control-
ling force-feedback devices. In terms of the haptics and forc es we discussed in
Section 4.2, DirectInput can support constant forces, frictional for c es, ramp
forces and periodic forces.
// pointer to constant force effect
LPDIRECTINPUTEFFECT g_pEffect1c = NULL;
// friction force effect
LPDIRECTINPUTEFFECT g_pEffect1f = NULL;
// spring force effect
LPDIRECTINPUTEFFECT g_pEffect1s = NULL;
// constructed effect (an impulse)
LPDIRECTINPUTEFFECT g_pEffect1poke = NULL;
static BOOK bForce1=FALSE, bForce2=FALSE;
// enumerate - joysticks with force feedback - very similar to non FF joysticks.
static BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE
*
pInst,
VOID
*
pContext ){
LPDIRECTINPUTDEVICE8 pDevice;
HRESULT hr;
hr = g_pDI->CreateDevice( pInst->guidInstance, &pDevice, NULL );
if(FAILED(hr))return DIENUM_CONTINUE;
nJoys++;
if(nJoys == 1){g_pJoystick1=pDevice; bForce1=TRUE;}
else {g_pJoystick2=pDevice; bForce2=TRUE;}
if(nJoys == 2)return DIENUM_STOP; // only need 2
return DIENUM_CONTINUE;
}
// the following changes are needed to set up the force feedback joysticks
HRESULT InitDirectInput( HWND hDlg, HINSTANCE g_hInst){
..// enumerate for Force Feedback (FF) devices ONLY
hr = g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumFFDevicesCallback,
NULL,DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK);
.. // if joystick 1 is found with FF create the effects for constant force,
// friction and impulse.
if(bForce1)CreateForceFeedbackEffects(&g_pEffect1c,&g_pEffect1f,
&g_pEffect1s,&g_pEffect1poke,
g_pJoystick1);
..// Do similar thing for second or more FF joystick
return S_OK;
}
Listing 17.4. To find a joystick with force feedback, a device enumeration procedure
similar to that for non-force-feedback devices is performed. The source code for
CreateForceFeedbackEffects() is outlined in Listing 17.5.
i
i
i
i
i
i
i
i
486 17. Programming Input and Force Feedback
static void CreateForceFeedbackEffects(LPDIRECTINPUTEFFECT
*
ppEffectC,
LPDIRECTINPUTEFFECT
*
ppEffectF,LPDIRECTINPUTEFFECT
*
ppEffectS,
LPDIRECTINPUTEFFECT
*
ppEffectPoke, LPDIRECTINPUTDEVICE8 pDevice){
DIEFFECT eff; // effect parameter structure
DIPROPDWORD dipdw;
HRESULT hr;
// set the effects to work in 2 dimensions (X Y)
DWORD rgdwAxes[2] = { DIJOFS_X, DIJOFS_Y };
// set default values for the parameters of the forces - these will be
// changes as necessary
// consult DirectInput Manual for details.
LONG rglDirection[2] = { 0, 0 };
DICONSTANTFORCE cf = { 0 };
DICONDITION cd = { 0, 10000,10000,0, 0, 0 };
DICONDITION cd2[2] = { 0, 10000,10000,0, 0, 0, 0, 10000, 10000,
0, 0, 0};
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = FALSE;
// tell force to automatically center the joystick on startup
if(FAILED(hr=pDevice->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph)))return;
// the "eff" structure shows typical settings for a constant force.
ZeroMemory( &eff, sizeof(eff) ); // constant force effect
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE; // force continues indefinitely
eff.dwSamplePeriod = 0; // no time limit on force
eff.dwGain = DI_FFNOMINALMAX; // maximum force
eff.dwTriggerButton = DIEB_NOTRIGGER; // force is NOT triggered
eff.dwTriggerRepeatInterval = 0;
eff.cAxes = 2; // number of axes to get force
eff.rgdwAxes = rgdwAxes; // which axes
eff.rglDirection = rglDirection; // direction of force
eff.lpEnvelope = 0; // no - envelope constant force
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0;
// create the effect using the "eff" effect structure - return
// pointer to effect
if(FAILED(hr=pDevice->CreateEffect(GUID_ConstantForce,&eff,ppEffectC,
NULL)))return;
..//
..// the other effects are set in a similar way.
// Consult the full project code for details
return;
}
Listing 17.5. Create a constant force effect that can be played out to a joystick device.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.181.154