I was finally successful in writing a custom CHOP to accept real-time input from a Joystick.
I spent a fair amount of time mulling this over, writing code, discarding code, re-writing code, etc. until I finally did it right. The biggest challenge I faced was deciding what bloody parameters I needed in the CHOP. So I did some research on libararies I could use to input the Joystick data: that would, in turn, give me a good foundation on how I would design the CHOP.
I looked briefly at the DirectX APIs, but quickly decided that since Houdini was a cross-platform application I needed a cross-platform solution. I found the Simple DirectMedia Layer, or SDL at http://www.libsdl.org - an open source, cross platform media layer for doing cross platform games on Windows, Linux, Mac, etc.
Note that to make SDL work you either have to compile it yourself and install it on your computer, or you can pick up the pre-built binaries that exist on the SDL web site. I elected to just use the pre-built libraries. I copied the runtime DLL into my windows PATH (I confess, I just put it in c:/windows), and I put the .lib file as well as the SDL subdirectory containing the SDL header files in the directory where I was building the CHOP source code.
I was then able to use the following command line to build my CHOP:
hcustom -l SDL.lib -I ./SDL CHOP_Joystick.C
I'll also mention that as a starting point, I used the Houdini sample code CHOP_FifthGlove.h and CHOP_FifthGlove.C as reference for how to go about building the CHOP. In essence, this Joytick CHOP was going to poll the device values when requested to by the Houdini system, convert its values to "normalized" values and insert the values into the CHOP channels.
The SDL libraries provided APIs for accessing the Joystick, and after a bit of reading I decided I wanted to keep it simple in my first attempt. So, while SDL provides support for Joystick Axes, Buttons, Hats and Trackwheels, I decided to only support Axes and Buttons.
Initially I wanted to write a single CHOP that could read every Axis and every Button on a single Joystick, but I quickly realized that this was going to be very tricky... at least for me. I wasn't familiar with the fine details of CHOP programming and I didn't have access to deep API documentation either, so I was relying strictly on the sample CHOP programs provided by Houdini. So the concession I made was that my Joystick CHOP would output one single channel, as described by the CHOP. If the animator configured the CHOP to watch the X-axis, it would spit out the value of the X-axis. If the animator configure the CHOP to watch the trigger button, it would spit out the value of the trigger button. To build a system that read from all the various axes and buttons on a Joystick, you would have to create multiple Joystick CHOP objects and merge their outputs together.
I also decided that the axis would have values from [-1, +1] and that buttons would have values of 0 (not pressed) or 1 (pressed).
The Joystick CHOP interface would have main parameters:
- Active (Boolean): When true, the Joystick CHOP would read from the device. When false, it would not read from the device.
- Joystick ID (int): This is the integer ID of the Joystick as detected by SDL. This is a little RAW, and I could have tried to have the CHOP detect the Joystick and even provide its name in the CHOP menu (e.g. "Logitech Extreme 3D Pro"), but that was getting a little complicated for my taste. I wanted to get something working first.
- Value Type (enum): This is one of "Axis" or "Button", indicating that this Joystick CHOP listens to an Axis input or a Button input.
- Value ID (int): This was the specific Axis or Button (depending on the value of Value Type) that the CHOP would listen to on the device.
After a few long evenings of re-learning C++ and playing with the various API calls in SDL to make it all work, it finally started compiling and working. Here's a screen shot of the Joystick CHOP in action:

And here's a snippet of source code from the CHOP itself - this is the actual call where we get the values from the Joystick hardware:
if (VALUE_TYPE() == JOYSTICK_TYPE_AXIS)
{
if (SDL_JoystickNumAxes(myJoystick) <= VALUE_ID())
{
cerr << "No joystick axis with index " << VALUE_ID()
<< " (max " << SDL_JoystickNumAxes(myJoystick)-1
<< ")" << endl;
return;
}
float axisValue = 0.0F;
axisValue = (float)SDL_JoystickGetAxis(myJoystick, VALUE_ID());
// cout << "axis = " << SDL_JoystickGetAxis(myJoystick, VALUE_ID()) << endl;
// cout << "joystick opened = " << SDL_JoystickOpened(0) << endl;
value = axisValue / -32767.5F;
}
else if (VALUE_TYPE() == JOYSTICK_TYPE_BUTTON)
{
if (SDL_JoystickNumButtons(myJoystick) < VALUE_ID())
{
cerr << "No joystick button with index " << VALUE_ID()
<< " ( max " << SDL_JoystickNumButtons(myJoystick)-1
<< ")" << endl;
return;
}
value = (float)SDL_JoystickGetButton(myJoystick, VALUE_ID());
}
else
{
cerr << "Unknown Joystick Value Type" << endl;
return;
}
If anybody is interested in getting a copy of the source code for the Joystick CHOP, let me know, and I'll gladly provide it.
Recent Comments