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
Copyright © by Knowhere Studios All Right Reserved. Published on: 2004-12-13 (12 reads) [ Go Back ] |