Getting Input

There are many types of input devices that VR Juggler application objects can use including positional, digital, and analog. All application objects 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 proxies managed by gadget::DeviceInterface<T> instantiations. There are gadget::DeviceInterface<T> instantiations for each type of input data that Gadgeteer can handle. There is one for positional input, one for analog, and so on. In this section, we will only demonstrate the use of position and digital device interfaces. Refer to the section called “Device Proxies and Device Interfaces” for more detailed information on the use of all the available device interfaces.

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 vrj::App
{
public:
   init();
   preFrame();
private:
   gadget::PositionInterface mWand;
}

Note the declaration of the variable mWand of type gadget::PositionInterface. 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.

...
const float units = getDrawScaleFactor();
gmtl::Matrix44f wand_pos(mWand->getData(units));
...

The above code shows an example of using the positional 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 “Device Proxies and Device Interfaces” for more information about using device proxies and device interfaces.

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, “VR Juggler kernel control loop”.

Figure 4.1. VR Juggler kernel control loop

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 the Gadgeteer Input Manager 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 vrj::App::preFrame(), and

  2. Any time spent in vrj::App::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

In this section, we present a tutorial that demonstrates simple input handling using Gadgeteer device interfaces. The tutorial overview is as follows:

  • Description: Simple application that prints the location of the head and the wand.

  • Objective: Understand how to get positional and digital input in a VR Juggler application.

  • Member functions: vrj::App::init(), vrj::App::preFrame()

  • Directory: $VJ_BASE_DIR/share/samples/OGL/simple/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 vrj::GlApp
    {
    public:
       virtual void init();
  5    virtual void preFrame();
    
    public:
       gadget::PositionInterface mWand;     // Positional interface for Wand position
       gadget::PositionInterface mHead;     // Positional interface for Head position
 10    gadget::DigitalInterface  mButton0;  // Digital interface for button 0
       gadget::DigitalInterface  mButton1;  // Digital interface for button 1
    };

Initializing the Device Interfaces: vrj::App::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: vrj::App::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 --- //
       const float units = getDrawScaleFactor();
       gmtl::Matrix44f wand_matrix(mWand->getData(units));     (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.