Game Development Community

Add support for the P5 Glove to Torque

by Brian Richardson · 12/14/2004 (11:09 pm) · 26 comments

Download Code File

Adding new input devices to Torque
Add support for the P5 Glove to The Torque Game Engine!

This article will explain how I added support for the P5 Glove to Torque. I'm attempting to document the discovery process I went through to add the feature. I hope this will help you learn strategies to learn things about a large piece of new code. If you don't care about that, and just want to get the glove supported, look at the CODE CHANGE: blocks. Also, it's a good introduction to the input system, so you could use this as a guide to add new device support to Torque.

When approaching a new system, you need to figure out ways to discover how it works. I'm a big fan of using the debugger. I'll set some breakpoints then look at the callstack to see how the program flows. Another thing to do is to think of something that behaves similiar to what you're trying to accomplish. We are going to use this method in this article. In this case, the mouse acts very simliar to the glove. So a good place to start learning is to look at how the mouse gets handled in Torque.

GET TO WORK!
I have a lot of experence with the Windows API, and I've played around with other windowing systems. They all share the idea of having events placed in a message queue that gets processed. Based on this experience, I took a guess that Torque behaved similiarly. It would have to take the events that it received in whatever runtime environment it was in and translate it into's own messages. The first place to poke around would be the platform layer!

I'm working with windows, so I do a find in files for "mouse" in the platformwin32 directory. If you do this, you'll see a bunch of results that look like it's using the DirectInput interface to use the mouse, and also some results in winWindow.cc. Which one gets used? In my case, it was the winWindow.cc. I checked this by running "echoInputState()" in the console. (TODO: Confirm this). I found this function in winDirectInput.cc. Which was found by the find-in-files. So, I started looking closer at winWindow.cc. If you do, you'll quickly run into a function called "CheckCursorPos". Bingo! You'll see it is injecting events into Torques message queue! We'll add our own CheckCursorPos() call right after that does essientially the same thing.
CODE CHANGE:

In engine/platformWin32/winWindow.cc

In the include block, add this include:

#include "winP5Device.h"

In Platform::process(), under CheckCursorPos() add the following:

   if(windowLocked && windowActive)
	P5Glove::Glove().CheckCursorPos();
Ok, we've got a hook to inject new messages into Torque. What's next? We need to know how to fill up that event. If you look at the CheckCursorPos code in winWindow.cc, you'll see the "InputEvent" structure that gets filled up with data, and added to the queue with the Game->postEvent call. If you just wanted the glove to emulate the mouse, you could stop here and just fill up the structure the same way. However, I wanted to add a full new device type in. If you look at event.deviceType, it gets set to MouseDeviceType. We need to find out where this is defined and add a GloveDeviceType to it. I did a search for MouseDeviceType in platformWin32 and ended up with no good hits. So I went up and did a serch in the engine directory and ended up with a good hit in engine/platform/events.h.
CODE CHANGE:

In engine/platform/events.h:

In add this to the enum InputDeviceTypes list:

/// Input device types
enum InputDeviceTypes
{
   UnknownDeviceType,
   MouseDeviceType,
   KeyboardDeviceType,
   JoystickDeviceType,
   GloveDeviceType
};

Ok, we can now add new messages into Torque's message queue. Isn't that exciting! woo! What happens to these messages though? Right now, they'll just disappear into the abyss. We need to add some method of responding to these events. Let's go back to looking at the mouse as an example. In order to map the mouse to a game event, you use the moveMap.bind script command. Let's find out how this works. I did a find in files in the Torque directory for ::bind. Why the double colon? This will filter out all of the glBindTexture calls, and just get us to the class that implements the bind command quickly. If you do this, you'll get a couple hits with Net::bind, and actionMap.cc.

Let's look at actionmap.cc, this file implements the logic that maps an input event (mouse, keyboard, glove!) to a script command. Let's just keep searching for "mouse". The first hit you see is getDeviceTypeAndInstance. This little function maps a string to fields in the InputEvent structure. You can see the compare for "mouse" and the deviceType = mouseDeviceType line. So let's add our glove additions in here!

CODE CHANGE:

In engine/sim/actionMap.cc:

In ActionMap::getDeviceTypeAndInstance, Add this block after the joystick block:

 

  } else if (dStrnicmp(pDeviceName, "glove", dStrlen("glove")) == 0) {
	  deviceType = GloveDeviceType;
	  offset = dStrlen("glove");

Let's continue searching for mouse. The next hit you get is "getDeviceName" which does basically the opposite. Add this block in:

CODE CHANGE In ActionMap::getDeviceName, add this block after the joystick block:

 case GloveDeviceType:
	  dSprintf(buffer, 16, "glove%d", deviceInstance);
	  break;
I stopped at this point. I reasoned that the functions to convert the string to the structure and back where done, and that should be all it needed to begin working! I compiled and waved my arm around and nothing got triggered.

So I did a bit of debugging and found out that ActionMap::processAction was the function that actually processed input events. I noticed that the inputEvents I was creating in winWindow.cc where making it here. So at least I was that far! If I was a bit smarter, I could have just continued to search for "mouse", if you do that you'll see a nice little conditional statement here:
CODE CHANGE:

Finally in ActionMap::processAction, find the following lines:

   } else if (pEvent->action == SI_MOVE) {
      if (pEvent->deviceType == MouseDeviceType) {
         const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst,
                                      pEvent->modifier,   pEvent->objType);

I just added another compare to allow this code to run for mouse or glove.

Change the if statement to this:

      if ((pEvent->deviceType == MouseDeviceType) || (pEvent->deviceType == GloveDeviceType)) {
Finally, waving my hand around caused my script triggers to be called, and I could continue working on my project!

To use the code included, download this file. Then add the winP5Device.cc file to your Torque project. Put both the cc, and the h file into your engine/platformWin32 directory. Then, unzip the P5 SDK into the lib/p5 directory. Finally, add the lib to your Torque project. Compile for 2 minutes and let it cool before serving to guests.

Hope that helps someone out there! Please contact me and let me know how I can improve this article!

Brian Richardson

skinny at knowhere.net
Page «Previous 1 2
#1
12/14/2004 (11:09 pm)
Cool resource, Brian.
#2
12/16/2004 (4:07 pm)
Where is a good place to find the P5 Glove
#3
12/16/2004 (5:42 pm)
I didn't need to shop for mine, but a couple of people mentioned sources on my last .plan
#4
02/08/2005 (1:34 pm)
Thanks for the resource. I am adding a motion tracker as an input device and this was a good starting point to look at. I found that I had to do one more step not explicitly called out in your resource. You need to add the following to
default.binds.cs

moveMap.bind( glove, xaxis, yaw );
moveMap.bind( glove, yaxis, pitch );

and remove the mouse's mapping to those commands
//moveMap.bind( mouse, xaxis, yaw );
//moveMap.bind( mouse, yaxis, pitch );


and delete the config.cs and config.cs.dso files to make it take effect next time you start the game.
It seems if I leave the mouse's mapping in there it never takes the glove's mapping.
#5
02/08/2005 (1:38 pm)
Is it possible to bind both mouse and glove (and keyboard) at the same time?
#6
02/09/2005 (10:19 am)
@Michael:

Ahh, thanks for pointing that out!

@Desmond:

You can bind both at the same time. When I was testing, I had the glove, keyboard, and mouse all bound to commands.
#7
04/06/2005 (5:43 am)
#8
04/07/2005 (6:52 am)
@Juan,

If you look at the P5Glove::Glove().CheckCursorPos(); code, you can see where it gets data from the glove and feeds it into the input system. Hopefully, your serial port library/code can buffer the input so you don't have to make it multithreaded. ;) If not, you'll have to write a thread to read the port data, and figure out a safe communication mechanism to feed data into the event system.
#9
04/19/2005 (1:48 am)
#10
11/14/2005 (6:48 pm)
I keep getting the following errors on compile:
c:\Torque\SDK\engine\platformWin32\winP5Device.cc(62): error C2065: 'fFilterY' : undeclared identifier
c:\Torque\SDK\engine\platformWin32\winP5Device.cc(57): error C2065: 'fFilterX' : undeclared identifier

I checked my code and the link settings for my project and keeps doing this? Could you provide a list of files you have in the P5 directory? Also does this work in multipler games?

The P5 Glove is setup already to go for testing and I am getting this. It is as if it is not seeing the indentifiers in the P5Motion.h file, but I have them in my P5 directory. I must be missing something simple.
#11
11/15/2005 (11:40 am)
@Mark,

Yup it works in multiplayer mode.

I almost remember exposing the fFilterX/Y variables out so that I could use them, but I'm not positive. It's been almost a year since I touched this. I'll try to check it out when I get home.
#12
08/24/2006 (3:44 am)
I know it's been a long time, but -- is there a solution to Mark's problem?
#13
08/25/2006 (6:53 am)
I was having the same problem as Mark and Itay. I managed to get everything up and running. Here's what I did:

1) I created the ../lib/p5/ folder
2) I copied P5Bend.cpp, P5Bend.h, P5DLL.lib, P5Motion.cpp, p5motion.h into the P5 Folder
3) Under C++/General in the project properties I added ../lib/p5 to the Additional Includes
4) Under Linker/General in the project properties I added ../lib/p5 to the Additional Lib. Directories
5) Under Linker/Input in the project properties I added ../lib/p5/P5DLL.lib to the Additional Dependencies
6) I added the files from Step 2 to platformWin32 folder in the solution
7) The following are my code changes:

CODE CHANGE:

In engine/platformWin32/winP5Device.cpp:

In P5Glove::CheckCursorPos(), added missing variable type

const nBendThresh = 55;

[b]TO[/b]

const int nBendThresh = 55;

CODE CHANGE:

In engine/platformWin32/p5motion.cpp:

In P5Glove::CheckCursorPos(), under extern float fRelYawPos, fRelPitchPos, fRelRollPos;  ADD

[b]extern float fFilterX, fFilterY;[/b]


After making the script changes Harrington mentioned, I had full mouse control using the P5 Glove!
Hope this helped.
#14
08/25/2006 (1:06 pm)
Thanx Michael.

I'll give it a try...
#15
09/13/2006 (5:42 am)
I know the following code change isn't brilliant or hard to figure out on your own, but I found this very useful after going through the P5 SDK. The function, SetMouseState(int nDeviceID, bool bState) tells the P5 to act as the system mouse, which is a quick solution to giving you P5 support in menus without making a lot of changes.

I decided to expose the function to Torque script by using DInputManager

CODE CHANGE:

In engine/platformWin32/winDirectInput.h:

Declare this variable:

[b]bool mP5MouseActive;[/b]

Declare these functions:

[b]bool enableP5Mouse();[/b]

[b]bool disableP5Mouse();[/b]

Next came defining the functions for DInputManager

CODE CHANGE:

In engine/platformWin32/winDirectInput.cpp:

Define anywhere you would like:

[b]
bool DInputManager::enableP5Mouse()
{
   
   mP5MouseActive = P5Glove::Glove().SetMouseState(0, TRUE);
#ifdef LOG_INPUT
   Input::log( mP5MouseActive ? "P5 Mouse activated.\n" : "P5 Mouse failed to activate!\n" );
#endif
   return( mP5MouseActive );
}

bool DInputManager::disableP5Mouse()
{
   
   mP5MouseActive = P5Glove::Glove().SetMouseState(0, FALSE);
#ifdef LOG_INPUT
   Input::log( mP5MouseActive ? "P5 Mouse activated.\n" : "P5 Mouse failed to activate!\n" );
#endif
   return( mP5MouseActive );
}
[/b]

Finally, I created Console functions so you can enable/disable the P5 mouse in script.

CODE CHANGE:

Near the other Console functions (enableMouse, disableMouse, ect.):

[b]
ConsoleFunction( enableP5Mouse, bool, 1, 1, "enableP5Mouse()" )
{
   argc; argv;
   return( DInputManager::enableP5Mouse() );
}

ConsoleFunction( disableP5Mouse, bool, 1, 1, "disableP5Mouse()" )
{
   argc; argv;
   return( DInputManager::disableP5Mouse() );
}
[/b]

So once you are in script, you can call enableP5Mouse() and disableP5Mouse. The P5 will take control of the system mouse, which is great for GUI driven games or menus.
#16
11/14/2006 (10:28 pm)
Hi All,
I am a newbie to both TGE and the P5 glove. I want to thank you all for your detailed docs it has helped me pick torque up much faster.

I was wondering if anyone could expound on how to implement the P5 SDK. I got lost in Michaels instructions.

-----
1) I created the ../lib/p5/ folder
2) I copied P5Bend.cpp, P5Bend.h, P5DLL.lib, P5Motion.cpp, p5motion.h into the P5 Folder
3) Under C++/General in the project properties I added ../lib/p5 to the Additional Includes
4) Under Linker/General in the project properties I added ../lib/p5 to the Additional Lib. Directories
5) Under Linker/Input in the project properties I added ../lib/p5/P5DLL.lib to the Additional Dependencies
6) I added the files from Step 2 to platformWin32 folder in the solution
7) The following are my code changes:
---------

Please point me to a step by step I am really green with all this.

Any help is much appreciated.


Respectfully,
Irura
#17
11/15/2006 (4:24 am)
Hey Irura! Sorry if my directions were not newbie friendly, I can simplify it a little further if you need. The steps I listed were all specific to adding and linking the necessary files and libraries to a MS Visual Studio compiler.

Are you lost on code changes, getting the code to compile, or how to setup the solution changes?
#18
11/15/2006 (10:48 pm)
Hi Michael,

Thank you for the quick response. I managed to put in the code changes according to Brians post, I think the problem am having is with linking the files and libraries and getting it to compile with a MS Visual Compiler and ofcourse the latter setting up the solution changes.

Any chance you could help me out with that? Did you run into any issues compiling that i should look out for?

Respectfully,
Irura
#19
11/16/2006 (9:16 am)
I did hit a couple of snags with the linking, but it was mostly due to user error. Do you know how to change linker settings and "include directories" with a VS compiler? That's how I performed steps 3 - 6.

I right clicked on the project and selected properties. In the dialog that pops up, you'll see a list of settings to the left. The ones that say C++ and Linker contain the settings you need to change.

For Step 1: I created a folder in the SDK\lib\ folder called P5

For Step 2: I got all the files from this resource and the P5 SDK. I put P5dll.lib in the new lib folder I created, and the rest in engine\platformWin32\

For step 3: Go to C/C++->General->Additional Include Directories

For Step 4 & 5: Go to Linker->General->Additional Library Directories

For Step 6: See step 2


Once you have those settings and the all your files copied to the directories you create, everything should work fine.

If you did all this, what linker errors are you getting?
#20
11/17/2006 (8:02 am)
Thanks Michael.

I am going to try this today. Fingers crossed! I will write you again and let you know how it went.

Thanks again!

Irura
Page «Previous 1 2