Contents

Chapters

Sections

j3d.org

[ Previous ] [ Up ] [ Next ]

InputDevices

© Justin Couch 2000

An input device consists of the lowest level interface with the data source. It processes the raw input and fills in the sensor information. An input device can provide information to the sensors in one of three fashions:

  1. BLOCKING. The device will hold up the processing routine while it extracts information from the physical device. This may require polling the device to get information.
  2. NON BLOCKING. If the device does not need to block while getting data. There is information constantly available and streamed in at regular intervals. It should be queried regularly by the runtime environment
  3. DEMAND DRIVEN. Data is always available, but is only presented to the runtime environment when it is specifically asked for by the application. Causes the least load on a runtime environment.

Generally speaking, you will need some sort of custom device driver for every input device and more often than not, that involves native code. For some environments, you may need to do no more than query the operating system specific driver capabilities. Under Win32, you may only need a thing layer to the DirectInput APIs to allow the use of a joystick. In other environments, you may need to access the serial/parallel ports directly. For these, either native code or the JavaCOMM interface (javax.comm) may provide the necessary APIs.

No matter what the source of the device information, you will need to implement the InputDevice interface. For this example, we've created a simple InputDevice that reads values from a file and is used to drive a single sensor. We'll call this class FileInputDevice.

  

Writing InputDevices

An input device deals with several different needs at once. On one hand it must take the input from the real piece of hardware (or software) and on the other, it must supply information on demand to the runtime environment. With the way that Java3D deals with devices, so long as you implement the InputDevice interface, it doesn't really care how you structure your code. In our simple example it is only one file, but for more complex cases you may need a whole separate package as well as native code.

Before we start on writing an actual device, we need to quickly look at how it gets added to the environment. This may appear a strange on the surface, but it will help explain a few of the things that we will do later. As we mentioned in the previous paragraph, so long as the interface is implemented, we can do what we like with the rest of the code. Also, back in Chapter 1 Getting Started, you saw that the PhysicalEnvironment has an addInputDevice() method. To get your input device registered with the system, you need to write the loading code. The addInputDevice() method takes an existing instance of an InputDevice and registers that with the runtime. That means in your code, you will need to write code to construct the device and register it with the runtime. This allows us to do whatever we like with the constructor and its parameters.

Now, getting back to the implementation of the device, there are three things that the code needs to achieve:

  1. Construct the class and any other required parts
  2. Read the input file and perform any preprocessing needed.
  3. Calculate values on demand for the runtime environment.

In the InputDevice definition you will notice that it contains an initialize() method. Like you would expect, this is called by the runtime environment to tell your code that it is time to initialize anything that it may need. That is, the construction of the class and the initialization are two separate steps. We'll take this cue and only use the constructor to build the bare essentials. The actual initialization, such as reading in the configuration file and preprocessing will be done in the second step.

Because we might have more than one instance of the device loaded into the application, the constructor takes an integer parameter to tell it what instance number it is:

private static final String FILE_NAME = "position_inputX.dat";
private String config_file;

public FileInputDevice(int deviceNumber)
{
    char[] index_char =
            Integer.toString(deviceNumber).toCharArray();

    config_file = FILE_NAME.replace('X', index_char[0]);
}
This just takes the standard pattern of the input file name and replaces X for the instance number. The obvious restriction is that there can be a maximum of only ten different devices (0-9).

At the point where Java 3D is ready to use your input device, it calls the initialize() method. In here we take the name of the input file that we created above, open it and parse the contents. For this exercise we are going to produce a simple linear position interpolator. You can read about the specifics in the code so we won't go into it here. Summarised, we make use of the static ClassLoader getSystemResourceAsStream to search for the input file in the classpath, open it and present a stream to us.

public boolean initialize()
{
    boolean init = false;

    try
    {
        InputStream is =
                ClassLoader.getSystemResourceAsStream(config_file);
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader reader = new BufferedReader(isr);

        StreamTokenizer strtok = new StreamTokenizer(reader);
        strtok.commentChar('#');
        strtok.eolIsSignificant(false);
        strtok.parseNumbers();

        ...

    }

    return init;
}
Note that the method returns a boolean that indicates if the process actually worked. If it returns false then Java 3D won't use that input device. Hence the reason for the init variable.

Last on the list of code to implement is processing the input from the device when it is needed. When the pollAndProcessInput method is called, it is Java 3D telling your class to grab a new lot of input from the real device and make it available to be read. You don't have to produce data at any other time if you don't want to. Because we are interpolating the data, we don't pre-calculate what the outputs may be. We generate information based on the current system time:

public void pollAndProcessInput()
{
    long current_time = System.currentTimeMillis();

    // do the processing here and setup values to be returned
}

That's all the input device needs to do. The next step is to implement the sensors because that is what Java 3D interacts with to get the real values.