This chapter will show you how to configure Syzygy to work with your input devices.

New in Syzygy 1.2: This section is now relevant to running programs in Standalone Mode as well as in Cluster Mode (see Cluster Mode); applications can now load input device drivers in Standalone Mode, provided the Syzygy libraries were built as dynamic-link libraries.

Input Device Parameters

As discussed in Syzygy System Configuration, a Syzygy parameter or 'dbatch' file contains two types of parameters, local (computer-specific) and global. Input devices typically have both. You define a global input device parameter that specifies which device driver module(s) to load and any filtering to be performed on the outputs. Then you define local input device parameters on the computer that the device is hooked up to that contain necessary configuration information for each driver module. Finally, you add an input map specification (another local parameter) to a virtual computer definition that makes the device definition a part of the virtual computer.

Global Input-device Parameters

With the exception of the PForth filter field, input device records are simpler than those for graphics configuration. Each contains four fields:

This sort of standardization is easy to achieve with a PForth filter.

Local Input-device Parameters

Many input devices require additional parameters. For example, devices that connect via serial interface may require e.g.:

Standalone Mode Local Parameter

When you want to load an input device in Standalone Mode, you must set the variable SZG_STANDALONE/input_config in the parameter file to the name of a global input device definition record. Note that currently if you specify an input device the Input Simulator is deactivated.

Virtual Computer Input-device Parameters

Finally, you add an input map specification to a virtual computer definition. This resembles a local parameter; it says that a particular global input device parameter will run on a particular computer in the context of the current virtual computer.

Input Event Streams

Input data in Syzygy comes in the form of a stream of events. There are currently three types of events. Matrix events, which usually represent the position and orientation (referred to in combination as the placement) of a tracking sensor. Axis events contain a floating-point number representing the state of a continuously-varying input. Button events contain an integer, usually representing a binary on-off input device. Within each class, events are identified by a 0-based index; in applications, a particular event type and index is generally associated with a particular function, for example matrix event #0 is generally assumed to represent the position and orientation of the sensor attached to the user's head. All three event types have a default value that will be returned whenever an application requests an event outside the range of existing indices; for matrices this is the identity matrix, for the other two types it is 0.

Transformation to Syzygy Coordinates

For a HowTo about getting your tracker data mapped in to Syzygy coordinates, see Tracker Coordinate Conversion

Troubleshooting

If your program isn't getting input, verify that DeviceServer is running on all computers connected to input devices:

  dps DeviceServer

lists all DeviceServers running on the cluster. If one is missing, check that computer's console for error messages.

If all DeviceServers are running and you're still not getting input events, run the DeviceClient diagnostic program as described in Syzygy Input Framework: Practical Examples.

Examples

As an example of an input device configuration record, here's the device definition for the wireless gamepad used in the Beckman Institute Cube. This gamepad has a 2-D joystick and 8 buttons. The device configuration record specifies the driver module to be loaded ('arJoystickDriver') and applies a PForth filter to re-scale the values returned by the joystick to the range [-1,1] (axis event #0 represents the left/right position of the joystick, while axis event #1 represents its front/back position):

<param>
  <name> cube_joystick </name>
  <value>
    <szg_device>
      <input_sources> arJoystickDriver </input_sources>
      <input_sinks></input_sinks>
      <input_filters></input_filters>
      <pforth>
        define filter_axis_0
          getCurrentEventAxis 0.000031 * setCurrentEventAxis
        enddef
        define filter_axis_1
          getCurrentEventAxis -0.000031 * setCurrentEventAxis
        enddef
      </pforth>
    </szg_device>
  </value>
</param>

Note that if you combine two input sources in a single record, and they both provide some of the same kinds of events (e.g. both provide button events), then the event indices of the second input source follow those of the first. For example, if in this example we followed arJoystickDriver with another device which had two buttons, then the original gamepad's buttons would still have indices 0-7 but the new one's buttons would be 8 and 9.

Configuration records for tracking devices are generally the most complex. Here's the device definition for the MotionStar Wireless tracker we use in the Cube. The PForth filter aligns the tracker coordinate system with the Cube's structure, uses a lookup-table procedure to correct the distortions in the tracker data, compensates for the different ways the two sensors are mounted on the LCD goggles and on the gamepad, and applies a simple IIR filter to the vertical component of the head position to suppress the rather excessive noise:

<param>
<name> cube_tracker </name>
<value>
  <szg_device>
    <input_sources> arMotionstarDriver </input_sources>
    <input_sinks></input_sinks>
    <input_filters></input_filters>
    <pforth>
      initTrackerCalibration
      initIIRFilter

      /* Define stuff to rotate & remap positional coordinates
         and rotate orientation by 135 degrees around Y */
      matrix yTransRotMatrix
      -45. yaxis yTransRotMatrix rotationMatrix

      vector oldLo
      vector oldHi
      vector newLo
      vector newHi
      1.2 -5. 1.2  oldLo vectorStore
      10.4 5. 10.4 oldHi vectorStore
      -5.  0. -5.  newLo vectorStore
      5.   10. 5.  newHi vectorStore

      vector oldRange
      vector newRange
      oldHi oldLo 3 oldRange arraySubtract
      newHi newLo 3 newRange arraySubtract

      vector newOldRatio
      newRange oldRange 3 newOldRatio arrayDivide

      vector position

      define rescale_position
        position oldLo 3 position arraySubtract
        position newOldRatio 3 position arrayMultiply
        position newLo 3 position arrayAdd
      enddef

      matrix yRotMatrix
      -135 yaxis yRotMatrix rotationMatrix

      matrix inputMatrix
      matrix rotMatrix
      matrix transMatrix

      /* Apply transformations that are common to all sensors */
      define filter_all_matrices
        inputMatrix getCurrentEventMatrix

        /* Extract translation, rotate it by 45 degrees about y */
        inputMatrix position extractTranslation
        yTransRotMatrix position position vectorTransform

        /* Rescale position coords */
        rescale_position

        /* Rotate rotation component about Y */
        inputMatrix rotMatrix extractRotationMatrix
        yRotMatrix rotMatrix rotMatrix matrixMultiply

        /* Recombine translational and rotational components */
        position transMatrix translationMatrixV
        transMatrix rotMatrix inputMatrix matrixMultiply

        /* Apply tracker-calibration lookup table */
        inputMatrix inputMatrix doTrackerCalibration

        inputMatrix setCurrentEventMatrix
      enddef

      /* Fix rotational matrix components
         (depends on how sensors are mounted) */
      matrix headZRotMatrix
      matrix wandZRotMatrix
      matrix wandTweakMatrix

      /* Head sensor is mounted sideways */
      -90 zaxis headZRotMatrix rotationMatrix

      /* Wand sensor is mounted upside-down */
      -180 zaxis wandZRotMatrix rotationMatrix

      /* Wand sensor points upwards by about 20 degrees
         with gamepad held subjectively level (actually
         tilted up slightly, but it feels right) */
      -20. xaxis wandTweakMatrix rotationMatrix

      matrix wandRotMatrix
      wandZRotMatrix wandTweakMatrix wandRotMatrix matrixMultiply

      /* Head-specific filter (applied after generic, above) */
      define filter_matrix_0
        inputMatrix getCurrentEventMatrix
        inputMatrix inputMatrix doIIRFilter
        inputMatrix headZRotMatrix inputMatrix matrixMultiply
        inputMatrix setCurrentEventMatrix
      enddef

      /* Hand-specific filter (applied after generic) */
      define filter_matrix_1
        inputMatrix getCurrentEventMatrix
        inputMatrix wandRotMatrix inputMatrix matrixMultiply
        inputMatrix setCurrentEventMatrix
      enddef

    </pforth>
  </szg_device>
</value>
</param>