Tutorial - Use VRPN

Introduction

VRPN (Virtual Reality Peripheral Network) is a free, open source, tool that handles many VR devices.

VRPN acts as a device server. You configure VRPN to use your devices, then your program can easily connect to the server to get the device data in a standardized way.

VRPN is used by a lot of VR applications: commercial ones, like Virtools, WorldViz, but also free applications like OpenSceneGraph, or Panda3D.

VRPN is cross-platform and runs on many different OS inclunding Windows, Linux, MacOS ...

VRPN has been released to the public domain by Russell M. Taylor II from the University of North Carolina at Chapel Hill, so a huge thanks to him, the university and all the community that contributed to improve the project.

The list of supported devices can be found on the VRPN home page.

Requirements

To complete this tutorial you need basic programming knowledge a working programming environment (Visual Studio, Eclipse, gcc, cygwin...).

Understanding VRPN

VRPN "converts" data from most devices to mostly three types : Tracker, Analog and Button.

The Tracker type holds a position and an orientation.

The Analog type is used for any type of axis : joystick axis, mouse axis ...

The Button type is used for any type of binary button : joystick button, mouse button ...

For example a mouse has a 2 channel Analog and a 3 channel Button.

A Wand, a typical VR device, has a Tracker, an Analog data for the its joystick, and Buttons.

A Wiimote is seen as a lot of channels in an Analog and also several channels in a Button.

Getting VRPN

You can get the latest VRPN package on the VRPN page.

Attached to the bottom of the page is a package for Windows (7.26) including binaries and libs with support for the Wiimote.

Configuring VRPN server

We will configure the VRPN server with a simple mouse. For any other device, the procedure is the same. You might also need to add some configuration parameters.

The first thing to do is to configure the VRPN server.

Locate the vrpn_server.exe file. In the same folder you should have the 'vrpn.cfg' file.

Edit this file and locate the line "# vrpn_Mouse Mouse0".

Remove the '#' and run vrpn_server.exe. The first word on the line is the driver type to use, the second word being the name you chose for this device and that you will use for the client connection.

The VRPN server is now running. We can use the vrpn_print_devices application to test the server.

Open a DOS shell, and run the vrpn_print_devices application with the argument : 'Mouse0@localhost'

'Mouse0' is the name you specified in the vrpn.cfg config file,'localhost' is the network address of the server.

After moving your mouse and clicking it a few times, you should see something like that :

Coding a VRPN client

A simple client

Attached at the end of the page is the sample project.

Here's the most simple code to get Analog values :

#include "vrpn_Analog.h"

#include <iostream>

using namespace std;

void VRPN_CALLBACK handle_analog( void* userData, const vrpn_ANALOGCB a )

{

int nbChannels = a.num_channel;

cout << "Analog : ";

for( int i=0; i < a.num_channel; i++ )

{

cout << a.channel[i] << " ";

}

cout << endl;

}

int main(int argc, char* argv[])

{

vrpn_Analog_Remote* vrpnAnalog = new vrpn_Analog_Remote("Mouse0@localhost");

vrpnAnalog->register_change_handler( 0, handle_analog );

while(1)

{

vrpnAnalog->mainloop();

}

return 0;

}

Make sure to add the vrpn include and lib dir, and link with vrpn.lib.

Here's the result :

Now some explanations :

vrpn_Analog_Remote* vrpnAnalog = new vrpn_Analog_Remote("Mouse0@localhost");

vrpn_Analog_Remote is a class that allows you to connect to a vrpn server that has a defined a driver with an Analog output. The constructor takes in the name of the device and the network address of the server.

vrpnAnalog->register_change_handler( 0, handle_analog );

Then we need to register a callback function, that will be called back (aha) anytime there's a new update coming from the vrpn server.

Here's the callback :

void VRPN_CALLBACK handle_analog( void* userData, const vrpn_ANALOGCB a )

{

int nbChannels = a.num_channel;

cout << "Analog : ";

for( int i=0; i < a.num_channel; i++ )

{

cout << a.channel[i] << " ";

}

cout << endl;

}

The callback has a vrpn_ANALOGCB parameter that contains the updated values in the 'channel' array. The number of channels of this Analog can be found with the num_channel member of the vrpn_ANALOGCB structure.

Now the only thing that we need is to make sure the vrpn_Analog_Remote object is properly updated :

while(1)

{

vrpnAnalog->mainloop();

}

Adding Buttons and Trackers

Buttons and Trackers work exactly the same :

#include "vrpn_Tracker.h"

#include "vrpn_Button.h"

#include "vrpn_Analog.h"

#include <iostream>

using namespace std;

void VRPN_CALLBACK handle_analog( void* userData, const vrpn_ANALOGCB a )

{

int nbChannels = a.num_channel;

cout << "Analog : ";

for( int i=0; i < a.num_channel; i++ )

{

cout << a.channel[i] << " ";

}

cout << endl;

}

void VRPN_CALLBACK handle_button( void* userData, const vrpn_BUTTONCB b )

{

cout << "Button '" << b.button << "': " << b.state << endl;

}

void VRPN_CALLBACK handle_tracker(void* userData, const vrpn_TRACKERCB t )

{

cout << "Tracker '" << t.sensor << "' : " << t.pos[0] << "," << t.pos[1] << "," << t.pos[2] << endl;

}

int main(int argc, char* argv[])

{

vrpn_Analog_Remote* vrpnAnalog = new vrpn_Analog_Remote("Mouse0@localhost");

vrpn_Button_Remote* vrpnButton = new vrpn_Button_Remote("Mouse0@localhost");

vrpn_Tracker_Remote* vrpnTracker = new vrpn_Tracker_Remote( "Tracker0@localhost");

vrpnAnalog->register_change_handler( 0, handle_analog );

vrpnButton->register_change_handler( 0, handle_button );

vrpnTracker->register_change_handler( 0, handle_tracker );

while(1)

{

vrpnAnalog->mainloop();

vrpnButton->mainloop();

vrpnTracker->mainloop();

}

return 0;

}

And the result with just a mouse :

And with a real tracker :

Wiimote

The VRPN server can be compiled with support for the Wiimote, using the WiiUse library. Attached to the bottom of the page is a winows package including a VRPN server with Wiimote support.

If you uncomment the "vrpn_WiiMote WiiMote0 0" line, the server should detect your Wiimote :

If you change the Analog_Remote and Button_Remote both to "WiiMote0@localhost", you should get an output like that :

To decode those values, refer to the vrpn_Wiimote.h file.

Roughly :

// channel[0] = battery level (0-1)

// channel[1] = gravity X vector calculation (1 = Earth gravity)

// channel[2] = gravity Y vector calculation (1 = Earth gravity)

// channel[3] = gravity Z vector calculation (1 = Earth gravity)

// channel[4] = X of first sensor spot (0-1023, -1 if not seen)

// channel[5] = Y of first sensor spot (0-767, -1 if not seen)

// channel[6] = size of first sensor spot (0-15, -1 if not seen)

// channel[7] = X of second sensor spot (0-1023, -1 if not seen)

// channel[9] = Y of second sensor spot (0-767, -1 if not seen)

// channel[9] = size of second sensor spot (0-15, -1 if not seen)

// channel[10] = X of third sensor spot (0-1023, -1 if not seen)

// channel[11] = Y of third sensor spot (0-767, -1 if not seen)

// channel[12] = size of third sensor spot (0-15, -1 if not seen)

// channel[13] = X of fourth sensor spot (0-1023, -1 if not seen)

// channel[14] = Y of fourth sensor spot (0-767, -1 if not seen)

// channel[15] = size of fourth sensor spot (0-15, -1 if not seen)

// and on the secondary controllers (skipping values to leave room for expansion)

// channel[16] = nunchuck gravity X vector

// channel[17] = nunchuck gravity Y vector

// channel[18] = nunchuck gravity Z vector

// channel[19] = nunchuck joystick angle

// channel[20] = nunchuck joystick magnitude

Go ahead !

That's it, you should now be able to use VRPN with most devices in your application !

Going further with VRPN

The VRPN server can be on another machine. You can have access to the devices plugged to another machine simply by specifying the network address of the vrpn_server when you create your remote devices.