4. ImmersaDesk and Other Hardware
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
- User wear CrystalEyes stereo glasses - turn them on by
pressing a small button located on the
right side of the frame.
- Stereo emitters are placed behind the screen.
- Immersadesk can use various tracking systems, that
the CAVE uses. There are two primary systems:
- Logitech (ultrasonic)
- Ultrasonic emitter is placed on the top of the screen
facing user.
- Head tracking: Special CrystalEyes glasses with
tiny microphones.
Notice that if user bend to much over the screen, and
he is not facing the transmitter anymore he can
get out of range. Green LED light on the Logitech
hardware will indicate this situation. There are two
solution to this problem:
- User should sit on a chair while using Immersadesk.
Then he will tend to look up.
- Use CrystalEyes glasses with following
modification (see fig xx). Receiving microphones, are mounted on a
small plastic triangle which is attached to the glasses
under 45 degree angle. In this case, even when user bends
over the screen, receiving microphones are still
facing transmitter.
Figure 3: Logitech glasses modification
- Hand tracking: Logitech "flying mouse" is used
as a wand. It has 4 buttons - 3 in front, and 4th
on left and right side of the mouse. Notice that
you cannot point with this wand to the back, since
microphones, that are mounted in front of that mouse
will not face transmitter.
- Flock of Birds
Same as in the CAVE.
This page unintentionally left blank.
II. Software
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.
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
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(id,pos)
- CAVEGetOrientation(id,or)
- CAVEGetVector(id,vec)
- CAVEGetSensorPosition(sensor,coords,pos)
- CAVEGetSensorOrientation(sensor,coords,or)
- CAVEGetSensorVector(sensor,id,vec)
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.
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.
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.
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.
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.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
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).
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:
- float x,y,z - the position of the sensor
- float azi,elev,roll - the orientation of the sensor (Euler angles)
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:
- int num_buttons - number of buttons on the controller
- int button[] - state of each button
- int num_valuators - number of valuators on the controller
- float valuator[] - state of each valuator
CAVENETID - a unique ID for a networked CAVE user
CAVE_USER_ST - a structure containing data for a networked user.
The structure entries are:
- CAVENETID id - the user's ID
- float timestamp - the last time data was received from this user
(in local CAVE time)
- int num_sensors - the number of tracker sensors
- CAVE_SENSOR_ST sensor[] - the user's tracking data; sensor[0]
contains the head data; the remaining entries contain the
data for the wand and other tracked devices
- CAVE_CONTROLLER_ST controller - the user's controller data (buttons
& valuators)
- void *app_data - a pointer which can be used to store application
data associated with the user. The library does not touch
this entry except to zero it when a new user is initialized.
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:
- int num_sensors - the number of sensors being tracked
- CAVE_SENSOR_ST *sensor[] - the data from the tracker sensors
- CAVE_CONTROLLER_ST *controller - the controller status
- float *time - the current CAVE time; this is updated once per
display frame
- float *framesPerSecond - the current frame rate
- int *numUsers - the number of networked users
- CAVE_USER_ST **user - an array of pointers to the networked
users' data
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.
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).
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.
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.
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
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.
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.
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.
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
/* 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();
}
#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);
}