Two Viewer CAVElib

(aka Project Zaphod)

This document describes changes being made to the CAVE library interface to support multiple tracked users in a single ImmersaDesk or CAVE application. The test library and headers can be found in /usr/local/CAVE/zaphod/.

The current test library supports multiple viewers and multiple wands, as described below.

New functions

Although the existing CAVE library can support up to 32 tracked sensors and multiple input devices, it treats all the sensors (except the first) as interchangeable, and the input devices as a single collection of buttons and valuators. The new additions to the library API distinguish between tracked viewers (heads) and wands, and also associates button/valuator data with individual wands. The new functions are:
int CAVENumViewers(void)
Returns the total number of viewers that are being tracked; this can range from 1 to CAVE_MAX_VIEWERS (4). The viewers are numbered sequentially, from 0 to CAVENumViewers()-1 (the viewer numbers are used in arguments to other functions below).
int CAVENumWands(void)
Returns the total number of tracked wands; this can range from 0 to CAVE_MAX_SENSORS (32). The wands are numbered sequentially, from 0 to CAVENumWands()-1.
CAVE_SENSOR_ST * CAVEHeadSensor(int viewerNum)
Returns a pointer to the sensor struct for the given viewer. This pointer can be passed to CAVEGetSensorPosition(), CAVEGetSensorOrientation(), CAVEGetSensorVector(), and CAVESensorTransform(). If viewer #viewerNum does not exist, NULL is returned.
CAVE_SENSOR_ST * CAVEWandSensor(int wandNum)
Returns a pointer to the sensor struct for the given wand. This pointer can be passed to CAVEGetSensorPosition(), CAVEGetSensorOrientation(), CAVEGetSensorVector(), and CAVESensorTransform(). If wand #wandNum does not exist, NULL is returned.
CAVE_CONTROLLER_ST * CAVEWandControls(int wandNum)
Returns a pointer to the controller struct containing the button/valuator data which is associated with the given wand. The controller data can be read from the button[] and valuator[] entries of the struct. If wand #wandNum does not exist, NULL is returned.
void CAVEGetViewerEyePosition(int viewerNum,CAVEID eye,float *x,float *y,float *z)
Returns the position of the given viewer's left or right eye.
pfList * pfCAVEViewerChannels(int viewerNum)
Returns a pfList which contains pointers to all of the Performer channels which are rendering views for viewer #viewerNum.
int CAVEViewer
Global variable containing the number of the user whose view is currently being rendered. This is only meaningful within the display process.

"Obsolete" functions

Most of the standard CAVE library functions assume a single viewer and a single wand. To properly support multi-viewer mode, an application will have to use the functions listed above in place of the traditional functions. For example,
	CAVEGetPosition(CAVE_HEAD_NAV, pos);
would be changed to
	CAVEGetSensorPosition(CAVEHeadSensor(viewer), CAVE_NAV_FRAME, pos);
where viewer takes on appropriate values in the range 0 to CAVENumViewers()-1.

Similarly, a reference to CAVEBUTTON1 would be changed to CAVEWandControls(wand)->button[0].

The older library functions will still work, however, they will only report data for the first viewer and wand. The functions which fall into this category are:

Navigation

Currently, navigation is shared by all viewers. An option may be added in the future to support separate navigation matrices for each viewer.

Configuration

The new configuration file options are ChannelGeometry, HeadSensors, WandSensors, WandButtons, WandValuators, SensorOffset, SensorRotation, and SimulatorNumViewers.
ChannelGeometry <wall>,<eye>,<user> <pipe> <geometry>
ChannelGeometry replaces the WallDisplay option, allowing you to specify window geometry on a per-user, per-eye basis. A channel is any single view of the virtual world - a single (wall,eye,user) combination; <wall> is the name of any CAVE wall; <eye> can be "left" or "right", or "*" to configure both eye-views as the same; <user> can be any user number (0-3), or "*" to configure all user's views as the same. <pipe> and <geometry> are the same as for WallDisplay.
For example, the following configures a single pipe for two users, where their individual displays are side by side on the pipe:
   ChannelGeometry desk,*,0 :0.0 1024x768+0+768
   ChannelGeometry desk,*,1 :0.0 1024x768+0+0
   
HeadSensors <sensor-number> <sensor-number> ... Defines which tracker sensors are tracking the users' heads. <sensor-number> is a number from 0 to N-1, where N is the number of sensors being tracked, or -1. The first sensor will be user for the first user (i.e. its data is returned by CAVEHeadSensor(0)), the second for the second user, etc. If <sensor-number> is -1, that user's head is not tracked; instead, the default position and orientation data will be reported (that defined by DefaultTrackerPosition and DefaultTrackerOrientation).
WandSensors <sensor-number> <sensor-number> ... Defines which tracker sensors are used for the wands. This works the same as HeadSensors, indicating which sensor data will be returned by CAVEWandSensor(0), etc.
WandButtons <wand-number> <button-number> <button-number> ... Maps the "raw" controller data reported by the tracker process to buttons reported for the individual wands.
WandValuators <wand-number> <val-number> <val-number> ...
SensorOffset <sensor-number> <X> <Y> <Z> [<units>]
SensorRotation <sensor-number> <axis-X> <axis-Y> <axis-Z> <angle>
SimulatorNumViewers <num-viewers>

Typical settings for these options, for a DuoDesk, would be:

	ChannelGeometry desk,*,0 :0.0 1024x768+0+768
	ChannelGeometry desk,*,1 :0.0 1024x768+0+0

	HeadSensors 0 2
	WandSensors 1 3

        SensorOffset   0   3.5 0 -2.5 inches
        SensorRotation 0   0 0 1 -90
        SensorOffset   2   3.5 0 -2.5 inches
        SensorRotation 2   0 0 1 -90
        SensorOffset   1   0 3 -7 inches
        SensorRotation 1   1 0 0 -30
        SensorOffset   3   0 0 -7 inches
        SensorRotation 3   1 0 0 0

        WandButtons   0  0 1 2
        WandValuators 0  0 1
        WandButtons   1  3 4 5
        WandValuators 1  2 3

Simulator

To run a 2-viewer CAVE simulator, place the following in your .caverc:
	Simulator y
	SimulatorNumViewers 2
	ChannelGeometry simulator,*,0 - 512x512+0+0
	ChannelGeometry simulator,*,1 - 512x512+520+0
This will display the two views side by side, with the first user's display on the left. The simulator tracking and wand controls are the same as in the regular CAVE library, except that you can select one user or the other to control. The F1 key selects the first user, the F2 key selects the second user.


Last modified 3 April 1998.
Dave Pape, pape@evl.uic.edu