CAVE User's Guide

CAVE Library version 2.5.6

June 23, 1996

Electronic Visualization Laboratory
University of Illinois at Chicago
851 S. Morgan St., Room 1120
Chicago, IL 60607-7053
(312) 996-3002
(312) 413-7585 fax
(c) 1996 Electronic Visualization Laboratory, University of Illinois at Chicago

written by Dave Pape, Carolina Cruz-Neira, Marek Czernuszenko



TABLE OF CONTENTS

1. Purpose of the CAVE User's Guide

2. CAVE Description

3. CAVE Equipment

3.1. Overall structure
3.2. Projectors and mirrors
3.3. Stereo glasses
3.4. Stereo emitters
3.5. Wand
3.6. Tracking systems
3.7. Audio system
3.8. Workstation

4. ImmersaDesk and Other Hardware

4.1. Immersadesk
4.2. Other Hardware

5. CAVE Programming

5.1. Introduction
5.2. Compiling a CAVE program
5.3. Display callbacks
5.4. Interaction
5.5. Navigation
5.6. Multiprocessing
5.7. Distributed CAVE
5.8. Networking
5.9. Form of a basic CAVE program

6. CAVE Library

6.1. Overview
6.2. Data types
6.3. Basic CAVE functions
6.4. CAVE macros, variables, and miscellaneous functions
6.5. Environment variables
6.6. Internal functions

7. CAVE Simulation

7.1. Introduction
7.2. Simulated tracking
7.3. Simulated wand controls
7.4. Simulated display

8. Supporting software

9. CAVE configuration files

10. Sample programs

10.1. CAVE sample program 1
10.2. CAVE sample program 2




1. Purpose of the CAVE User's Guide

This CAVE User's Guide contains all the information an application developer needs to successfully create a CAVE experience. We assume that the reader has a basic knowledge of the C programming language and is familiar with IrisGL or OpenGL.

I. Hardware

2. CAVE Description

The CAVE (CAVE Automatic Virtual Environment) is a projection-based VR system that surrounds the viewer with 4 screens. The screens are arranged in a cube made up of three rear-projection screens for walls and a down-projection screen for the floor; that is, a projector overhead points to a mirror, which reflects the images onto the floor. A viewer wears stereo shutter glasses and a six-degrees-of-freedom head-tracking device. As the viewer moves inside the CAVE, the correct stereoscopic perspective projections are calculated for each wall. A second sensor and buttons in a wand held by the viewer provide interaction with the virtual environment.

The current implementation of the CAVE uses three walls and the floor. The projected images are controlled by two SGI Onyxes with a total of four Reality Engine 2 graphics pipelines. For testing, you can run the CAVE using any number of walls simultaneously. The number of CAVE walls used does not affect your program. The CAVE library determines which walls you want to use and does the necessary setup when your program starts.

A diagram of the CAVE environment is shown in Figure 1.


Figure 1: CAVE System

3. CAVE Equipment

3.1. Overall structure

CAVE hardware should be configured by system and video engineers so it is usable. Under normal operation, CAVE users should only be concerned with turning on and off the different components (although, due to ongoing CAVE research, the hardware configuration could change occasionally, at which time you should be notified by your system administrator.) The following is a description of the CAVE equipment.

3.2. Projectors and mirrors

The projectors and the mirrors for the side walls are located behind each wall. The projector for the floor is suspended from the ceiling of the CAVE. The projectors are very sensitive to almost everything. It takes at least one hour to align and calibrate each projector and mirror to match at the corners of the CAVE. Please be careful not to move the projectors or mirrors if you have to walk in that area.

Never turn off the projectors; this can cause them to go out of alignment. Use the standby button on the remote control to activate them. Press and hold the standby button on the remote control for a couple of seconds. Do not touch any other control that might cause the projectors to go out of alignment.

Once you are done using the CAVE, remember to put all the projectors back in standby mode. The projector tubes have a limited life span that can be extended by putting them on standby when not in use.

3.3. Stereo glasses

To see the virtual environment in stereoscopic 3D, users wear Stereographics' CrystalEyes liquid crystal shutter glasses. The glasses are very fragile. The glasses must be turned on by pressing a small button located on the right side of the frame. To turn them off, press the same button. They will not work if the user is facing away from the emitters.

3.4. Stereo emitters

The stereo emitters are little white boxes placed around the edges of the CAVE. They are the devices that synchronize the stereo glasses to the screen update rate of 120Hz or 96Hz. They are always on. You should not have to do anything with them.

3.5. Wand

A wand (a 3D mouse) with buttons is the interactive input device. Currently, EVL has two wands; both wands use the Ascension Flock of Birds tracking system, but have different control devices. The primary wand has three buttons and a pressure-sensitive joystick. It is connected to the CAVE through a PC which is attached to one of the Onyx's serial ports. A server program on the PC reads data from the buttons and joystick and passes them to the Onyx. The older wand just has three buttons, and is attached to the mouse port of the Onyx. When using the older wand, be sure that the mouse pointer is on the main screen (the ":0.0" display) while the CAVE is running, or your program will not be able to detect the state of the wand buttons.

3.6. Tracking systems

The CAVE supports several different tracking systems. The primary system is an Ascension Technologies Flock of Birds. Alternative systems include the Polhemus Fastrak and the Logitech sonic tracker. There are also "simulated" tracking options available, using either the keyboard and mouse or a spaceball. The use of one or another is transparent to the CAVE programmer, since it is defined in the CAVE configuration file. Systems typically have two sensors, one for tracking the user's head, and another for the wand.

3.7. Audio system

The audio system components are: an Indy workstation, speakers, a MIDI interface, and synthesizer.

The Indy functions as a "sound server" for the CAVE. Commands are sent to the workstation over the network, and it then either generates sounds internally, or controls the synthesizer.

The speakers are located in the corners of the CAVE. They are always turned on. DO NOT TOUCH any of the controls on the speakers. Everything is controlled from the synthesizer.

The MIDI interface and synthesizer are located on a rack next to the CAVE.

3.8. Workstation

The current implementation of the CAVE runs using a Silicon Graphics Onyx with three Reality Engine 2s. Each Reality Engine is attached to a CAVE wall.

4. ImmersaDesk and Other Hardware

4.1 Immersadesk

The ImmersaDesk is a drafting-table format virtual prototyping device. The ImmersaDesk features a 4x5-foot rear-projected screen at a 45-degree angle. The size and position of the screen give a wide-angle view and the ability to look down as well as forward.

A diagram of the Immersadesk is shown in Figure 2.


Figure 2: Immersadesk Diagram

Except for the display device the same hardware is used as in the CAVE

4.2 Other Hardware

This page unintentionally left blank.

II. Software

5. CAVE Programming

5.1 Introduction

5.2 Compiling a CAVE program

A CAVE program should include the appropriate CAVE header file - either cave.h for IrisGL programs or cave_ogl.h for OpenGL programs. IrisGL programs will need to be linked with the CAVE library, the IrisGL library, and the math library (-lcave -lgl -lm). OpenGL programs will need to be linked with the OpenGL CAVE library, the OpenGL library, the math library, and the X libraries (-lcave_ogl -lGL -lX11 -lXi -lm). Programs using the C2C version of the CAVE library will also require the C2C and sphere libraries (-lc2c -lsphere); those using the BOOM3 version will require Fakespace's BOOM library (-lboom).

To use audio functions, include "vssClient.h" (after "cave.h") and link with the sound library (-lsnd).

All CAVE system files are normally found in /usr/local/CAVE; the headers in /usr/local/CAVE/include and the libraries in /usr/local/CAVE/lib.

5.3. Display callbacks

Graphics in a CAVE program are handled using callback functions which are called by the CAVE library's display loop for each view that must be rendered. This approach is taken so that the library can take care of all the necessary projections and synchronization.

The rendering for each wall of the CAVE is done by a separate process (see Multiprocessing below). When a stereo display is used, each process will call the application's display function twice per frame; in monoscopic mode, it is called once per frame. Because the application cannot know in advance how often the display function will be called, a "frame function" callback is provided. This callback will always be called exactly once per frame in each rendering process, before the display callback. The third type of callback that is available is the initialization callback. This function will be called exactly once, at the beginning of the next frame after it is defined. This can be used for any one-time display operations, such as defining materials and textures.

An application's display callback function is defined by passing a function pointer to CAVEDisplay. The frame function is defined with CAVEFrameFunction. The initialization callback is defined using CAVEInitApplication.

Your program does not have to perform any window or projection commands. The CAVE library does that for you, in order to produce the correct stereo perspective. The CAVE by default is set to RGB mode, double buffered, with z-buffering. Do not issue the swapbuffers command; it is handled internally by the CAVE library. You are responsible for any other graphics commands, such as lighting, object transformations, smoothing of lines, or clearing the screen. The standard CAVE is a 10-foot cube. The origin of the coordinate system (0, 0, 0) for the CAVE is normally located at the center of the floor, that is, 5 feet away from any wall. This means that you have from +5 to -5 feet horizontally and 0 to 10 feet vertically to define objects inside the CAVE. The exact location of the CAVE origin is defined in the configuration file by the "Origin" option. If you wish to change its location, you must change all the configuration settings that are given in CAVE coordinates (Origin, TransmitterPosition, and ProjectionCorners) to use the same new coordinate system.

All the walls of the CAVE share the same reference coordinate system, as shown in Figure 4. The coordinate system is a right-handed system. All locations and orientations returned by the trackers to the CAVE library will follow this convention.

By default, the near and far clipping planes of the CAVE are located at 0.1 and 100.0 feet. Those values can be changed by modifying the global variables CAVENear and CAVEFar.


Figure 4: CAVE Coordinates

5.4. Interaction

A user interacts with a CAVE application using the tracker and controller. The tracker reports the position and orientation of its sensors; the controller consists of a set of buttons which can be on or off, and valuators, such as the joystick, which report floating point values (normally between -1 and 1).

The states of the tracker and controller are stored in structures in shared memory. Several library functions are available to read this data, and to compute derived values, such as tracker vectors. These structures are updated between frames, once per display frame. Thus, the values will remain constant during a given frame.

5.4.1. Trackers

In the past, the library supported exactly two tracked sensors - one for the user's head, and one for the wand - and many of the functions reflect this. The latest library supports up to 8 sensors (depending on the tracking hardware). The sensor data is accessible through the global struct pointed to by CAVEptr; the number of sensors being read is CAVEptr->num_sensors, and the array CAVEptr->sensor contains pointers to the data for each sensor. The first sensor (CAVEptr->sensor[0]) is always the user's head; this is necessary for the proper display projections to be generated. The remaining sensors are the wand or whatever other objects are being tracked.

The functions for getting tracker-related values are:

CAVEGetPosition, CAVEGetOrientation, and CAVEGetVector return values for either the head or the wand (the first two sensors), based on the id argument. CAVEGetSensorPosition, CAVEGetSensorOrientation, and CAVEGetSensorVector return values for the sensor pointed to by the sensor argument. These functions can return values either in the coordinate system of the physical CAVE (that used by the trackers), or in the application's world coordinate system (as defined by the navigation).

5.4.2. Controller

The status of the buttons and/or valuators on the wand (or other control device) are stored in the global struct pointed to by CAVEController. The typical CAVE wand has three or four buttons and a joystick (which consists of two valuators - the X & Y position); there are macros for getting these values. The macros are CAVEBUTTON1, CAVEBUTTON2, CAVEBUTTON3, CAVEBUTTON4, CAVE_JOYSTICK_X, and CAVE_JOYSTICK_Y. Note that, for historical reasons, the macros number the buttons starting from 1, rather than 0, so CAVEBUTTON1 corresponds to CAVEController->button[0], etc.

The values in CAVEController reflect the current state of the buttons; the function CAVEButtonChange can be used to find out how a button state has changed since the last time it was checked. The argument for CAVEButtonChange uses the same numbering as the CAVEBUTTON macros.

5.5. Navigation

The CAVE structure and tracking hardware generally limit a user's movements to a 10 foot square area or smaller. This is not enough space for many applications, so it is necessary to introduce a navigation coordinate transformation which can move the CAVE's physical coordinate system around in the virtual space. The CAVE library maintains a navigation transformation matrix which is controlled by various functions, and provides conversions between the tracker (physical) and world (navigated) coordinate systems.

The basic functions for navigation are CAVENavTranslate, CAVENavRot, and CAVENavScale. These functions are equivalent to the corresponding IrisGL functions. The transformations are all defined in the physical CAVE coordinate system; i.e. a CAVENavTranslate(0.0,0.0,-1.0) will move the CAVE through the virtual world one unit in the direction of the front wall, and a CAVENavRot(30.0,'y') will turn the CAVE by 30 degrees to the left. The other functions for controlling the matrix are CAVENavLoadIdentity, CAVENavLoadMatrix, CAVENavMultMatrix and CAVENavGetMatrix.

When the CAVE library calls an application's display function, the default coordinate system is the physical coordinates. To use the navigated coordinates, the application should call CAVENavTransform.

It is sometimes necessary to convert values between the physical and navigated coordinate systems. The function CAVENavConvertCAVEToWorld will take a position in physical coordinates and transform it into navigated coordinates; CAVENavConvertWorldToCAVE will perform the reverse transformation. CAVENavConvertVectorCAVEToWorld and CAVENavConvertVectorWorldToCAVE will perform the same transformations for direction vectors instead of positions.

The library stores the navigation matrix in shared memory, so these functions can be called from any process.

5.6. Multiprocessing

The CAVE library splits an application up into several processes to handle the different tasks involved in running the CAVE. The basic flow of a CAVE program is shown in Figure 5.

Figure 5: Program flow

The different child processes are all forked by CAVEInit. Only the parent application process will return from CAVEInit; the others all start internal library functions. There is one display process per active wall, one process for tracking (if tracking is enabled), and one process for networking (if networking is enabled). The display processes call application-provided callback functions; the FrameFunction callback is called once per frame, and the Display callback is called either once or twice, depending on whether the CAVE is running in stereo.

To maintain acceptable frame rates, the display processes should only perform rendering. All computations should be done in parallel in the main application process. The application and display processes have to use shared memory for any common data that must be exchanged between them. The functions CAVEMalloc and CAVEFree can be used to allocate and release shared memory, in the same manner as malloc and free. The amount of shared memory that can be allocated depends on the size of the library's arena, which can set using CAVESetOption; this must be done before calling CAVEConfigure, as that is where the arena is created. Alternatively, CAVEUserSharedMemory can be called to create a shared memory arena, which can then be used with amalloc (part of SGI's standard libraries) to allocate memory from that arena. For a shared arena and any global shared pointers to be visible to all the processes, they should be allocated before CAVEInit is called. Shared memory allocated from the arena after calling CAVEInit is visible to all processes, but the pointer will need to be passed to any processes other than the allocator.

Following this parallelized approach, it may be necessary to guarantee that the computation process does not modify shared data while it is being used by the display processes. There are (at least) two methods of dealing with this problem. One method is to use locks or semaphores to limit simultaneous access to shared data. The CAVE library includes convenience routings (CAVENewLock, CAVEFreeLock, CAVESetReadLock, CAVEUnsetReadLock, CAVESetWriteLock, CAVEUnsetWriteLock) which provide two-level access control. If the display processes only read the shared data, setting a read lock will allow any number of them to access the data at the same time, while a write lock will allow exactly one process to change the data at a time.

The other method of controlling shared memory is to double-buffer the data. In this case, the display processes have a pointer to one copy of the data which will not change while they are rendering, and the computation process makes changes to a separate copy of the data. When the computation process finishes updating the shared data, it can swap buffers with the display processes by calling CAVEDisplay with the pointer to the updated data as an argument for the callback function. CAVEDisplay blocks until the end of the current frame; this guarantees that the data for the display processes will only change between frames.

As noted above, the library will fork a separate process for each of the active CAVE walls. This means that the application's display function may be called several times in parallel; the exact number of processes varies depending on the CAVE configuration. In some cases, the display function will perform an action that should only be done by one process (such as diagnostic output or audio); for this, the function CAVEMasterDisplay returns true for exactly one display process. Also, it is sometimes necessary to synchronize the display processes, when shared memory is being modified; the function CAVEDisplayBarrier will cause the processes to block until all of them have called it. When using CAVEDisplayBarrier, be sure that all the display processes will call it.

5.7. Distributed CAVE

Current SGI hardware will only allow up to three graphics pipes in a single system. For a CAVE to have four walls, each with its own pipe, two Onyxes must be used; this is referred to as a "distributed CAVE". In a distributed CAVE, a separate copy of an application must be run on each Onyx, and these systems need to be tightly synchronized and to share data. The library includes functions to automatically share its own data (tracking and navigation) and to synchronize the graphics displays. Fast, low latency communications are needed, and so the distribution uses a Hippi network or Scramnet reflective memory. Basic functions are also provided for sharing application data between the two machines.

To run a distributed CAVE application, the configuration options "Distribution" and "AppDistribution" must be set appropriately (to Scramnet, Hippi, or TCP). The "DistribID", and hardware related options ("DistribHippiULP", "ScramnetDevices", etc.) must also be set; these are normally defined in the system configuration file. One machine is considered the master node - its DistribID is 0; the other machine, with DistribID 1, is the slave. The master node must be the system which does the tracking. An application should be started on the master node first, and then on the slave node. This is mostly important when using Hippi, as the master's display process must be ready to receive a message from the slave before the slave sends it, or the two nodes will not be able to synchronize. When distribution is enabled, the tracker data, controller state, navigation matrix, and CAVE time are shared between the two machines; the master node sends the latest values to the slave node at the beginning of each frame.

If an application uses shared memory to communicate data between a computation process and the display processes, this data will probably need to be shared between the distributed CAVE nodes to guarantee that all the displays use the same data. This can be done using standard networking libraries (RPC, PVM, etc.) or with the CAVE library. To use the CAVE library functions for distributing application data, the "AppDistribution" configuration option must be set; it does not need to be the same as "Distribution". The functions CAVEDistribSend and CAVEDistribReceive can then be used to send and receive data between the two nodes; CAVEDistribMaster can be used to have only the master node perform computations. CAVEDistribBarrier synchronizes processes on the two nodes. When AppDistribution is disabled, these functions will all return immediately without doing anything (CAVEDistribMaster will always return true in this case). CAVEDistribConnect must be called before CAVEDistribSend, CAVEDistribReceive, or CAVEDistribBarrier are called; all of these functions should only be called from one process, as they use a common communication channel. CAVEDistribClose should be called when the application exits, to close the communications channel.

5.8. Networking

The library includes an option for networking of CAVE applications, whereby several users on different machines can share a virtual environment. If networking is enabled (via the configuration), a separate process will be started to handle it. This process automatically broadcasts the local user's tracking and controller data to all other CAVEs in its group at regular intervals. It also receives this data from the remote CAVEs and stores it in shared memory in a list of the users who are currently in the shared environment. Application-specific data can be passed to the networking process in order for it to be broadcast to the other CAVEs.

To use the multicast networking with an application, a CAVE configuration such as the following is needed:

	Network          mcast
	NetworkAddress   224.2.242.117
	NetworkPort      5302
	NetworkAppPort   5303
	NetworkTTL       4
	NetworkUpdateInterval   .05
All CAVEs which will be sharing the virtual world must use the same network address and ports. The TTL controls how far packets will be broadcast across routers and tunnels. The update interval sets how often the local CAVE will broadcast its tracking data.

The global array CAVEUser contains pointers to structures with the tracking and controller data for all the networked users. The current number of networked users is stored in *CAVENumUsers. These values are updated immediately whenever new data is received by the network process. Different CAVEs will not necessarily be at the same location in the virtual space, so their physical coordinate systems may not be the same. Hence, all networked tracking data is in world coordinates (as controlled by the CAVENav functions), so that all users will share a common frame of reference. The functions CAVENetGetPosition, CAVENetGetOrientation, and CAVENetGetVector return tracking data for networked users like the corresponding local tracking functions.

Application callback functions can be defined to be called when a new user is first added to the list, or when a user exits the application and is removed from the list. These callbacks are defined using CAVEAddCallback.

The function CAVENetSend broadcasts application data. The data can be received in two ways, either by calling CAVENetReceive or by using a callback function. CAVENetReceive is non-blocking; if no new application data has been received by the network process, it returns immediately. If new data is available, it will be returned along with a pointer to the network user struct for the remote CAVE which broadcast the data. Alternatively, a callback can be defined with CAVEAddCallback; this function will then be called automatically with any new application data which is received. A program should only use one of CAVENetReceive or an application data callback, not both.

Be aware that all network callback functions are called in the networking process. They will need to use shared memory to communicate with the main computation process or the display processes.

5.9. Form of a basic CAVE program

5.9.1. Program code

        #include <cave.h>

        void app_shared_init(), app_compute_init(),
	     app_init_gl(), app_draw(),
	     app_compute();

        main(int argc,char **argv)
        {
	 CAVEConfigure(&argc,argv,NULL);
	 app_shared_init(argc,argv);  
	 CAVEInit();  
	 CAVEInitApplication(app_init_gl,0);  
	 CAVEDisplay(app_draw,0);       
	 app_compute_init(argc,argv);  
	 while (!getbutton(ESCKEY))
                app_compute();
         CAVEExit();
        }

5.9.2. Program flow

CAVEConfigure(): This routine reads the CAVE configuration file, and parses argc/argv for any user-specified configuration options.

app_shared_init(): This initializes anything that will be shared by the computation and rendering processes (allocating shared memory, etc.). Since all of the program's data that is not in shared memory will be duplicated up to four times by the forks in CAVEInit, any large chunks of data that do not need to be shared should be allocated after CAVEInit, to save memory.

CAVEInit(): This routine initializes the CAVE. The primary operation that it performs is to fork several processes. These processes are all identical at this point. One process (the "computation process") returns from CAVEInit and runs the rest of main(). The other processes handle the tracking and rendering. There is one rendering process for each wall. These processes call the application's GL init function once (whenever one is given), and repeatedly call the application's drawing function.

CAVEInitApplication(), app_init_gl(): A pointer to the application's graphics initialization function is passed to the rendering process. Since the rendering is not done by the computation process (the one that returns from CAVEInit), but by a separate rendering process, any GL initialization needed for the rendering cannot be done directly by the computation process. Instead, this function sets a pointer in shared memory to tell the rendering process what function to call.

CAVEDisplay(), app_draw(): A pointer to the application's drawing function is passed to the rendering process. As in the CAVEInitApplication routine, this sets a pointer in shared memory to the function. The rendering process then sees this pointer and calls the function itself.

app_compute_init(): This initializes any non-shared data that will be used by the computation process.

app_compute(): This performs the application's computations. Any results that are used by the drawing function should be stored in shared memory.

CAVEExit(): This causes all CAVE processes to exit and restores the machine to its normal state.

Note: If your program does nothing in the computation process (i.e. app_compute() is empty), you should probably call sginap so that the computation process doesn't waste a lot of CPU time that the other processes could use. CAVEInitApplication does not have to be called if it is not needed. When it is used, it should be called after CAVEInit and before CAVEDisplay.

6. CAVE Library

6.1. Overview

A library of C functions and macros has been developed to control the operation of the CAVE. The CAVE library takes care of all the tasks that have to be performed to correctly operate the CAVE. CAVE functions keep all the devices synchronized, produce the correct perspective for each wall, keep track of which walls are in use, and provide the applications with the current state of all the CAVE elements. This section describes in detail each of the routines and macros of the CAVE library.

NOTE: The names of all CAVE functions, macros, and global variables start with the word CAVE (CAVEDisplay, for example).

6.2. Data types

The following data types are defined in cave.h and are used for various CAVE function arguments or global variables.

CAVE_WALL_ID - an enumerated type for identifying the different walls available in the CAVE, with values such as CAVE_FRONT_WALL, CAVE_SCREEN0_WALL, CAVE_SIMULATOR_WALL, etc. There is a distinct value for each wall which can be selected by the "Walls" configuration option.

CAVEID - an enumerated type for most identifier constants other than wall names.

CAVE_SENSOR_ST - a structure containing tracker sensor data. The entries are:

The orientation values are in degrees; azi and roll range from -180 to 180, and elev ranges from -90 to 90. The order of the rotations is azi (Y), elev (X), roll (Z).

CAVE_CONTROLLER_ST - a structure containing controller status information. The entries are:

CAVENETID - a unique ID for a networked CAVE user

CAVE_USER_ST - a structure containing data for a networked user. The structure entries are:

CAVELOCK - an IPC lock, as returned by CAVENewLock and used by CAVESetReadLock, etc.

CAVECALLBACK - a pointer to a callback function (i.e. void (*)())

CAVE_ST - a structure containing pointers to all the library data for the CAVE. Some of the data are stored in shared memory; their entries are therefore pointers, as the CAVE_ST structure itself is not shared. The structure entries include:

6.3. Basic CAVE Functions

The following are the basic CAVE library functions which control the operation of a CAVE program. CAVEInit, CAVEDisplay, and CAVEExit are used by all CAVE applications; the rest are optional. These functions should be called from your main process; they cannot be called from a rendering process.
void CAVEConfigure(int *argc,char **argv,char **appdefaults)
Initializes the CAVE configuration. The CAVE library's internal shared memory arena is created, the various global variables are initialized, the configuration files are read, and then any configuration options given in appdefaults or argc/argv are set (in that order). See Section 9 for a description of the CAVE configuration options.
appdefaults is an array of strings; each string should look just like a line in a configuration file. The last entry in the array must be NULL.
Options set with argc/argv consist of pairs of arguments; the first argument is the keyword with a leading '-' (eg "-walls"), and the second argument contains the rest of the option (eg "front left"). One additional option available with argc/argv is "-caveconfig", which specifies another configuration file to read.
After calling CAVEConfigure, argc & argv will be modified to remove all configuration options, leaving the rest of the command line for the application. NULL may be passed for argc/argv or appdefaults.
CAVEConfigure is called by CAVEInit; if you call it directly, you should do so before calling CAVEInit. Only the first call to CAVEConfigure will do anything.
After everything has been read, the final CAVE configuration will be printed to stderr. This printout can be disabled by setting the environment variable CAVEDEBUGCONFIG to "OFF".

void CAVEDisplay(CAVECALLBACK function,int num_args,...)
This function passes the CAVE library a pointer to your drawing routine. Your routine will be called by the rendering processes once per eye view per frame (i.e. twice per frame for stereo, once per frame for monoscopic mode). All rendering should be done from this routine; any GL calls made directly by the main computation process will have no effect on what is displayed in the CAVE. CAVEDisplay blocks until the next swapbuffers call by the rendering processes.
The first argument is a pointer to the drawing routine. The second argument is the number of arguments that the drawing routine receives (5 is the maximum). If your routine does not take any arguments, pass zero (0). The remainder are the arguments to be passed to your routine. These are stored as void *'s, and so MUST be pointers (also, they should use shared memory if they point to values that the computation process may change).
CAVEDisplay can only be called after CAVEInit.

void CAVEExit(void)
Ends a CAVE program. This function will signal all the CAVE processes to halt, and then calls exit.

void CAVEFrameFunction(CAVECALLBACK function,int num_args,...)
This function passes the CAVE library a pointer to a routine which should be called once per frame. The routine will be called exactly once per frame whether the CAVE is in mono or stereo mode; it is called at the beginning of a frame, before both the init and display routines. CAVEFrameFunction blocks until the next swapbuffers call by the rendering processes.
The first argument is a pointer to the frame routine. The second argument is the number of arguments that the routine receives (5 is the maximum). If your routine does not take any arguments, pass zero (0). The remainder are the arguments to be passed to your routine. These are stored as void *'s, and so must be pointers.
CAVEFrameFunction can only be called after CAVEInit.

void CAVEInit(void)
Initializes the CAVE environment. This function starts the rendering processes, and initializes the trackers and graphics. After CAVEInit is called, the rendering processes are separate from the main computation process; only the computation process will return to your program from CAVEInit.

void CAVEInitApplication(CAVECALLBACK function,int num_args,...)
This function passes the CAVE library a pointer to your graphics initialization routine. Your routine should do any GL initialization that is required for your display functions. The rendering processes will call this routine exactly once, at the beginning of the next frame. CAVEInitApplication blocks until the next swapbuffers call by the rendering processes.
The first argument is a pointer to the graphics initialization routine. The second argument is the number of arguments that the graphics initialization routine receives (5 is the maximum). If your routine does not take any arguments, pass zero (0). The remainder are the arguments to be passed to your routine. These are stored as void *'s, and so must be pointers.
CAVEInitApplication should be called after CAVEInit, and before CAVEDisplay.

void CAVEStopApplication(CAVECALLBACK function,int numargs,...)
This function is used to suspend an application's display processes without actually exiting. It clears the display, initialization, and frame functions (set by CAVEDisplay, CAVEInitApplication, & CAVEFrameFunction), and then has the display processes call function. This routine will not return until after function has been called. Note: you do not have to call CAVEStopApplication before exiting a CAVE program, unless you want the graphics processes to call a "clean-up" function.

6.4. CAVE macros, variables, and miscellaneous functions

CAVE macros simplify access to the wand information. The global variables provide various information about the state of the CAVE.

6.4.1 Sensor & Controller macros

CAVESENSOR(i)
Macro for a pointer to the i'th tracking sensor; i.e. CAVEptr->sensor[i]. Sensor 0 is the head, sensors 1 and up are the wand or any other tracked devices. This pointer can be passed to CAVEGetSensorPosition, CAVEGetSensorOrientation, CAVEGetSensorVector, or CAVESensorTransform.
CAVENETSENSOR(user,i)
Macro for a pointer to the networked user user's i'th tracking sensor; i.e. user->sensor[i]. Sensor 0 is the head, sensors 1 and up are the wand or any other tracked devices.
CAVEBUTTONn = [ 0 | 1 ]
There are three buttons attached to the wand. They can be accessed through the above macros, where n = 1, 2, 3, or 4. The macros have the value 1 when the button is pressed, and 0 when not pressed.
CAVEBUTTON1 corresponds to the left wand button
CAVEBUTTON2 corresponds to the middle wand button
CAVEBUTTON3 corresponds to the right wand button
CAVEBUTTON4 corresponds to the fourth button on the Logitech flying mouse
CAVE_JOYSTICK_X
CAVE_JOYSTICK_Y
The PC-based wand has a pressure-sensitive joystick in addition to buttons. These two macros will give the X & Y coordinate values of the joystick, normalized to be in the range [-1.0,1.0]. (Note: when the joystick is not being pressed, these values will be close to, but not exactly, 0).

6.4.2 Global Variables

The following are global variables used by the CAVE library. CAVENear and CAVEFar can be changed by an application. The other variables are meant for information only; your program should not change them.
int CAVENear,CAVEFar
The near and far clipping plane distances for the CAVE's perspective projection. These are not shared; each rendering process has independent copies.
int CAVEEye
The eye view which is currently being drawn when your display function is called; the possible values are CAVE_LEFT_EYE and CAVE_RIGHT_EYE. This variable is not shared, since the rendering processes are not synchronized except when they call swapbuffers.
int CAVEWall
The wall which a rendering process is responsible for. Possible values are CAVE_FRONT_WALL, CAVE_LEFT_WALL, CAVE_RIGHT_WALL, CAVE_FLOOR_WALL, CAVE_ARPAFLOOR_WALL, CAVE_SCREEN[0-7]_WALL, CAVE_DUAL_EYE_WALL, CAVE_LEFT_EYE_WALL, CAVE_RIGHT_EYE_WALL, CAVE_SIMULATOR_WALL, CAVE_SIMULATOR1_WALL, and CAVE_SIMULATOR2_WALL.
float *CAVEFramesPerSecond
The current frame rate. This is pointer to a float because it is stored in shared memory, and so is the same for all processes.
float *CAVETime
The current "CAVE time". This records the number of seconds since CAVEInit. The variable is updated in the display loop, once per frame, and is stored in shared memory.
char *CAVEVersion
A string identifying the version of the CAVE library. It contains the version number and release date.
CAVE_CONTROLLER_ST *CAVEController
A structure containing the status of the wand controls. The 'button' entry is an array of ints that give the state of the buttons (0 or 1); the 'valuator' entry is an array of floats that give the state of any valuators. The PC-based wand has two valuators - the joystick X and Y. The CAVEBUTTON and CAVE_JOYSTICK macros access this structure.
int *CAVENumUsers
The number of networked users. This is the number of active nodes which the network has received data from, plus the local node.
CAVE_USER_ST **CAVEUser
An array of networked user data. The first *CAVENumUsers entries of CAVEUser are pointers to structures containing the tracking data from the different nodes in the CAVE networking group. CAVEUser[0] contains the local node's data. The other entries are not guaranteed to always maintain the same position in the array; they may be moved as nodes join and leave the networking group. However, the pointer to a given CAVE's data will not change (unless the CAVE exits and then later rejoins the group).

6.4.3 Miscellaneous Functions

void CAVEAddCallback(CAVEID cbtype, CAVECALLBACK function, void *app_data)
Defines an application function which will be called by the library when appropriate. cbtype is the type of callback; its value should be one of: CAVE_DISPLAY_CALLBACK, CAVE_INITGRAPHICS_CALLBACK, CAVE_PERFRAME_CALLBACK, CAVE_NETADDUSER_CALLBACK, CAVE_NETDELETEUSER_CALLBACK, or CAVE_NETAPPDATA_CALLBACK. function is the application function to call; app_data is an argument to pass to the callback function. Defining a CAVE_DISPLAY_CALLBACK function is equivalent to calling CAVEDisplay; CAVE_INITGRAPHICS_CALLBACK is equivalent to CAVEInitApplication; CAVE_PERFRAME_CALLBACK is equivalent to CAVEFrameFunction.
The CAVE_NETADDUSER_CALLBACK will be called by the networking process whenever a new user is added to the CAVEUser array.
The CAVE_NETDELETEUSER_CALLBACK will be called by the networking process whenever a user is deleted from CAVEUser (a user is deleted when no new data has been received from the user for a significant amount of time).
The prototype for a networking add or delete callback is: void function(CAVE_USER_ST *user,void *app_data). user is a pointer to the user structure which is being added or removed; app_data is the application data pointer which was passed to CAVEAddCallback.
The CAVE_NETAPPDATA_CALLBACK will be called by the networking process whenever any application data (i.e. data sent via CAVENetSend) is received from another node. If this callback is used, the data will not be read by CAVENetReceive. The prototype for the net application data callback is: void function(CAVE_USER_ST *user,void *buffer,size_t size,void *app_data). user is a pointer to the user structure corresponding to the node which sent the data; buffer is a buffer containing the data; size is the size of the data in bytes; app_data is the application data pointer which was passed to CAVEAddCallback.
Note: The networking callbacks are called in the networking process; they should avoid using significant amounts of CPU time, or this process will be slowed and the network data may be backed up.
int CAVEButtonChange(int button)
Returns a flag indicating the change in a button's state, compared to the last time the function was called. 0 indicates the button has not changed, 1 indicates that it has been pressed, and -1 indicates that is has been released. button should be 1, 2, 3, or 4. The button states are remembered by this function in each process independently.
float CAVEConvertFromCAVEUnits(float val,CAVEID units)
Takes the distance val in CAVE units (the units specified in the configuration file by CAVEUnits), and returns its equivalent in the given units. units should be one of CAVE_FEET, CAVE_INCHES, CAVE_METERS, or CAVE_CENTIMETERS.
float CAVEConvertToCAVEUnits(float val,CAVEID units)
Takes the distance val in the given units, and returns the equivalent value in CAVE units (the units specified in the configuration file by CAVEUnits). units should be one of CAVE_FEET, CAVE_INCHES, CAVE_METERS, or CAVE_CENTIMETERS.
void CAVEDisplayBarrier(void)
Provides a synchronization barrier for the display processes. When this function is called from an application's display routine, it will wait until all of the display processes reach the barrier before returning. This function should not be called from any other processes; furthermore, it must be called by all the display processes that the library started, or the callers will block indefinitely.
void CAVEDistribBarrier(void)
Provides a synchronization barrier for separate nodes in a distributed CAVE. The calling process will wait until the barrier is reached on all of the distributed nodes before returning. This function should only be called by one process on each node. If distribution is not active, this function will return immediately.
void CAVEDistribClose(void)
Closes the distributed CAVE communications channel. This function should be called by the process which calls CAVEDistribConnect before exiting.
void CAVEDistribConnect(void)
Initializes a distributed CAVE communications channel for application use. This function must be called before any of the other CAVEDistrib functions are called; it must be called after CAVEInit().
boolean CAVEDistribMaster(void)
Returns TRUE when called on the master node of a distributed CAVE; FALSE for all other nodes.
int CAVEDistribReceive(void *buffer,size_t size)
Receives the next block of data sent by another node in a distributed CAVE. buffer is a buffer of at least size bytes which the data will be copied into. The function's returned value is the number of bytes received. This function blocks until data is received, unless distribution is not being used, in which case it returns immediately.
void CAVEDistribSend(void *buffer,size_t size)
Sends a block of data to other nodes in a distributed CAVE. buffer is a pointer to the data to send; size is the number of bytes of data. If distribution is not active, this function will return immediately without doing anything.
void CAVEFree(void *mem)
Frees a chunk of shared memory which was allocated by CAVEMalloc().
void CAVEFreeLock(CAVELOCK lock)
Frees up a CAVE lock, releasing the shared memory that it uses. The lock should be one returned by CAVENewLock().
boolean CAVEgetbutton(CAVE_DEVICE_ID device)
A CAVE equivalent to the IrisGL function getbutton(); returns the state of a button device. device should be one of the CAVE device names listed in cave.h; the names are the same as those used by IrisGL, except prefixed with CAVE_ (e.g. CAVE_AKEY). In IrisGL this function just calls getbutton() for the corresponding GL device. In OpenGL this function consults a table in shared memory which is updated whenever the main display process receives X events; it can thus be called from any CAVE process (note that the mouse pointer must be in the master display's window for events to be received).
void CAVEGetEyePosition(CAVEID eye,float *x,float *y,float *z)
Returns the position of an eye. The first argument indicates which eye's position you are requesting; it should have the value CAVE_LEFT_EYE or CAVE_RIGHT_EYE. The remaining three arguments return the position, in CAVE coordinates.
void CAVEGetOrientation(CAVEID oname,float *angle)
Returns the orientation of a sensor or eye. The oname argument indicates which object's orientation you are requesting; it should be one of CAVE_HEAD, CAVE_WAND, CAVE_LEFT_EYE, CAVE_RIGHT_EYE, CAVE_HEAD_NAV, CAVE_WAND_NAV, CAVE_LEFT_EYE_NAV, or CAVE_RIGHT_EYE_NAV (note that the eyes will have the same orientation as the head). The _NAV id's request the values in navigated (world) coordinates; the other id's request tracker coordinates. The orientation is returned in angle, which should be an array of three floats; angle[0] is the elevation (X rotation), angle[1] is the azimuth (Y rotation), and angle[2] is the roll (Z rotation).
void CAVEGetPosition(CAVEID posname,float *pos)
Returns the position of a sensor or eye. The posname argument indicates what position you are requesting; it should be one of CAVE_HEAD, CAVE_WAND, CAVE_LEFT_EYE, CAVE_RIGHT_EYE, CAVE_HEAD_NAV, CAVE_WAND_NAV, CAVE_LEFT_EYE_NAV, or CAVE_RIGHT_EYE_NAV. The _NAV id's request the values in navigated (world) coordinates; the other id's request tracker coordinates. The position is returned in pos, which should be an array of three floats.
void CAVEGetSensorOrientation(CAVE_SENSOR_ST *sensor,CAVEID frame,float *angle)
Returns the orientation of the tracked sensor whose data is pointed to by sensor. sensor can be a locally tracked sensor, or one from a networked user; the macros CAVESENSOR() and CAVENETSENSOR() return appropriate pointers. frame indicates the frame of reference for the returned data; it should be either CAVE_TRACKER_FRAME for physical, tracker coordinates, or CAVE_NAV_FRAME for world, navigated coordinates. The orientation is returned in angle, which should be an array of three floats; angle[0] is the elevation (X rotation), angle[1] is the azimuth (Y rotation), and angle[2] is the roll (Z rotation).
void CAVEGetSensorPosition(CAVE_SENSOR_ST *sensor,CAVEID frame,float *pos)
Returns the position of the tracked sensor whose data is pointed to by sensor. sensor can be a locally tracked sensor, or one from a networked user; the macros CAVESENSOR() and CAVENETSENSOR() return appropriate pointers. frame indicates the frame of reference for the returned data; it should be either CAVE_TRACKER_FRAME for tracker coordinates, or CAVE_NAV_FRAME for navigated coordinates. The position is returned in pos, an array of three floats.
void CAVEGetSensorVector(CAVE_SENSOR_ST *sensor,CAVEID vecname,float *vec)
Returns a unit vector of the tracked sensor whose data is pointed to by sensor. sensor can be a locally tracked sensor, or one from a networked user; the macros CAVESENSOR() and CAVENETSENSOR() return appropriate pointers. vecname indicates which vector to return, and its frame of reference. The allowed values for vecname are: CAVE_FRONT, CAVE_BACK, CAVE_LEFT, CAVE_RIGHT, CAVE_UP, and CAVE_DOWN, or any of these with the suffix _NAV. The _NAV forms return vectors in navigated coordinates; the base forms return vectors in tracker coordinates. The unit vector is returned in vec, an array of three floats.
float CAVEGetTime(void)
Returns the current "CAVE time", i.e. the number of seconds since the CAVE was initialized. The difference between this and *CAVETime is that CAVEGetTime computes the time when it is called, whereas *CAVETime is only updated once per frame.
long CAVEgetvaluator(CAVE_DEVICE_ID device)
A CAVE equivalent to the IrisGL function getvaluator(); returns the state of a valuator device. device should be one of the CAVE device names listed in cave.h; the names are the same as those used by IrisGL, except prefixed with CAVE_ (e.g. CAVE_MOUSEX). In IrisGL this function just calls getvaluator() for the corresponding GL device. In OpenGL this function consults a table in shared memory which is updated whenever the main display process receives X events; it can thus be called from any CAVE process.
void CAVEGetVector(CAVEID vectorid,float vector[3])
Computes a given tracker unit vector. The vector to return is specified by vectorid, which can be one of: CAVE_HEAD_FRONT, CAVE_HEAD_BACK, CAVE_HEAD_LEFT, CAVE_HEAD_RIGHT, CAVE_HEAD_UP, CAVE_HEAD_DOWN, CAVE_WAND_FRONT, CAVE_WAND_BACK, CAVE_WAND_LEFT, CAVE_WAND_RIGHT, CAVE_WAND_UP, CAVE_WAND_DOWN, or any of these constants suffixed with _NAV (e.g. CAVE_HEAD_FRONT_NAV). The _NAV constants request vectors in navigated coordinates; the other constants request tracker coordinates. The unit vector is returned in vector.
void CAVEGetWindowGeometry(long *origX,long *origY,long *width,long *height)
Returns the origin and size of the calling process's window (this function should only be called in a display process). origX and origY return the position of the lower left corner of the window, measured from the lower left corner of the screen, in pixels. width and height return the size of the window in pixels.
void CAVEHalt(void)
Tells all the child CAVE processes to exit. This performs the exact same actions as CAVEExit(), except that it returns to the caller, rather than calling exit().
void CAVEHeadTransform(void)
Sets up a transformation using the head tracking data, which can be used to position an object at the same location and with the same orientation as the user's head.
The transformation is relative to CAVE tracker coordinates; this function should be called with no transformations active other than that initialized by the CAVE library.
int CAVEInStereo(void)
Returns 1 if the CAVE is displaying stereoscopic images, 0 if it is monoscopic. Note that CAVEInStereo() returning true does not necessarily indicate that a rendering process will call the application's display function twice per frame, as each eye's view could be being handled by a separate process.
void *CAVEMalloc(size_t size)
Allocates a chunk of size bytes of shared memory. If no more shared memory is available, NULL is returned. The memory may be freed by CAVEFree(). The arena used by CAVEMalloc() is initialized in CAVEConfigure(); CAVEMalloc() can be called at any time after that.
int CAVEMasterDisplay(void)
Returns true for the one process which is drawing the 'master' wall (on a machine), false for all others. This can be used when exactly one display process should execute something.
When running a distributed CAVE, each node has its own master display process; CAVEMasterDisplay() will return true for one process on each node.
void CAVENavConvertCAVEToWorld(float inposition[3],float outposition[3])
Converts a position (inposition) in physical CAVE coordinates (such as a tracker position) to navigated world coordinates. The converted position is returned in outposition.
void CAVENavConvertVectorCAVEToWorld(float invector[3],float outvector[3])
Converts a vector (invector) in the physical CAVE coordinate system to the navigated world coordinate system. The converted vector is returned in outvector.
void CAVENavConvertVectorWorldToCAVE(float invector[3],float outvector[3])
Converts a vector (invector) in the navigated world coordinate system to the physical CAVE coordinate system. The converted vector is returned in outvector.
void CAVENavConvertWorldToCAVE(float inposition[3],float outposition[3])
Converts a position (inposition) in navigated world coordinates to physical CAVE coordinates (the coordinate system used by the trackers). The converted position is returned in outposition.
void CAVENavGetMatrix(Matrix m)
Copies the current navigation transformation matrix into m.
void CAVENavLoadIdentity(void)
Resets the navigation transformation matrix to identity.
void CAVENavLoadMatrix(Matrix m)
Replaces the navigation transformation matrix with the given matrix m.
void CAVENavLock(void)
Sets a lock for controlling access to the navigation transformation matrix. While the lock is set, the display processes will be blocked when they try to make a copy of it for the next frame. This can be used to make a series of navigation calls atomic; e.g.:
		CAVENavLock();
		CAVENavLoadIdentity();
		CAVENavTranslate(x,y,z);
		CAVENavRot(angle,'y');
		CAVENavUnlock();
Locking is not needed around single navigation function calls, as that is handled internally. A lock should not be set for very long periods, as it will block the display processes and reduce the frame rate.
void CAVENavMultMatrix(Matrix m)
Multiplies the current navigation transformation matrix by the given matrix m.
void CAVENavRot(float angle, char axis)
Performs a rotation of the CAVE, adding it to the navigation transformation. angle is in degrees; axis should be 'x', 'y', or 'z'.
void CAVENavScale(float xscale, float yscale, float zscale)
Performs a scaling of the CAVE, adding it to the navigation transformation.
void CAVENavTransform()
Applies the current navigation transformation. This should be called in the draw routine when you wish to use world (navigated) coordinates rather than physical (tracker) coordinates.
void CAVENavTranslate(float xtrans, float ytrans, float ztrans)
Performs a translation of the CAVE, adding it to the navigation transformation.
void CAVENavUnlock(void)
Releases the navigation lock set by CAVENavLock().
CAVE_USER_ST * CAVENetFindUser(CAVENETID id)
Returns a pointer to the user struct for the networked user with the given ID. If no such user is found, NULL is returned.
void CAVENetGetOrientation(volatile CAVE_USER_ST *user,CAVEID oname,float *or)
Returns the orientation of a networked user's sensor or eye. user is a pointer to the user structure (an entry in CAVEUser) to get the data from. oname indicates which object's orientation you are requesting; it should be one of CAVE_HEAD, CAVE_WAND, CAVE_LEFT_EYE, CAVE_RIGHT_EYE, CAVE_HEAD_NAV, CAVE_WAND_NAV, CAVE_LEFT_EYE_NAV, or CAVE_RIGHT_EYE_NAV. The first four choices return data in the local CAVE's tracker coordinate system; the last four return data in world (navigated) coordinates. (Note that the eyes will have the same orientation as the head). The orientation is returned in angle, which should be an array of three floats; angle[0] is the elevation (X rotation), angle[1] is the azimuth (Y rotation), and angle[2] is the roll (Z rotation).
void CAVENetGetPosition(volatile CAVE_USER_ST *user,CAVEID posname,float *pos)
Returns the position of a networked user's sensor or eye. user is a pointer to the user structure (an entry in CAVEUser) to get the data from. posname indicates what position you are requesting; it should be one of CAVE_HEAD, CAVE_WAND, CAVE_LEFT_EYE, CAVE_RIGHT_EYE, CAVE_HEAD_NAV, CAVE_WAND_NAV, CAVE_LEFT_EYE_NAV, or CAVE_RIGHT_EYE_NAV. The first four choices return positions in the local CAVE's tracker coordinate system; the last four return positions in world (navigated) coordinates. The position is returned in pos, which should be an array of three floats.
void CAVENetGetVector(volatile CAVE_USER_ST *user,CAVEID vecname,float *vec)
Computes a given tracker unit vector for a networked user. user is a pointer to the user structure (an entry in CAVEUser) to get the data from. The vector to return is specified by vecname, which can be one of: CAVE_HEAD_FRONT, CAVE_HEAD_BACK, CAVE_HEAD_LEFT, CAVE_HEAD_RIGHT, CAVE_HEAD_UP, CAVE_HEAD_DOWN, CAVE_WAND_FRONT, CAVE_WAND_BACK, CAVE_WAND_LEFT, CAVE_WAND_RIGHT, CAVE_WAND_UP, or CAVE_WAND_DOWN, or any of these names suffixed with _NAV (e.g. CAVE_WAND_FRONT_NAV). The _NAV choices return data in world coordinates; the other choices return data in the local CAVE's tracker coordinate system. The unit vector is returned in vec.
void CAVENetHeadTransform(volatile CAVE_USER_ST *user)
Sets up a transformation using a networked user's head tracking data, which can be used to position an object at the same location and with the same orientation as that user's head. user is a pointer to the user structure to get the data from.
int CAVENetReceive(void *buf,size_t size,CAVE_USER_ST **user)
Receives any application data which has been broadcast by another node in the CAVE networking group. The data returned will be the result of exactly one CAVENetSend() call. Data sent by the local application will not be received. buf is a pointer to the buffer to store the data in; size is the size of the buffer in bytes. user will return a pointer to the user structure corresponding to the node which broadcast the data. The return value is the number of bytes of data which were read; it is 0 if no new packets were available. If the incoming packet is larger than size, the excess bytes are discarded.
void CAVENetSend(void *data,size_t size)
Broadcasts application data to all other nodes in the CAVE networking group. data is a pointer to the data to send; size is the size of the data in bytes.
void CAVENetWandTransform(volatile CAVE_USER_ST *user)
Sets up a transformation using a networked user's wand tracking data, which can be used to position an object at the same location and with the same orientation as that user's wand. user is a pointer to the user structure to get the data from.
CAVELOCK CAVENewLock(void)
Creates a new CAVE lock structure which can be used for mutual exclusion between CAVE processes which use shared memory. A CAVE lock has two modes - read locking and write locking. Any number of processes can set a lock for read locking simultaneously; only one process can write lock it at any time. The lock returned by this function can be passed to CAVESetReadLock(), CAVESetWriteLock(), CAVEUnsetReadLock(), CAVEUnsetWriteLock(), and CAVEFreeLock(). The lock is created in shared memory; roughly 1300 locks can be allocated given the current size of the CAVE library's arena.
On the Onyx, these locks use hardware spin-locks, which are not guaranteed to prevent starvation.
void CAVEResetTracker(void)
Signals the tracking process to reset the tracker hardware (via the SIGUSR2 signal).
void CAVESensorTransform(CAVE_SENSOR_ST *sensor)
Sets up a transformation using the given sensor's tracking data, which can be used to position an object at the same location and with the same orientation as the sensor. sensor can be a locally tracked sensor, or one from a networked user; the macros CAVESENSOR() and CAVENETSENSOR() return appropriate pointers.
The transformation is relative to CAVE tracker coordinates; this function should be called with no transformations active other than that initialized by the CAVE library.
int CAVESetOption(CAVEID option,int value)
Sets options for various library functions. Options which affect the amount of memory allocated for CAVE operations (CAVE_NET_NUMBUFFERS, CAVE_NET_BUFFERSIZE, CAVE_SHMEM_SIZE) must be set before calling CAVEConfigure(). Options which affect the graphics initialization (CAVE_GL_SAMPLES, CAVE_GL_STENCILSIZE, CAVE_GL_ACCUMSIZE) must be set before calling CAVEInit(). The options available are:
CAVE_GL_ACCUMSIZE
The number of accumulation buffer bitplanes (per color component) that should be allocated when the graphics windows are opened. The default value is 0.
CAVE_GL_SAMPLES
The number of samples per pixel to be allocated in multisampling mode. When this is 0, multisampling is not enabled; when non-zero, the non-multisampled Z buffer and stencil sizes are set to 0. The default value is 0. If the hardware does not support the number of samples requested, the largest possible number of samples less than the request will be allocated.
CAVE_GL_STENCILSIZE
The number of stencil buffer bitplanes that should be allocated when the graphics windows are opened. The default value is 0.
CAVE_NET_UPDATELOCALDATA
A flag indicating whether CAVEUser[0] (the network data for the local user) should be updated when networking is disabled. If value is 1, the data will be updated once per frame by the master display process; if it is 0, the data will not be updated. The update will always occur if networking is enabled. The default value is 0.
This is useful for applications which make use of CAVEUser[0] and will need the data updated even when networking is not active. Other applications should leave it off to avoid unnecessary overhead in the display process.
CAVE_NET_NUMBUFFERS
The number of buffers to use for queueing application data received by the networking process. The default value is 32. This is only meaningful if CAVENetReceive() is used; if an application data callback is used instead, the library will not use a buffer queue.
CAVE_NET_BUFFERSIZE
The size (in bytes) of the buffers that the networking process will use for sending and receiving application data. The default value is 4096 bytes.
CAVE_PROJ_USEWINDOW
A flag indicating whether CAVEGetProjection() should use the current window size when computing the projection for the simulator view. The default value is 1.
CAVE_SHMEM_SIZE
Defines the size of the shared arena used by CAVEMalloc(); value is the arena size in bytes. This must be done before CAVEConfigure() is called, as the arena cannot be changed once it is initialized. The default arena size is 8 megabytes for Irix shared memory, or 64 kilobytes for Scramnet shared memory.
CAVE_TRACKER_SIGNALRESET
A flag indicating whether signals should be used for the tracker reset function. CAVEResetTracker() sends a SIGUSR2 signal to the process handling tracking, to cause it to call the actual reset function. If your application uses SIGUSR2 itself, you may wish to disable this, although it should only be significant when the SerialTracking configuration is enabled, as then the tracking is done by the main display process. The default value is 1.
void CAVESetReadLock(CAVELOCK lock)
Sets a CAVE lock to indicate that the calling process will be reading the associated shared data. While the read lock is set, any number of other processes may also obtain read locks, but any processes requesting write locks will be blocked until all the read locks are released (by CAVEUnsetReadLock()).
void CAVESetWriteLock(CAVELOCK lock)
Sets a CAVE lock to indicate that the calling process will be writing the associated shared data. While the write lock is set, no other process may obtain a read or write lock on the given CAVE lock. The write lock is released by CAVEUnsetWriteLock().
void CAVEUnsetReadLock(CAVELOCK lock)
Releases a read lock which was set by CAVESetReadLock(). This reduces the count of readers by one; if the count reaches 0, a process waiting to set a write lock may then be allowed through.
void CAVEUnsetWriteLock(CAVELOCK lock)
Releases a write lock which was set by CAVESetWriteLock(). If other processes are waiting to set a read or write lock on this lock, one of them will then be allowed through.
void *CAVEUserSharedMemory(int size)
Creates a shared memory arena which can be used by your program. The argument is the size of the arena in bytes. The return value is a pointer to the arena which can be passed to amalloc in order to allocate space from it (be aware that amalloc requires some extra space for overhead - a few hundred bytes of general overhead, plus 16 bytes per amalloc'ed chunk of memory). This function should be called before CAVEInit, so that all processes will have access to the shared memory.
void CAVEWallTransform(void)
Sets up a transformation based on the calling process's wall. This transformation will make the origin coincide with the lower left corner of the wall, with the X and Y axes aligned with edges of the wall. It can be used to draw objects directly on the wall.
void CAVEWandTransform(void)
Sets up a transformation using the wand's tracking data, which can be used to position an object at the same location and with the same orientation as the wand.
The transformation is relative to CAVE tracker coordinates; this function should be called with no transformations active other than that initialized by the CAVE library.

6.5 Environment Variables

There are two Unix environment variables which can be used to customize the behavior of CAVE programs. They are:
CAVE_HOME
This defines the top-level directory containing the CAVE distribution; the default directory, if CAVE_HOME is not set, is /usr/local/CAVE. The library looks under this directory for the etc/ directory, which contains the system-wide configuration files, and for the bin/ directory, which contains the mplock and mpunlock commands.
CAVEDEBUGCONFIG
Normally, CAVEConfigure() prints the final configuration data to stderr when it has finished reading all the configuration files. If this variable is defined as "OFF", the printout will not be done.

6.6. Internal functions

This section describes some of the functions which are used internally in the CAVE library. They are not called directly by a normal CAVE application. However, they may be useful for developing other applications which need some of the functionality of the CAVE library.

There are functions for reading configuration files, running the tracking and networking processes, and getting the off-axis projection matrices for CAVE walls.

void CAVECheckXEvents(void)
void CAVEConfigurationInit(int *argc,char **argv,char **appdefaults,CAVE_CONFIG_ST *config)
void CAVECreateWindow(CAVE_WINDOW_ST *win,boolean dual_eye)
char *CAVEDebugConfigWallName(CAVE_WALL_ID wallnum)
void CAVEGetProjection(CAVE_WALL_ID wall,CAVEID eye,float frustum[6],float viewmat[16])
void CAVEdGetWallGeom(CAVE_WINDOW_ST *win,boolean dual_eye)
void CAVEDistribConnectP(void)
void CAVEDistribSaveFrameData(CAVE_ST *cave)
void CAVEDistribUpdateFrameData(CAVE_ST *cave,boolean ack)
void CAVEdMaskArpafloor(void)
void CAVEdSimDrawObjects(void)
void CAVEdSimCheckInput( )
char *CAVEFullPathName(char *file)
void CAVEGetProjection(CAVE_WALL_ID wall,CAVEID eye,float *frustum,Matrix viewmat)
void CAVENavSaveTransform(void)
void CAVENetStart(CAVE_CONFIG_ST *config)
Must call CAVEConfigure()
void CAVENetUpdateLocalData(void)
void CAVETrackerCleanup( )
void CAVETrackerStart(CAVE_CONFIG_ST *config)
Must call CAVEConfigure()
void CAVEUpdateSensors(void)
CAVE_CONFIG_ST CAVEConfig
CAVEOptions

7. CAVE Simulation

7.1 Introduction

The CAVE Library provides options to simulate some or all of the hardware-specific parts of the CAVE environment. This allows application developers to write and test code on ordinary workstations, without requiring constant use of the CAVE hardware.

There are three basic parts of the CAVE which can be simulated - the tracker input, the wand input, and the immersive display. When running a CAVE program, the configuration file (see Section 9) can be used to select the simulator mode for these options (note that the "Simulator y" option is available as a shorthand method of selecting all simulator options at once). The simulated tracking and wand use the keyboard and mouse for controls; the simulated display provides a perspective view, not limited to a single wall, and also an outside-the-CAVE third-person view.

7.2 Simulated tracking

Simulated tracking is selected by the configuration option "TrackerType simulator". The controls for moving the simulated head and wand are given below.

7.2.1 Head Controls

The simulated user's head can be moved and rotated within the CAVE using the arrow keys. Note that the head is restricted to remain within the confines of physical CAVE. The commands to control the head are:
LEFT_ARROW ............ Move left
RIGHT_ARROW ........... Move right
UP_ARROW .............. Move forward
DOWN_ARROW ............ Move backward
SHIFT + UP_ARROW ...... Move up
SHIFT + DOWN_ARROW .... Move down
ALT + LEFT_ARROW ...... Rotate left
ALT + RIGHT_ARROW ..... Rotate right
ALT + UP_ARROW ........ Rotate up
ALT + DOWN_ARROW ...... Rotate down
P .......... Reset head and wand to initial positions

7.2.2 Wand Controls

The wand is controlled using the mouse. Moving the mouse while holding down the appropriate key will move or rotate the wand. As with the head, the wand is restricted to stay inside the CAVE. When the user's head is moved, the wand is moved with it. If more than one wand is being simulated (using the SimulatorNumWands configuration option), only one wand at a time may be controlled; the wand to control is selected using the F keys (i.e. F1, F2, etc.).

The wand movement controls are as follows:

CTRL + mouse movement ....... Move wand left/right/forward/back
SHIFT + mouse movement ...... Move wand left/right/up/down
ALT + mouse movement ........ Rotate wand left/right/up/down
< and > .......... Roll wand (rotate about Z)
HOME ....... Reset wand to be in front of user
F1/F2/F3/... ................ Select wand 1/2/3/... as the current wand being controlled.

7.3 Simulated wand controls

The simulated wand controls (buttons & joystick) are selected by the configuration option "Wand simulator".

Pressing the mouse buttons corresponds to pressing the wand buttons. Holding down the spacebar while moving the mouse controls the joystick values. Note that the joystick controls set the X and Y values based on the current position of the mouse on the screen, rather than the mouse's relative movement (i.e. the top of the screen is Y=1.0, etc.). The joystick is reset to (0,0) when the spacebar is released.

7.4 Simulated display

The simulated display is selected by using the "simulator" wall (or "simulator1" or "simulator2") in the Walls configuration option.

There are three display modes for the simulator wall. In mode 0, it displays what would be rendered on one of the CAVE walls; in mode 1, it displays a normal perspective view of the application's environment from the position of the user's head; and in mode 2, it displays a third-person view showing the user inside the CAVE. The simulator views can also show the position of the user's head and of the wand, the current frame rate, and the outline of the physical CAVE, and can black-out the parts of the scene which would not be visible due the lack of right, back, and ceiling walls.

The keyboard controls for these options are:

0 ...... Switch to "wall-view" mode
1 ...... Switch to user centered Perspective mode
2 ...... Switch to Outside the CAVE mode
D .......... Switch to "Desk mode" (for outline & blackout)
C .......... Switch to "CAVE mode" (for outline & blackout)
T .......... Toggle timing (frame rate) display
W .......... Toggle display of wand
U .......... Toggle display of user (head)
INSERT ..... Toggle display of CAVE/Immersadesk outline
DEL ........ Toggle blackout of right, rear, and ceiling walls
H .......... Print help text
When in wall-view mode (mode 0), the following keys select which wall's display is rendered:
F ........ front wall
L ........ left wall
B ........ floor ("bottom")
R ........ right wall
D ........ Immersadesk (screen7)
When using the outside-the-CAVE view, you can move the viewpoint around with the following controls:
KEYPAD ARROWS (2,4,6,8) .... Rotate the viewpoint
KEYPAD -/+ ................. Zoom in/out
KEYPAD 5 ................... Reset the viewpoint

8. Supporting software

There are several auxiliary programs which are used either by the CAVE library or for testing the CAVE hardware. These programs can be found in /usr/local/CAVE/bin.
mplock
mpunlock
These programs will isolate and unisolate CPUs on the system. The arguments are the numbers of the CPUs which are to be isolated/unisolated. If the CPULock configuration option is set, mplock will be run by the CAVE library on startup, and mpunlock will be run when the CAVE exits. These programs are separate from the CAVE library because CPU isolation requires superuser privileges; hence, the mplock and mpunlock binaries must be owned by root and have their setuid permissions bit set.
vss
The sound server. vss must be running on the audio server machine (whose IP address is given in the configuration file) before a CAVE program starts. See the CAVE Audio Library manual for more information.
testpattern [cavesize] [pixel-xdim] [pixel-ydim]
This program generates test patterns used for aligning the CAVE projectors and matching colors between screens. It is intended to be run when the display is in 1025x768 stereo video mode. The first optional argument is the size of the CAVE (in feet); the default is 10'. The second and third arguments are the resolution of the display, if other than 1025x768. The different patterns are selected using the number keys (1, 2, 3, and 4). Pattern 1 is a grid of 6"x6" squares, with diagonals and circles, and an "L" in the leftbuffer and "R" in the rightbuffer. There are diagonals and circles for both the full screen and for 90% of the screen height (for the 10'x9' walls). Pattern 2 displays colorbars running both vertically and horizontally. Pattern 3 is the same as pattern 2, except flipped horizontally; this is meant for the right wall. Patterns 4 & 5 are the same as patterns 1 & 2, but rotated 45 degrees; these are intended for the setup of the ARPA Enterprise CAVE's floor.
cavevars
This is a basic confidence test for the CAVE library and hardware. It displays the values of all the CAVE library's global variables on the front wall, left wall, and floor. The values include the tracker data, the eye positions derived from the tracker data, the status of the wand buttons & joystick, and the timing information. The string "Left Eye" is displayed in the left buffer, and "Right Eye" in the right buffer. A set of X/Y/Z axes are displayed at the wand position and at the head position (using the CAVE library tracker vector macros). To exit the program, press Escape.
scramclear
scramclear clears Scramnet memory, resetting the entire contents to 0. This is sometimes necessary if a previous CAVE program using Scramnet did not exit properly, and left the Scramnet memory management tables in an incorrect state.
scraminit
scraminit is derived from Systran's Scramnet diagnostics program. It merely initializes the board without any interaction. This program must be run once, whenever the system is rebooted, before other programs can use Scramnet.
netserver
A very simple TCP/IP server which is used for exchanging data with other CAVEs when the networking method is tcp. Generally, multicast networking is preferable; this server was created for those networks which do not support UDP multicasting.
trackd
trackd is a tracking daemon, which can be used with the 'daemon' options for the TrackerType and Wand configuration. It supports the Flock of Birds and Spacepad trackers, and the PC based wand. To read the Flock tracker, run trackd with the flag -birds; to read the Spacepad, run it with the flag -spacepad. To read the PC wand, run it with the flag -controller <port>, where <port> is the name of the serial port that the PC is attached to (e.g. /dev/ttyd4). When using trackd with the wand, the serial port must not be configured for a dial/button box.

9. CAVE Configuration File

The CAVE configuration file lists a number of setup options for the CAVE which may change, but which would not normally be set by your program. These include such things as which walls to use, and offsets for the physical location of the tracker devices. When a CAVE program starts, the function CAVEConfigure will read the configuration file and save the information in a global record used by the various CAVE library functions. The default, system wide configuration file /usr/local/CAVE/etc/cave.config is read first. After reading this, CAVEConfigure will look for the file /usr/local/CAVE/etc/HOST.config, where HOST is the machine name, as returned by gethostname(). Next, the file .caverc in your home directory is read, followed by the file .caverc in the current directory. Any entries in one file will override the settings from the files which were read earlier. This means that your .caverc file(s) should only contain those values which you wish to change from the default; most of the values, such as those for the trackers, should only be specified in the system configuration files.

The configuration file is a text file with one configuration setting per line. Each setting consists of a keyword followed by one or more values for that configuration variable. Lines beginning with # are comments. The parsing of keywords by CAVEConfigure is case-insensitive. All options which specify linear measurements should have their units given at the end of the line; the units that may be used are inches, feet, centimeters, and meters; if no units are given, feet are assumed. The configuration options that use units are: InterocularDistance, HeadSensorOffset, WandSensorOffset, TransmitterPosition, Origin, CAVEWidth, CAVEHeight, SimulatorView, and ProjectionCorners. The possible keywords and their meanings are:

AppDistribution <method>
Selects the communication method to use for distributed CAVE function calls made by the application. This can be different from the distribution method used for the library internals (selected by Distribution). The possible values for <method> are the same as for Distribution, below.
AudioServer <ip-address>
The numerical IP address of the host which is running the audio server. CAVEConfigure will set the environment variable SOUNDSERVER using this value.
BirdsHemisphere <hemisphere>
Specifies which hemisphere to have the Flock-of-Birds tracker use. hemisphere should be one of "lower", "upper", "front", "aft", "left", or "right". The default is "lower".
BirdsSensors <headsensor> <wandsensor> ...
Specifies which Flock of Birds sensors to read. The arguments are the numeric ID's of the sensors, as reported by the tracker. The sensors will be stored in the CAVESENSOR list in the order given here; i.e. the first sensor listed will be used for the head, the second for the wand, etc. The default values are "2 3".
BirdsTransmitter <id>
Gives the numeric ID of the Flock of Birds transmitter. The default is 1.
Boom3Buttons 2c|mv|sh3|shjr|3c|dhjlr
Identifies what type of controls a BOOM has; this determines what CAVEController data will be available when the wand type is "boom3". "2c" indicates a BOOM2C with two handles with one button per handle; two CAVE button values are available. "mv" indicates a BOOM with two buttons per handle; four button values are available. "sh3" indicates a single-handle BOOM with 3 buttons; 3 button values are available. "shjr" indicates a single-handle BOOM with a joystick and a 'reset switch'; one button and two valuators (joystick X & Y) are available. "3c" indicates a BOOM3C with one button per handle; two buttons are available. "dhjlr" a BOOM3C with a joystick on one handle and a L/R rocker on the other; two buttons and two valuators are available; rocker left is returned as CAVEBUTTON1 being pressed, and rocker right is returned as CAVEBUTTON2 being pressed.
This option is only used in the special BOOM version of the CAVE library.
Boom3Config <config-file>
Gives the name of the BOOM configuration file, which will be passed to boom_open() by the boom3 tracking option.
This option is only used in the special BOOM version of the CAVE library.
Calibration y|n
Whether or not to use calibration for the tracker positions.
CalibrationFile <filename>
The name of the file containing the tracker calibration data.
CAVEConfig <filename>
Gives the name of another configuration file to read. The file is read at the point that the CAVEConfig is encountered. If filename is not an absolute path, the file is first searched for in the current directory, then in the user's home directory, and finally in /usr/local/CAVE/etc (or wherever $CAVE_HOME points). Only the first instance of the file to be found will be read.
CAVEHeight <height> <units>
The height of the physical CAVE. This is only used in calculating the projection for the original CAVE walls (front, left, right, floor, ceiling, wall).
CAVEScale <scalefactor>
An amount to scale the size of the CAVE by. All tracking and projection data will be scaled by this factor, so the effect will be to produce a view that looks like the virtual world has been scaled.
CAVEWidth <width> <units>
The width of the physical CAVE. This is assumed to be the depth as well. This is only used in calculating the projection for the original CAVE walls (front, left, right, floor, ceiling, wall).
ControllerDaemonKey <key>
Gives the key number for the shared memory segment which is being used by the controller daemon process.
CPULock y|n
Whether to "lock" the CPUs or not. If this is 'y', each CAVE process will be forced to run on a different, isolated CPU. The isolation will prevent other processes on the system from using these CPUs. CPU 0 will not be isolated; if there are not enough processors for all the CAVE processes, the remainder will all share CPU 0. CAVEExit will un-isolate the CPUs. This requires the programs mplock and mpunlock (in /usr/local/CAVE/bin). If your CAVE program crashes while using this option, you should run mpunlock manually to un-isolate the processors.
DefaultTrackerOrientation <elevation> <azimuth> <roll>
The default values to be used for a tracker's orientation. These values are used when no tracker is selected, or for sensors which are not being tracked.
DefaultTrackerPosition <x> <y> <z>
The default values to be used for a tracker's position. These values are used when no tracker is selected, or for sensors which are not being tracked.
DisplayMode <mode>
Determines whether the display will be stereoscopic or monoscopic. <mode> can be either "mono", "stereo", or "oldstereo". "stereo" selects the newer stereo buffer method; "oldstereo" selects the older STR_RECT style stereo.
Distribution <method>
Selects the communication method to use for a distributed CAVE. If distribution is not to be used, <method> should be "none". The distribution methods available are "scramnet", "hippi", "msgq", and "tcp". "scramnet" distribution uses Scramnet reflective memory, and will support more than 2 machines. "hippi" distribution uses raw Hippi communications between two machines, either point-to-point or through a Hippi switch. "msgq" distribution uses Unix message queues to communicate between two separate CAVE processes running on the same machine. "tcp" distribution uses TCP/IP sockets.
DistribHippiMasterIfield <ifield>
The Ifield of the master node in a Hippi distributed CAVE. This is needed if the Hippi connection between the machines uses a switch. System ifields are typically defined in an imap file, such as /usr/etc/hippi.imap.
DistribHippiSlaveIfield <ifield>
The Ifield of the slave node in a Hippi distributed CAVE. This is needed if the Hippi connection between the machines uses a switch.
DistribHippiULP <ulp>
The ULP to use for Hippi communications in the distributed CAVE. The library's internal synchronization and communication will use this ULP; any communication done by an application via calls to CAVEDistrib functions will use a separate connection with ULP <ulp>+1.
DistribID <id>
The ID number for a distributed CAVE node. This should be in the range 0 to N-1, where N is the number of distributed nodes. The node with ID 0 is the master; it must be started before any slave nodes.
DistribMsgqKey <key>
The message queue key to use for communications in a "msgq" style distributed CAVE. The library's internal synchronization and communication will use this key; any communication done by an application via calls to CAVEDistrib functions will use a separate queue with key <key>+1.
DistribNodes <number>
The number of nodes that the distributed CAVE is composed of. All current distribution methods support only 2 nodes.
DistribTCPMaster <hostname>
The network host name of the master node when using TCP/IP distribution.
DistribTCPPort <port-number>
The port number to use for TCP/IP distribution communications. <port-number> should be greater than 1024, and different from any standard service port numbers (see /etc/services).
FilterBirds y|n
If this flag is "y", the Flock-of-Birds tracker's hardware filtering option will be enabled.
FilterBirdsParameter <param>
Specifies the argument for the Flock of Birds hardware filtering command. <param> is an integer that defines which filters are to be used; the possible values are described in the Ascension manual under CHANGE VALUE / FILTER STATUS.
GangSwap y|n
If this option is "y", the Reality Engine's hardware swapready lines will be used to synchronize the multiple displays in a distributed CAVE. This option only works with IrisGL.
HeadSensorOffset <X-offset> <Y-offset> <Z-offset> <units>
Offset values from the position of the head sensor to the point between the user's eyes. The offset is added to the position reported by the head tracker to yield the position that the CAVE library will report.
HeadSensorRotation <axis-X> <axis-Y> <axis-Z> <angle>
The rotation of the physical head sensor relative to the sensor as reported by the library. This is defined by an axis vector and an angle of rotation around that vector. The angle is in degrees.
HeadSensorRotationMatrix <m00> <m01> <m02> <m10> <m11> <m12> <m20> <m21> <m22>
Defines the head sensor rotation using a 3x3 matrix, instead of an axis and angle.
HideCursor y|n
Whether or not to blank the cursor when in the CAVE windows.
InterocularDistance <distance> <units>
The distance between the user's eyes. 2.75 inches is the default value.
Network <method>
What style of networking to use. <method> can be one of "mcast", "tcp", "c2c", or "none". If networking is enabled (i.e. <method> is not "none"), a separate process will be started by CAVEInit to broadcast and receive tracking and application data. For the "mcast" <method>, UDP multicasting is used; for "tcp", a TCP/IP connection is made to the netserver server; for "c2c", Argonne's C2C library is used.
NetworkAddress <mcast-ip-address>
The numeric IP address of the multicast group which will be used for broadcasting CAVE data, when the networking method is "mcast". All CAVE applications running on the local multicast network using this address will share data; different applications on the same network should use different addresses to keep their data separate.
NetworkAppPort <port-number>
The port number to use for broadcasting application data (for the functions CAVENetSend and CAVENetReceive). <port-number> should be greater than 1024, and different from any standard service port numbers (see /etc/services).
NetworkCPUHog y|n
Whether or not to the networking process should run as fast as possible. Default value is 'n', which causes the process to sleep briefly (10 milliseconds) whenever no new data is being sent or received, to limit its CPU use. If set to 'y', the process will constantly check for new data to send or receive without pausing.
NetworkMaxUsers <num-users>
The maximum number of users to expect to receive data from. This defines the size of the CAVEUser array. The default value is 32.
NetworkPort <port-number>
The port number to use for broadcasting tracking data. <port-number> should be greater than 1024, and different from any standard service port numbers (see /etc/services).
NetworkTCPServer <hostname>
The name of the host running netserver, for the TCP networking method.
NetworkTTL <ttl>
The 'time-to-live' for multicast networking packets. This can be used to control how far packets will spread through gateways and multicast tunnels (see mrouted documentation for details).
NetworkUpdateInterval <interval>
Controls how frequently the networking process will broadcast tracking packets. <interval> is the number of seconds between broadcasts (ie a value of .05 will broadcast new data 20 times a second).
If <interval> is negative, the local tracking data will never be broadcast. This provides a 'stealth' mode where the local CAVE sees all other CAVEs, but is not visible to them.
Origin <left-distance> <floor-distance> <front-distance> <units>
Origin of the CAVE coordinate system. This is specified as the distance from the three walls to the origin. For the 10' CAVE, these values are "5 0 5 feet". This is only used in calculating the projection for the original CAVE walls (front, left, right, floor, ceiling, back).
ProjectionCorners <wall-name> <lower-left x y z> <upper-left x y z> <lower-right x y z> <units>
Defines the corners of the projection plane for a given wall. This is used by the "screen[0-7]", "left_eye", and "right_eye" walls. For a "screen" wall, it gives the positions of the lower left, upper left, and lower right corners of the screen in tracker coordinates. For a head-tracked wall, the three points define the geometry of that eye's projection plane in eye-space coordinates; eye-space coordinates are defined as having the eye at the origin, with the axes aligned with the user's head - the Z axis points directly back, the Y axis points up, the X axis points right.
This is used to compute the perspective projection for <wall-name>, and so must be given if one of the "screen" or head-tracked walls is selected by "Walls".
Scramnet y|n
Indicates whether Scramnet should be initialized for use. This flag will be automatically set if any other configuration options (such as Distribution) require Scramnet.
ScramnetDevices <memoryDevice> <registerDevice>
The file names of the VME devices which correspond to Scramnet memory and Scramnet control registers.
ScramnetMemBase <baseAddress>
The base address of Scramnet memory, for mmap()ing the Scramnet device. This value can be found in the Scramnet software's configuration file (cfg/scrcfg.dat).
ScramnetMemSize <size>
The size of Scramnet memory in bytes. This value can be found in the Scramnet software's configuration file (cfg/scrcfg.dat).
ScramnetRegBase <baseAddress>
The base address for mmap()ing the Scramnet control registers. This value can be found in the Scramnet software's configuration file (cfg/scrcfg.dat).
ScramnetRegSize <size>
The size of the Scramnet control registers' area of memory. This value can be found in the Scramnet software's configuration file (cfg/scrcfg.dat).
SerialTracking y|n
Indicates whether tracking should be done serially or in parallel. If SerialTracking is enabled, one of the rendering processes will read the tracker and wand devices; if it is disabled (the default), a separate process is used to read the devices. In general, serial tracking should only be used with the simulator tracker, where a separate process can put too much of a load on the X server and slow up the rest of an application, or with the daemon or Scramnet trackers, which do not require much CPU processing.
Simulator y|n
Shorthand option for selecting full simulator mode. Using the configuration "Simulator y" is equivalent to specifying the options "Walls Simulator", "WallDisplay simulator -1 window", "Tracking y", "TrackerType simulator", "Wand simulator", "WandSensorOffset 0 0 0", "WandSensorRotation 1 0 0 0", "HeadSensorOffset 0 0 0", "HeadSensorRotation 1 0 0 0", "TransmitterOffset 0 0 0", "TransmitterRotation 1 0 0 0", "UseCalibration n", "SerialTracking y", "Distribution none", and "AppDistribution none". NB: Saying "simulator n" will merely set the flag CAVEConfig->Simulator to 0; it will not undo any other effects of a previous "simulator y".
SimulatorJoystickControl <device>
Specifies the key to press to activate the joystick control when using the simulator wand. <device> is the name of a device as given in <gl/device.h> (e.g. "CAPSLOCKKEY"). The default is the spacebar.
SimulatorJoystickFullScreen y|n
If this value is "y", the joystick value for the simulator wand will be determined by the mouse position on the screen, rather than within the display window. The default value is 'n'.
SimulatorNumWands <num>
The number of wand sensors to simulate, when using simulator tracking. The default is 1.
SimulatorView <width> <height> <distance> [<units>]
The viewing parameters which define the projection frustum for the simulator display. <width> and <height> are the size of the screen; <distance> is the distance of the viewer's head from the screen.
SyncBirds y|n
If this flag is "y", the Flock-of-Birds tracker will be made to synchronize itself with the projectors, to prevent any interference. This requires additional hardware connecting the Birds and the projector.
TrackerBaud 19200|9600|...
Baud rate for the tracker serial device. 19200 is the default.
TrackerDaemonKey <key>
Gives the key number for the shared memory segment which is being used by the tracker daemon process.
TrackerPort <portname> [<port2name>]
Name of the serial port(s) that the trackers are attached to. <portname> should be something like /dev/ttym2. The Logitech tracker uses a second serial port for the wand tracker; <port2name> identifies this port.
TrackerType <name>
Which type of tracking hardware is being used. <name> should be one of "birds", "spacepad", "logitech", "daemon", "boom", "boom3", "mouse", "simulator", "spaceball", or "none". "birds" selects the Ascension Flock-of-Birds magnetic tracker. "spacepad" selects the Ascension Spacepad tracker. "logitech" selects the Logitech ultrasonic tracker. "daemon" selects the tracker daemon method, where tracking is done by a completely independent program, communicating with the library through standard SysV shared memory (see the TrackerDaemonKey configuration). "boom" selects the library's built-in BOOM tracking code; this has only been tested on NCSA's older BOOM2, and may not work on other hardware versions. "boom3" selects tracking which uses Fakespace's libboom library to read the BOOM data; it requires the "Boom3Config" option to be set; this is only available with the special BOOM version of the CAVE library. "mouse" selects a simple mouse-controlled tracking system, which can be useful for testing purposes. "simulator" selects a simulated tracking system that uses keyboard and mouse controls (see Section 7.2). Since both the tracker positions and the wand controls are handled by the tracker process, "TrackerType none" can be used to have this process only check the wand controls. Note: the spaceball tracker is not available in the OpenGL version of the library.
Tracking y|n
Whether or not to run the tracker process. If tracking is disabled, the head and wand are assigned fixed default positions (set by DefaultTrackerPosition and DefaultTrackerOrientation), and all the buttons and joystick values are set to 0.
TransmitterOffset <X-offset> <Y-offset> <Z-offset> <units>
The position of the transmitter in CAVE coordinates. This is used to adjust the values returned by the trackers.
TransmitterRotation <axis-X> <axis-Y> <axis-Z> <angle>
The orientation of the transmitter in the CAVE space. This is defined by an axis vector and an angle of rotation around that vector. The angle is in degrees.
TransmitterRotationMatrix <m00> <m01> <m02> <m10> <m11> <m12> <m20> <m21> <m22>
Defines the transmitter rotation using a 3x3 matrix, instead of an axis and angle.
Units <units>
What units to use for the CAVE coordinates. <units> should be "feet", "meters", "inches", or "centimeters". Tracking data will be reported in the given units, and the graphics projections will be in those units. "feet" is the default.
WallDisplay <wall-name> <pipe#> [<window-geometry>]
Describes where a given wall's window will be displayed on the workstation. <wall-name> can be one of "front", "left", "right", "floor", "back", "ceiling", "arpafloor", "screen0", ..., "screen7", "dual_eye", "left_eye", "right_eye", "simulator", "simulator1", or "simulator2".
<pipe#> is the number of the graphics pipe used by the wall. Pipe number 0 corresponds to display :0.0, number 1 to :0.1, etc. If <pipe#> is -1, the wall will inherit your shell's DISPLAY variable, rather than redefining it. Also, an X display (e.g. "evl:0.0") can be specified in place of a pipe number; this can be used to display the wall on a remote machine in place of the local hardware.
<window-geometry> defines the area on the display for the window; it is given in the format "XDIMxYDIM+XOFFSET+YOFFSET" (e.g. 512x512+300+100). If the string "window" is given instead of a size and offsets, the wall will be displayed in a normal, bordered window which can be moved and resized. If no geometry is given, it will default to the full managed area of the screen.
WallExitCommand <wall> <command>
Specifies a shell script or program which is to be run before the given wall exits (when CAVEExit() or CAVEHalt() is called). The command will be executed by the wall's display process, after the window has been closed.
WallEyes <wall-name> both|left|right
Indicates for which eyes a view should be rendered on the given screen. The options are "both", "left", and "right". The normal default value is "both", except for the left_eye and right_eye walls, which default to "left" and "right". Also, if "DisplayMode" is mono, then any walls for which both eyes have been selected will be automatically switched to left eye only. <wall-name> should be one of the allowed wall names listed under "Walls" (below).
WallInitCommand <wall> <command>
Specifies a shell script or program which is to be run when the given wall is initialized. The command will be executed by the wall's display process, before the window is created, with the DISPLAY environment variable set to the wall's display. This can be used for automatically changing the video mode of a pipe.
Walls <wall> <wall> ...
Lists which walls are active. <wall> can be one of "front", "left", "right", "floor", "back", "ceiling", "arpafloor", "screen0", "screen1", ..., "screen7", "dual_eye", "left_eye", "right_eye", "simulator", "simulator1", or "simulator2". The display information (see WallDisplay) must be provided for a wall to be used (the system config file should already contain that information for all walls that are physically available).
The "simulator" wall selects the simulator-style display (see Section 7.4. The "simulator1" wall is a simulator display that is always in viewing mode 1; the "simulator2" wall is always in mode 2.
The "screen#" walls are intended for the Immersadesk or any other fixed screen display that does not correspond to one of the canonical six walls. If one is selected, the corresponding "ProjectionCorners" configuration data must also be given.
The "arpafloor" wall produces a projection for the floor of the ARPA Enterprise CAVE, which is rotated by 45 degrees about Y. It also masks out the portions of the window which should not be displayed (after the application's display function is called).
The "dual_eye" wall produces side-by-side stereo images, for the BOOM2 display.
The "left_eye" and "right_eye" walls each produce a monoscopic head-coupled view from the position of the given eye. The are for use with an HMD or BOOM3. The "ProjectionCorners" data must also be given for these walls.
Wand <wand-type>
The type of wand being used. <wand-type> should be one of "mouse", "PC", "logitech", "daemon", "boom3", "simulator", "custom", or "none". This determines what controls are read (buttons/joystick). The "mouse" wand will read the three mouse buttons. "PC" indicates the PC-based wand with three buttons and a pressure-sensitive joystick. The "logitech" option is for the Logitech flying mouse; it should only be used in conjunction with "TrackerType logitech". The "daemon" option is for use with an independent daemon program (such as trackd), which communicates with the library through standard SysV shared memory (see the ControllerDaemonKey configuration). The "boom3" wand reads the controllers on a BOOM; the "Boom3Buttons" configuration option should indicate what type of BOOM controller is being used; this is only available with the special BOOM version of the CAVE library. The "simulator" option will use the mouse and spacebar to simulate the PC wand's buttons and joystick. The "custom" option is used along with the "WandButtons" and "WandValuators" configurations to define an arbitrary set of control devices for the wand.
WandButtons <device1> <device2> ...
Defines the button devices to read for a custom wand (see "Wand" above). The devices should be GL device names, as defined in <gl/device.h>. <device1> will be read for CAVEBUTTON1, <device2> for CAVEBUTTON2, etc. Up to 16 buttons may be given.
WandSensorOffset <X-offset> <Y-offset> <Z-offset> <units>
The offset from the location of the physical sensor attached to the wand to the location which will be reported by the library for the wand.
WandSensorRotation <axis-X> <axis-Y> <axis-Z> <angle>
The rotation of the physical wand sensor relative to the sensor as reported by the library. This is defined by an axis vector and an angle of rotation around that vector. The angle is in degrees.
WandSensorRotationMatrix <m00> <m01> <m02> <m10> <m11> <m12> <m20> <m21> <m22>
Defines the wand sensor rotation using a 3x3 matrix, instead of an axis and angle.
WandValuators <device0> <min0> <max0> <device1> <min1> <max1> ...
Defines the valuator devices to read for a custom wand (see "Wand" above). The devices should be GL device names, as defined in <gl/device.h>. <device0> will be read for CAVEController->valuator[0] (aka CAVE_JOYSTICK_X), <device1> for CAVEController->valuator[1], etc. <min#> and <max#> define the minimum and maximum values that will be returned by the device itself; these values are then mapped to -1.0 to 1.0 when stored in CAVEController->valuator[#]. Up to 16 valuators may be given.

10. Sample Programs

10.1. CAVE sample program 1 (IrisGL)

/* simple.c
/*    A trivial CAVE demo program, demonstrating the most basic CAVE library
/*   functions. This program just draws a red triangle in the front of the
/*   CAVE. No interaction (outside of moving around), and nothing changes.
*/

#include <cave.h>

void simple_draw(void);

main(int argc,char **argv)
{
/* Initialize the CAVE */
 CAVEConfigure(&argc,argv,NULL);
 CAVEInit();
/* Give the library a pointer to the drawing function */
 CAVEDisplay(simple_draw,0);
/* Wait for the escape key to be hit */
 while (!getbutton(ESCKEY))
	sginap(10);
/* Clean up & exit */
 CAVEExit();
}


/* simple_draw - the display function. This function is called by the
  CAVE library in the rendering processes' display loop. It draws a red
  triangle 2 feet tall, 4 feet off the floor, and 1 foot in front of the
  front wall (assuming a 10' CAVE). */
void simple_draw(void)
{
 float vert1[3] = { -1, 4, -4},
	vert2[3] = { 1, 4, -4},
	vert3[3] = { 0, 6, -4};
 cpack(0); clear(); zclear();
 cpack(0xff);
 bgnline();
   v3f(vert1);
   v3f(vert2);
   v3f(vert3);
   v3f(vert1);
 endline();
}

10.2. CAVE sample program 2 (OpenGL)

#include <cave_ogl.h>
#include <GL/glu.h>

void init_shared_data(),gl_init_fn(),draw_fn();

/* Shared data */
float *x,*y,*z;

main(int argc,char **argv)
{
 float vx,vy,vz;
 CAVEConfigure(&argc,argv,NULL);
 init_shared_data();
 vx = (drand48()-0.5)/10.0;
 vy = (drand48()-0.5)/10.0;
 vz = (drand48()-0.5)/10.0;
 CAVEInit();
 CAVEInitApplication(gl_init_fn,0);
 CAVEDisplay(draw_fn,0);
 while (!CAVEgetbutton(CAVE_ESCKEY))
	{	/* Bounce the ball around */
	if (*x < -5) vx = fabs(vx);
	else if (*x > 5) vx = -fabs(vx);
	if (*y < 0) vy = fabs(vy);
	else if (*y > 10) vy = -fabs(vy);
	if (*z < -5) vz = fabs(vz);
	else if (*z > 5) vz = -fabs(vz);
	*x += vx;
	*y += vy;
	*z += vz;
	sginap(1);  /* Make this loop run about 100 iterations/second */
	}
 CAVEExit();
}

/* Get a shared arena, amalloc the shared variables, & initialize them */
void init_shared_data()
{
 x = (float *) CAVEMalloc(sizeof(float));
 y = (float *) CAVEMalloc(sizeof(float));
 z = (float *) CAVEMalloc(sizeof(float));
 *x = 0;
 *y = 5;
 *z = 0;
}

static GLUquadricObj *sphereObj;

/* initialize the graphics - define the lighting data */
void gl_init_fn()
{
 float light_pos[] = { -5, 10, 5, 0 };
 float mat_diffuse[] = { 0.6, 0.5, 0.1, 1 };
 glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
 glEnable(GL_LIGHT0);
 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse);
 sphereObj = gluNewQuadric();
}

/* draw the scene - draw a ball at (*x,*y,*z), and, if button 1 is pressed,
 draw a smaller ball in front of the wand */
void draw_fn()
{
 glClearColor(0.5, 1., 1., 0.);
 glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
 glEnable(GL_LIGHTING);
 glPushMatrix();
   glTranslatef(*x,*y,*z);
   gluSphere(sphereObj, 1.0, 8, 8);
 glPopMatrix();
 if (CAVEBUTTON1)
	{
	float wandPos[3],wandFront[3];
	CAVEGetPosition(CAVE_WAND,wandPos);
	CAVEGetVector(CAVE_WAND_FRONT,wandFront);
	glPushMatrix();
	  glTranslatef(wandPos[0]+wandFront[0]*2,wandPos[1]+wandFront[1]*2,
			wandPos[2]+wandFront[2]*2);
	  gluSphere(sphereObj, .25, 8, 8);
	glPopMatrix();
	}
 glDisable(GL_LIGHTING);
}