|
Sensors© Justin Couch 2000Sensors are the other half of the custom input device handling. A sensor represents a single element on your input device that may produce information. Within a device, the sensors are not required to produce the same amount of information. You will notice that in the above code examples, we neglected to mention how to get information out from the input device to the runtime. If you look at a standard joystick, there are two degrees of freedom - forwards/backwards and left/right. There are also usually at least two buttons. This corresponds to a single sensor - the combination of one item that can have up to 6DOF and some buttons. On a high end joystick, you may have two or more sensors. The hat on the joystick will usually provide a second sensor as a set of 6DOF input. The hat may be coded to produce pure translation information (slides) where the main stick will produce roll and pitch changes (orientation). You may choose to use these seperately as two sensors, or a single combined sensor, depending on your device driver. Where you choose to associate the buttons is a matter or personal preference. Note that although they are physically the same device input system, one has more sensors than the other.
In our example system, we have only one sensor for that device. When we
construct the input device, we also need to construct all of the sensors that
it generates. Typically this is done at the construction stage, but may be
generated on demand. Each sensor instance is given a reference to the
Typically, the sensors are created during the initialize method call that we use to create the rest of the device. In this case we only have a single sensor so we can hard code the rest of the supporting methods:
private static final int NUM_SENSOR_READS = 5;
private Sensor sensor;
public boolean initialize()
{
sensor = new Sensor(this, NUM_SENSOR_READS);
// etc
}
public int getSensorCount()
{
return 1;
}
public Sensor getSensor(int sensorIndex)
{
return sensor;
}
Sensor instances for a particular InputDevice should not change
with time. Once you have a sensor representing a particular element in the
physical device, it should never be replaced with a new instance. This can
cause all sorts of strange behaviour within the application and environment.
A sensor is fixed, particularly when it comes to reading the information from
the sensor to use within your application.
Reading information from the Sensor does not provide the transformation
matrix directly. Instead we have a third class called
When the sensor is used to track head or hand information, sometimes some
smoothing of motions needs to be taken into account. A head tracker may only
generate 10 updates a second, but we are generating 30 frames a second. For
these cases, the in between values can be reasonably precisely predicted
using standard linear interpolation. If you know that the sensor is reading
head or hand input information you may wish to set the prediction policy on
the sensor. Setting this policy to either
To deal with prediction, the
Generating Sensor InformationIn the Java 3D view of the world, the sensors don't actually do anything. That is, you don't need to extend the implementation of theSensor
class as everything is provided. Your input device is told to read the latest
information and place that in the Sensor object. When the application wishes
to use sensor information, then it gets the latest information from that
Sensor and applies it in the way that you have coded (for
example, to modify a transform of a TransformGroup in the scene
graph).
Generating new sensor information should only be done in response to a call
to the In our example device, we use the basic parameterized version of a line between two points (for one component)
x(t) = x0 + t(x0 - x1)A whole bunch of pre-calculation has been done, but to show what happens next, this is the code. x_diff is the last part of the equation
in parantheses.
private Transform3D position_tx = new Transform3D();
private Vector3f translation = new Vector3f();
public void pollAndProcessInput()
{
...
translation.x = x0[leg_num] + delta_t * x_diff[leg_num];
translation.y = y0[leg_num] + delta_t * y_diff[leg_num];
translation.z = z0[leg_num] + delta_t * z_diff[leg_num];
position_tx.setTranslation(translation);
sensor.setNextSensorRead(current_time,
position_tx,
EMPTY_BUTTONS);
}
To minimize the amount of garbage collection, you should always avoid
constructing new objects wherever possible. It is better that you keep the
current sensor read instance and set new information than creating a whole
collection of new instances. However, you must bear in mind that the
Sensor object keeps track of the last n inputs and that may be
used by the application, which could cause problems if you always reuse the
SensorRead instance. The EMPTY_BUTTONS variable is
a zero length array of ints to indicate that there are no buttons for this
device.
Reacting to Sensor InputThere are various ways that you might want to react to sensor input. If you know that the application is using a joystick, then you may want to read sensor data every single frame. Other times you may only want something to happen when the sensor enters a particular volume of space. In either case, it requires the use of behaviours to trigger your application code to read information from the sensors and apply it to the scene graph.
Using our example application again, we will use two input devices. One will
control the user's position, the other will control an object in the scene.
For the smoothest possible movement, we want to act on every frame. This
means that we should create a
public class SensorBehavior extends Behavior
{
private Sensor sensor;
private TransformGroup tx_group;
private Transform3D tx;
private int elapsed_frame_delay;
private WakeupCondition criteria;
public SensorBehavior(Sensor sensor, TransformGroup tg)
{
this(sensor, tg, 0);
}
public SensorBehavior(Sensor sensor,
TransformGroup tg,
int elapsedFrames)
{
if(!tg.getCapability(TransformGroup.ALLOW_TRANSFORM_WRITE))
throw new IllegalStateException("Cannot write to transform");
this.sensor = sensor;
tx_group = tg;
elapsed_frame_delay = elapsedFrames;
tx = new Transform3D();
}
public void initialize()
{
criteria = new WakeupOnElapsedFrames(elapsed_frame_delay);
wakeupOn(criteria);
}
public void processStimulus(Enumeration crits)
{
sensor.getRead(tx);
tx_group.setTransform(tx);
wakeupOn(criteria);
}
}
As we have done with the other examples, we always create a single instance
of objects and reuse them wherever possible. Note also that this behaviour is
like our mouse behavior where we have to re-register the wake up criteria for
each call to processStimulus().
An alternate to this is to only create behaviors that change when the sensor
enters an area. Using the |
|
[ j3d.org ]
[ Aviatrix3D ]
[ Code Repository ]
[ Java3D ]
[ OpenGL ]
[ Books ]
[ Contact Us ]
Hosted by Yumetech Last Updated: $Date: 2006/04/18 18:20:20 $ |