Getting Input

There are many types of input devices that VR Juggler applications can use including positional, digital, and analog. All applications share the same processes and concepts for acquiring input from devices. The main thing to remember about getting input in applications is that all VR Juggler applications receive input through device handles managed by vjDeviceInterfaces. There are vjDeviceInterfaces for each type of input data that VR Juggler can handle. There is one for positional input, one for analog, and so on. They all have very similar interfaces and behave exactly the same. (Refer to the section called “The vjDeviceInterface Helper Class” and the section called “The vjProxy Helper Class” for more information.)

How to Get Input

While there has already been a brief presentation about getting input in an application, we need something more. Since all device interfaces look the same, we will focus on an example of getting positional input. All other types are very similar. We begin with a simple application object skeleton.

class myApp : public vjApp
{
public:
   init();
   preFrame();
private:
   vjPosInterface mWand;
}

Note the declaration of the variable mWand of type vjPosInterface. This is the first addition to an application. Device interfaces are usually member variables of the user application class, as in this example.

myApp::init()
{
   mWand.init("NameOfPosDevInConfiguration");
}

The device interface has to be told about the device from which it will get data. This is done by calling the device interface object's init() method with the symbolic string name of the device. This device name comes from the active configuration. We are now ready to read from the device.

...
vjMatrix wand_pos;
wand_pos = *(mWand->getData());
...

The above code shows an example of using the device interface in an application. It shows some sample code where the application copies the positional information from a device interface. When it is dereferenced, the device interface figures out what device it points to and returns the data from that device. Again, refer to the section called “The vjDeviceInterface Helper Class” for more information about using vjDeviceInterface.

Where to Get Input

In the previous section, we showed how to get input from devices, but we never said where to put the code. The location, surprisingly, is application dependent. There are some very good guidelines regarding where applications should process input. Before explaining them, however, we should review the VR Juggler kernel control loop, presented again in Figure 4.1.

Figure 4.1. VR Juggler kernel control loop

This diagram looks complicated, but the key here is the updateAllData() call near the bottom of the diagram. This is where VR Juggler updates all the cached device data that will be used in drawing the next frame. This updated copy is used by all user references to device data until the next update and the end of the next frame of execution.

This means two things:

  1. The device data is most fresh in vjApp::preFrame(), and

  2. Any time spent in vjApp::preFrame() increases the overall system latency.

The first point is important because it means that the copy of the device data with the lowest latency is always available in the preFrame() member function. The second point is equally important because it says why user applications should not waste any time in preFrame(). Any time spent in preFrame() increases system latency and in turn decreases the perceived quality of the environment. Hence, it is crucial to avoid placing computations in preFrame().

Tutorial: Getting Input

Table 4.1. Tutorial Overview

DescriptionSimple application that prints the location of the head and the wand
ObjectiveUnderstand how to get positional and digital input in a VR Juggler application
Member functions
  • vjApp::init()

  • vjApp::preFrame()

Directory$VJ_BASE_DIR/share/samples/tutorials/simpleInput
Files
  • simpleInput.h

  • simpleInput.cpp

Class Declaration and Data Members

In the following class declaration, note the data members (mWand, mHead, etc.). This application has four device interface member variables: two for positional input (mHead and mWand) and two for digital input (mButton0 and mButton1). Each of these member variables will act as a handle to a “real” device from which we will read data in preFrame().

  1 class simpleInput : public vjGlApp
    {
    public:
       virtual void init();
  5    virtual void preFrame();
    
    public:
       vjPosInterface     mWand;      // Positional interface for Wand position
       vjPosInterface     mHead;      // Positional interface for Head position
 10    vjDigitalInterface mButton0;   // Digital interface for button 0
       vjDigitalInterface mButton1;   // Digital interface for button 1
    };

Initializing the Device Interfaces: vjApp::init()

The devices are initialized in the init() member function of the application. For each device interface member variable, the application calls the variable's own init() method. The argument passed is the symbolic name of the configured device from which data will be read. From this point on in the application, the member variables are handles to the named device.

  1 virtual void init()
    {
       // Initialize devices
       mWand.init("VJWand");
  5    mHead.init("VJHead");
       mButton0.init("VJButton0");
       mButton1.init("VJButton1");
    }

Examining the Device Data: vjApp::preFrame()

The following member function implementation gives an example of how to examine the input data using the device interface member variables.

  1 virtual void preFrame()
    {
       if ( mButton0->getData() )                              (1)
       {
  5       std::cout << "Button 0 pressed" << std::endl;
       }
       if( mButton1->getData() )                               (1)
       {
          std::cout << "Button 1 pressed" << std::endl;
 10    }
     
       std::cout << "Wand Buttons:"                            (2)
                 << " 0:" << mButton0->getData()
                 << " 1:" << mButton1->getData()
 15              << std::endl;
                     
       // -- Get Wand matrix --- //
       vjMatrix wand_matrix;
       wand_matrix = *(mWand->getData());                      (3)
 20    std::cout << "Wand pos: \n" << wand_matrix << std::endl;
    }
1

These statements check the status of the two digital buttons and write out a line if the button has been pressed.

2

This writes out the current state of both buttons.

3

The final section prints out the current location of the wand in the VR environment.