Remove DInput and use ...

Use this board to ask specific questions about the DirectX 9 book, or specific problems you are having with it (e.g. problems understanding a section or source code). If relevant, include the chapter, page number, sample program name, or a brief code snippet.

Remove DInput and use ...

Postby kim.wahlman on Mon Jun 21, 2010 4:29 pm

A vague topic I know but anyway, I read on MSDN that using DInput for keyboard and/or mouse events is not desirable. Instead it should be used with windows messages. I have tried to figure out how to make this change but I am stuck.
Anyone out there that has made this change that feel OK with sharing it?

I have tried a few things like moving 'dir' to the class header in the Camera class but I end up with
Code: Select all
1>camera.cpp(94) : error C2677: binary '+=' : no global operator found which takes type 'D3DXVECTOR3' (or there is no acceptable conversion)
1>camera.cpp(96) : error C2677: binary '-=' : no global operator found which takes type 'D3DXVECTOR3' (or there is no acceptable conversion)
1>camera.cpp(98) : error C2677: binary '+=' : no global operator found which takes type 'D3DXVECTOR3' (or there is no acceptable conversion)
1>camera.cpp(100) : error C2677: binary '-=' : no global operator found which takes type 'D3DXVECTOR3' (or there is no acceptable conversion)
1>camera.cpp(102) : error C2276: '&' : illegal operation on bound member function expression
1>camera.cpp(102) : error C2276: '&' : illegal operation on bound member function expression
1>camera.cpp(103) : error C3867: 'Camera::dir': function call missing argument list; use '&Camera::dir' to create a pointer to member
1>camera.cpp(103) : error C2296: '*' : illegal, left operand has type 'D3DXVECTOR3 (__thiscall Camera::* )(float,float,float)'


I have tried with this
Code: Select all
#ifndef _CAMERA_H_
#define _CAMERA_H_

#include <d3dx9.h>
#include "d3dApp.h"

class Camera
{
public:
   Camera();
   const D3DXMATRIX& view() const;
   const D3DXMATRIX& proj() const;
   const D3DXMATRIX& viewProj() const;

   const D3DXVECTOR3& right() const;
   const D3DXVECTOR3& up() const;
   const D3DXVECTOR3& look() const;

   D3DXVECTOR3& pos();

   void lookAt(D3DXVECTOR3& pos, D3DXVECTOR3& target, D3DXVECTOR3& up);
   void setLens(float fov, float aspect, float nearZ, float farZ);
   void setSpeed(float s);
   void update(float dt);

   /////////////////////////////////////////////////////////////////
   // READ ME !!! This is what I have tried, without success.
   /////////////////////////////////////////////////////////////////
   D3DXVECTOR3 dir(float x, float y, float z);
   D3DXVECTOR3& dir(float x, float y, float z);
   ////////////////////////////////////////////////////////////////
   // After moving this I tried to access dir in the msgProc function to be able
   // to pass the variables to Camera  function 'update' and get the change
   // that way instead. But I cant get it to work :(
   ///////////////////////////////////////////////////////////////
protected:
   void buildView();

   D3DXMATRIX mView;
   D3DXMATRIX mProj;
   D3DXMATRIX mViewProj;

   D3DXVECTOR3 mPosW;
   D3DXVECTOR3 mRightW;
   D3DXVECTOR3 mUpW;
   D3DXVECTOR3 mLookW;

   float mCamSpeed;
};

#endif
I wish to be able to use it in the msgProc function listed here.
Code: Select all
LRESULT d3dApp::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
   // Some code before ...
   case WM_KEYDOWN:
      {
         int vCode    = (int)wParam;
         int keyState = (int)lParam;

         // Movement
         switch(vCode)
         {
         case VK_UP:
            
            break;

         case VK_DOWN:

            break;

         case VK_LEFT:

            break;

         case VK_RIGHT:

            break;
         }

         if( wParam == VK_ESCAPE )
            enableFullScreenMode(false);
         else if( wParam == 'F' )
            enableFullScreenMode(true);
         return 0;
      }
// some code after ...
}
To control the camera. Would really like some assistance to get this to work! I pretty much uses the example unchanged so I can get it to work and after that learn from it.
Instead of poking around in parts I shouldn't poke in trying to figure this one out.

I guess I am doing this the wrong way, so anyone know how I can do this?
kim.wahlman
 
Posts: 11
Joined: Mon Jul 20, 2009 4:18 pm

Re: Remove DInput and use ...

Postby Hieran_Del8 on Mon Jun 21, 2010 8:12 pm

kim.wahlman wrote:I read on MSDN that using DInput for keyboard and/or mouse events is not desirable. Instead it should be used with windows messages. I have tried to figure out how to make this change but I am stuck.
Anyone out there that has made this change that feel OK with sharing it?

That's what they said.

jk. Using window messages for raw input is easy once you do it a few times. There's a few tricks to use it with a window message proc. You can use raw input for mice, keyboards, joysticks, gamepads, hid's, etc.

The reason for recommending against DirectInput is chiefly the keyboard input. DirectInput uses its own definitions of the keys, and does not translate well to keyboards in other countries. I think the German keyboard, for instance, is registered by DirectInput as one key to the right of the actual key. Microsoft recommends GetAsyncKeyState() or GetKeyboardState() instead.

The mouse input does not have acceleration enabled, which windows supports. So if you drag the mouse very quickly, it will only register the change in motion, not an accelerated change in motion. Windows messaging accounts for this. (Actually, I can't seem to verify this point at the moment. I can't find the doc that wrote about this.)

Because HID's can be abstracted from the software via this method, it makes raw input a nice mechanism to work with future technologies without need to upgrade.

kim.wahlman wrote:I have tried a few things like moving 'dir' to the class header in the Camera class

I'm not sure I understand the intent of adding that member function. Also, you can't have two member functions with the same name and parameters, differing only by return values.

I'll demonstrate using the raw mouse input and GetKeyboardState(). I will also demonstrate how to adjust the window message proc to account for it.

:arrow: Step 1: Initializing Raw Input

First it is necessary to register a device for raw input. To do this, I've written a separate function InitializeRawInput() that accepts a window handle as a parameter. It registers the mouse with the window as such:
Code: Select all
void InitializeRawInput(HWND hWnd)
{
   RAWINPUTDEVICE mouse;
   mouse.usUsagePage = 1;
   mouse.usUsage = 2;
   mouse.dwFlags = 0;
   mouse.hwndTarget = hWnd;

   RegisterRawInputDevices(&mouse, 1, sizeof(mouse));
}

It is called from the window message proc when it receives the WM_CREATE message:
Code: Select all
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
   case WM_CREATE:
      InitializeRawInput(hWnd);
      break;
...

:arrow: Step 2: Catching Raw input
Now with the mouse device registered for raw input, it is time to catch the raw input messages (WM_INPUT):
Code: Select all
   case WM_INPUT:
      {
            RAWINPUT input;

            UINT nSize= sizeof(input);
            GetRawInputData((HRAWINPUT)lParam,
                            RID_INPUT,
                            &input,
                            &nSize,
                            sizeof(input.header));

            ProcessMouseInput(&input); //user function
            return 0;
      }
      break;

The ProcessMouseInput() function is provided for convenience in processing specifically mouse messages. Since we did not register any other device for raw input, we will only get messages for the mouse. If you decide to register other devices (such as keyboard or joystick), you will need to make sure it is input from the mouse.

My ProcessMouseInput() function only adds the change in mouse position to a global mouse position, which contains an x and y value:
Code: Select all
void ProcessMouseInput(RAWINPUT *ri)
{
   g_mouse.x += ri->data.mouse.lLastX;
   g_mouse.y += ri->data.mouse.lLastY;
}

You can do whatever you want with the data. You can also retrieve the button presses through this RAWINPUT::data::mouse member.

:arrow: Step 3: Retrieving Keyboard state

Using GetKeyboardState() is really easy:
Code: Select all
BYTE keybrd[256];

GetKeyboardState(keybrd);

if(keybrd[VK_SHIFT] & 0x80)
   FireThrusters();
if(keybrd[VK_SPACE] & 0x80)
   FireBullet();
...

I hope this has been a helpful start to using raw input. I recommend reading the msdn docs on raw input. They also show an example of registering a game pad and joystick.

http://msdn.microsoft.com/en-us/library/ms645546%28v=VS.85%29.aspx
Hieran_Del8
 
Posts: 203
Joined: Fri Oct 03, 2008 12:18 pm
Location: Marengo, IL

Re: Remove DInput and use ...

Postby kim.wahlman on Thu Jun 24, 2010 4:33 am

Thanks! I will take a look at this when I have time, I really appreciate this!
kim.wahlman
 
Posts: 11
Joined: Mon Jul 20, 2009 4:18 pm

Re: Remove DInput and use ...

Postby kim.wahlman on Sun Jun 27, 2010 8:37 am

Thanks! I got it to work now, the keyboard anyway :p have to figure out the mouse abit now :) but thanks for the help to remove DInput :)
kim.wahlman
 
Posts: 11
Joined: Mon Jul 20, 2009 4:18 pm

Re: Remove DInput and use ...

Postby kim.wahlman on Tue Jun 29, 2010 12:00 am

Ok, I can't figure this one out. I wish to use raw input from the mouse to change pitch and yAngle. Read the comments
Code: Select all
void Camera::update(float dt)
{
   BYTE keyboard[256];
   GetKeyboardState(keyboard);

   D3DXVECTOR3 dir(0.0f, 0.0f, 0.0f);
   if( keyboard[0x57] & 0x80 ) // W
      dir += mLookW;
   if( keyboard[0x53] & 0x80 ) // S
      dir -= mLookW;
   if( keyboard[0x44] & 0x80 ) // D
      dir += mRightW;
   if( keyboard[0x41] & 0x80 ) // A
      dir -= mRightW;
   
   D3DXVec3Normalize(&dir, &dir);
   mPosW += dir*mCamSpeed*dt;

   // Raw input from the MOUSE ////////////////////////////////////////////////////////////
   // UINT dwSize;
   //
   // I have to retrive lParam from d3dApp::msgProc somehow OR send the data to pitch somehow
   // or I am just using your function the wrong way (made a class of it).
   // GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
   //
   // LPBYTE lpb = new BYTE[dwSize];
   // RAWINPUT* raw = (RAWINPUT*)lpb;
   // Error(s) /////////////////////////////////////////////////////////////////////////////
   // Tried to move GetRawInputData(...) inside d3dApp::msgProc but only got the result
   // camera.cpp(119) : error C2440: 'initializing' : cannot convert from 'void' to 'float'
   // row 119:
   //               float pitch = rInput->ProcessMouseInput(raw);
   //////////////////////////////////////////////////////////////////////////////////////

   // float pitch = rInput->ProcessMouseInput(raw);

   //float pitch  = gDInput->mouseDY() / 150.0f;
   float yAngle = gDInput->mouseDX() / 150.0f;

   D3DXMATRIX R;
   D3DXMatrixRotationAxis(&R, &mRightW, pitch);
   D3DXVec3TransformCoord(&mLookW, &mLookW, &R);
   D3DXVec3TransformCoord(&mUpW, &mUpW, &R);

   D3DXMatrixRotationY(&R, yAngle);
   D3DXVec3TransformCoord(&mRightW, &mRightW, &R);
   D3DXVec3TransformCoord(&mUpW, &mUpW, &R);
   D3DXVec3TransformCoord(&mLookW, &mLookW, &R);

   buildView();
   mViewProj = mView * mProj;
   // delete[] lpb;
}


Here is the rawInput header + source

Code: Select all
#ifndef _RAWINPUT_H_
#define _RAWINPUT_H_

#include "d3dApp.h"

class rawInput
{
public:
   rawInput();
   virtual ~rawInput();
   void InitRawInput(HWND hwnd);
   void ProcessMouseInput(RAWINPUT *ri);

protected:
   D3DXVECTOR3 g_mouse;
};

extern rawInput* rInput;

#endif
Code: Select all
#include "h/rawInput.h"

rawInput::rawInput()
{
   g_mouse = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
}

rawInput::~rawInput()
{
}

void rawInput::InitRawInput(HWND hWnd)
{
   RAWINPUTDEVICE mouse;
   mouse.usUsagePage   = 1;
   mouse.usUsage      = 2;
   mouse.dwFlags      = 0;
   mouse.hwndTarget   = hWnd;

   RegisterRawInputDevices(&mouse, 1, sizeof(mouse));
}

void rawInput::ProcessMouseInput(RAWINPUT *ri)
{
   g_mouse.x += ri->data.mouse.lLastX;
   g_mouse.y += ri->data.mouse.lLastY;
}
kim.wahlman
 
Posts: 11
Joined: Mon Jul 20, 2009 4:18 pm

Re: Remove DInput and use ...

Postby Hieran_Del8 on Wed Jun 30, 2010 11:39 pm

It can be a little confusing at first. I'm having difficulty understanding the intent of the separate raw input class and a direct input class. I've written a simple DirectInput-like class, named DXTestInput2. Here's the code:
Code: Select all
//DXTestInput2.h
//by Jay Tennant 6/30/10
//performs initialization on raw input for testing purposes
//to replace DXTestInput.h, which used DirectInput

#pragma once

#include <windows.h>

class DXTestInput2
{
private:
   BYTE m_keyboardState[256];
   BYTE m_keyboardStateRaw[256];
   struct DXTI_MOUSE_STATE
   {
      LONG x, y, wheel; //current position
      LONG dx, dy, dwheel; //change in position
      BOOL left, middle, right; //buttons
   };
   enum DXTI_MOUSE_BUTTON_STATE //named state of mouse buttons
   {
      UP = FALSE,
      DOWN = TRUE,
   };
   DXTI_MOUSE_STATE m_mouseState;
   DXTI_MOUSE_STATE m_mouseStateRaw;
public:
   DXTestInput2()
   {
      ZeroMemory(m_keyboardState, sizeof(m_keyboardState));
      ZeroMemory(m_keyboardStateRaw, sizeof(m_keyboardStateRaw));
      ZeroMemory(&m_mouseState, sizeof(m_mouseState));
      ZeroMemory(&m_mouseStateRaw, sizeof(m_mouseStateRaw));
   }

   //registers a mouse for raw input;
   //if DXTI_USE_RAW_INPUT was defined, the keyboard is registered for raw input, too;
   //otherwise, GetKeyboardState() is used
   HRESULT Initialize(HWND hWnd)
   {
      RAWINPUTDEVICE Rid[2];
       
      Rid[0].usUsagePage = 0x01;
      Rid[0].usUsage = 0x02;
      Rid[0].dwFlags = 0;
      Rid[0].hwndTarget = hWnd;

      Rid[1].usUsagePage = 0x01;
      Rid[1].usUsage = 0x06;
      Rid[1].dwFlags = 0;
      Rid[1].hwndTarget = hWnd;

#ifndef DXTI_USE_RAW_KEYBOARD
      if( FALSE == RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) ) //registers only the mouse
         return E_FAIL;
#else
      if( FALSE == RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) ) //registers both mouse and keyboard
         return E_FAIL;
#endif
      return S_OK;
   }

   //processes WM_INPUT messages
   LRESULT ProcessInput(HWND hWnd, WPARAM wParam, LPARAM lParam)
   {
      if(RIM_INPUTSINK == wParam)
         return DefWindowProc(hWnd, WM_INPUT, wParam, lParam);

      RAWINPUT input;

      UINT nSize= sizeof(input);
      GetRawInputData((HRAWINPUT)lParam,
                  RID_INPUT,
                  &input,
                  &nSize,
                  sizeof(input.header));

      switch(input.header.dwType)
      {
      case RIM_TYPEKEYBOARD: //this message only occurs when the keyboard is registered for raw input
         m_keyboardStateRaw[input.data.keyboard.VKey] = (WM_KEYDOWN == input.data.keyboard.Message) ? 0x80 : 0x0;
         break;
      case RIM_TYPEMOUSE:
         switch(input.data.mouse.usButtonFlags)
         {
         case RI_MOUSE_LEFT_BUTTON_DOWN:
            m_mouseStateRaw.left = DOWN;
            break;
         case RI_MOUSE_LEFT_BUTTON_UP:
            m_mouseStateRaw.left = UP;
            break;
         case RI_MOUSE_RIGHT_BUTTON_DOWN:
            m_mouseStateRaw.right = DOWN;
            break;
         case RI_MOUSE_RIGHT_BUTTON_UP:
            m_mouseStateRaw.right = UP;
            break;
         case RI_MOUSE_MIDDLE_BUTTON_DOWN:
            m_mouseStateRaw.middle = DOWN;
            break;
         case RI_MOUSE_MIDDLE_BUTTON_UP:
            m_mouseStateRaw.middle = UP;
            break;
         case RI_MOUSE_WHEEL:
            m_mouseStateRaw.dwheel += input.data.mouse.usButtonData;
            break;
         }
         m_mouseStateRaw.dx += input.data.mouse.lLastX;
         m_mouseStateRaw.dy += input.data.mouse.lLastY;
         break;
      }
      return DefWindowProc(hWnd, WM_INPUT, wParam, lParam);
   }

   //updates the mouse and keyboard for input state
   void Poll()
   {
      PollKeyboard();
      PollMouse();
   }

   //updates only the keyboard for input state
   void PollKeyboard()
   {
#ifndef DXTI_USE_RAW_KEYBOARD
      ::GetKeyboardState(m_keyboardState);
#else
      ::memcpy(m_keyboardState, m_keyboardStateRaw, sizeof(m_keyboardState));
#endif
   }

   //updates only the mouse for input state
   void PollMouse()
   {
      m_mouseState.x += m_mouseState.dx;
      m_mouseState.y += m_mouseState.dy;
      m_mouseState.wheel += m_mouseState.dwheel;

      m_mouseState.dx = m_mouseStateRaw.dx;
      m_mouseState.dy = m_mouseStateRaw.dy;
      m_mouseState.dwheel = m_mouseStateRaw.dwheel;
      
      m_mouseState.left = m_mouseStateRaw.left;
      m_mouseState.middle = m_mouseStateRaw.middle;
      m_mouseState.right = m_mouseStateRaw.right;

      ZeroMemory(&m_mouseStateRaw, sizeof(m_mouseStateRaw));

      m_mouseStateRaw.left = m_mouseState.left;
      m_mouseStateRaw.middle = m_mouseState.middle;
      m_mouseStateRaw.right = m_mouseState.right;
   }

   //keyboard state checks
   BOOL IsKeyDown(INT vkCode) const {return m_keyboardState[vkCode & 0xff] & 0x80 ? TRUE : FALSE;}
   BOOL IsKeyUp(INT vkCode) const {return m_keyboardState[vkCode & 0xff] & 0x80 ? FALSE : TRUE;}

   //summed mouse state checks/sets;
   //use as convenience, ie. keeping track of movements without needing to maintain separate data set
   LONG GetMouseX() const {return m_mouseState.x;}
   LONG GetMouseY() const {return m_mouseState.y;}
   LONG GetMouseWheel() const {return m_mouseState.wheel;}
   void SetMouseX(LONG x) {m_mouseState.x = x;}
   void SetMouseY(LONG y) {m_mouseState.y = y;}
   void SetMouseWheel(LONG wheel) {m_mouseState.wheel = wheel;}

   //relative mouse state changes
   LONG GetMouseXChange() const {return m_mouseState.dx;}
   LONG GetMouseYChange() const {return m_mouseState.dy;}
   LONG GetMouseWheelChange() const {return m_mouseState.dwheel;}

   //mouse button state checks
   BOOL IsMouseLButtonDown() const {return (m_mouseState.left == DOWN) ? TRUE : FALSE;}
   BOOL IsMouseLButtonUp() const {return (m_mouseState.left == UP) ? TRUE : FALSE;}
   BOOL IsMouseRButtonDown() const {return (m_mouseState.right == DOWN) ? TRUE : FALSE;}
   BOOL IsMouseRButtonUp() const {return (m_mouseState.right == UP) ? TRUE : FALSE;}
   BOOL IsMouseMButtonDown() const {return (m_mouseState.middle == DOWN) ? TRUE : FALSE;}
   BOOL IsMouseMButtonUp() const {return (m_mouseState.middle == UP) ? TRUE : FALSE;}
};

I'm sorry that it's a bit messy. I like writing these test classes in headers so I don't need to compile and link to other assemblies, but for a project I'd highly recommend separating the implementations from the declarations--unlike what's been done here. I've done preliminary testing on it, so I know it works. The only problem I get is when both left and right mouse buttons press or release simultaneously, they sometimes fail to recognize the change in state. I'll have to look into this further.

After an instance of the object is created, Initialize() must be called, supplying a proper window handle. Upon successful initialization, the window proc associated with the window will start receiving WM_INPUT messages.

DXTestInput2 contains all the functionality of DirectInput. Poll() is used to synchronize the input messages with actual data state. This is useful in case the input data is being processed in a different thread from where the input data is being used.

If you want DXTestInput2 to manage raw input from the keyboard, you must define DXTI_USE_RAW_KEYBOARD before the header is included. I don't know if that works better than using GetKeyboardState(), but the usage will be transparent.

Use DXTestInput2::IsKey*() to test if a key is up or down. Use DXTestInput2::IsMouse*() to determine mouse button states. Use DXTestInput2::GetMouse*Change() to get changes in x, y, or wheel. And use Poll() whenever you want to synchronize the actual data state (like when you use poll() with DirectInput).

It would be advantageous to create an instance in a d3dapp derivative, such as:
Code: Select all
class MyApp : public D3DApp
{
protected:
   DXTestInput2 mInput;
//...etc...
public:
   virtual LRESULT msgProc(UINT msg, WPARAM wParam, LPARAM lParam);
//...etc...
};

and the overridden msgProc() would look like:
Code: Select all
LRESULT MyApp::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
   if(msg == WM_INPUT)
      return mInput.ProcessInput(mhMainWnd, msg, wParam, lParam);
   return D3DApp::msgProc(msg, wParam, lParam);
}


I looked through your code, and just as a nomenclature principle, I recommend adjusting the name of the member variable "g_mouse". I prefix "g_" to the beginning of a variable to signify to me that the variable is in global scope. I use "m_" to signify the variable is a member of a class. This is a well adopted practice of prefixing. It helps programmers read and share code more easily. For instance, Frank prefixes objects with "g" when they are global, and "m" when they are members. When a variable is local (like those used once in a function), it does not use those prefixes.

I think you're on the right track otherwise in the code. Use this DXTestInput2 module along with your own code, and let me know how it goes.
Hieran_Del8
 
Posts: 203
Joined: Fri Oct 03, 2008 12:18 pm
Location: Marengo, IL

Re: Remove DInput and use ...

Postby kim.wahlman on Fri Jul 02, 2010 2:10 am

The DInput class will be removed totaly when I get the rawinput to work. Sorry if I made that unclear, I wish to only use rawinput for mouse and keyboard.
Any DInput code still in my code is there until the rawinput replace it :)
kim.wahlman
 
Posts: 11
Joined: Mon Jul 20, 2009 4:18 pm


Return to DirectX 9 Book

Who is online

Users browsing this forum: No registered users and 1 guest