Tricks of the Windows Game Programming Gurus:Uplinking with DirectInput and Force Feedback

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

#### A Force Feedback Demo

If you like, you can just use the force feedback device like a normal joystick. However, the data packet sent back is now `DIJOYSTATE2` rather than `DIJOYSTATE`. The explanation of the code would take too long, so you’re going to have to figure it out based on the comments and the demo program.

However, the code basically sets up an effect that is composed of an envelope and a periodic description. Moreover, the effect is connected to the joystick fire trigger, so it starts when the trigger is held. Here’s the code that will set up the force feedback effect, assuming you have the `GUID` of the device and have set up the force feedback joystick as shown previously:

    // 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);

> Note: The force feedback code shown previously is based on the example in the DirectX SDK, so you can refer there for a much more in-depth explanation.

    diEffect.lpvTypeSpecificParams = &diPeriodic;

    // create the effect and get the interface to it

    lpdijoy2->CreateEffect(GUID_Square,   // standard GUID

                            &diEffect,   // where the data is

                            &lpdieffect, // where to put interface pointer

                            NULL);           // no aggregation

For a demo of this in action, check out `DEMO9_4.CPP`. It basically takes your little centipede demo and adds a machine gun to it! Of course, you need a force feedback joystick for the demo to work.

### Writing a Generalized Input System: T3DLIB2.CPP

Writing a simple set of wrapper functions around DirectInput is almost a no-brainer. Well, it takes some brains, but for the most part it’s fairly easy. All you need to do is create an API with a very simple interface and very few parameters that

  • Initializes the DirectInput system.
  • Sets up and acquires the keyboard, mouse, and joystick (or any subset).
  • Reads data from any of the input devices.
  • Shuts down, unacquires, and releases everything.

I have created such an API, and it’s available in `T3DLIB2.CPP|H` on the CD. The API does everything you need to initialize DirectInput and read any device. However, I didn’t do any input merging, as shown in the example a few sections previous. Rather, you will still receive input in terms of standard DirectInput device state(s) structures, and you’ll process the various fields within each device state structure (keyboard, mouse, and joystick). However, this gives you the most freedom.

Before reviewing the functions, take a look at Figure 9.15. It depicts the relationship between each device and the data flow.

Figure 9.15 The DirectInput software system.

Here are the globals for the library:

    LPDIRECTINPUT        lpdi;      // dinput object

    LPDIRECTINPUTDEVICE  lpdikey;   // dinput keyboard

    LPDIRECTINPUTDEVICE  lpdimouse; // dinput mouse

    LPDIRECTINPUTDEVICE2 lpdijoy;   // dinput joystick

    GUID    joystickGUID; // guid for main joystick

    char    joyname[80]; // name of joystick

    // all input is stored in these records

    UCHAR keyboard_state[256]; // contains keyboard state table

    DIMOUSESTATE mouse_state;  // contains state of mouse

    DIJOYSTATE joy_state;      // contains state of joystick

    int joystick_found;        // tracks if stick is plugged in

Input from the keyboard is placed in `keyboard_state[]`, the mouse data is stored in `mouse_state`, and the joystick data is stored in `joy_state` by the input system. The structures of these records are the standard DirectInput device state structures. But in general, the mouse and joystick are roughly equivalent as far as the x,y position goes. That is, you access them via the fields `lX` and `lY`, and the buttons are `BOOLEANs` in `rgbButtons[]`.

Let’s get to the functions. The variable `joystick_found` is a Boolean that is set when you request joystick access. If a joystick is found, it is True; otherwise, it is False. With it, you can conditionally block out code that uses the joystick. So without further ado, here is the new API.

Function Prototype:

    int DInput_Init(void);

Purpose:

`DInput_Init()` initializes the DirectInput input system. It creates the main COM object and returns True if successful, False otherwise. And of course, the global `lpdi` will be valid. The function does not create any devices, though. Here’s an example of initializing the input system:

    if (!DInput_Init())

       { /* error */ }

Function Prototype:

    void DInput_Shutdown(void);

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.