The current implementation of the CAVE uses three walls; we can project on the three side walls, or two walls and the floor. The projected images are controlled by an SGI Onyx/RE2 with three graphics pipelines. For testing, you can run the CAVE using one, two or three walls simultaneously. The number of CAVE walls used does not affect your program. The CAVE library automatically determines how many walls you want to use and does the necessary setup when your program starts.
A diagram of the CAVE environment is shown in Figure 1.
NEVER TURN OFF the projectors; this can cause them to go out of alignment.
Use the standby button on the remote control to activate them. Press and
hold the standby button on the back of the projector or the standby button
on the remote control for a couple of seconds. DO NOT TOUCH any other
control that might cause the projectors to go out of alignment.
Once you are done using the CAVE, remember to put all the projectors to
SLEEP by pressing the standby button. The projection tubes have a limited
life span that can be extended by putting them on standby when not in use.
The Indy functions as a "sound server" for the CAVE. Commands are
sent to the workstation over the network, and it then either generates
sounds internally, or controls the synthesizer.
The speakers are located in the corners of the CAVE. They are always turned
on. DO NOT TOUCH any of the controls on the speakers. Everything is
controlled from the synthesizer.
The MIDI interface and synthesizer are located on a rack at the entrance to
the CAVE. To use the synthesizer, you must load a set of dummy instruments
before you can use it from your CAVE program.
As graphics programmers, we are used to the concept of having our
workstation screen be a window enabling us to look out into the scene being
rendered. To see and explore our graphics, we move the window. To
manipulate objects, we provide a set of 2D controls (such as a mouse or a
set of sliders) with an abstract manipulation function assigned to them.
The CAVE presents a different visual paradigm: the inside-out paradigm. In
the CAVE, we are not looking at our scene, we are INSIDE the scene. We can
walk around objects, we can manipulate them and we can travel through the
environment.
This difference is very important to remember when designing applications
for the CAVE, because we can completely immerse a viewer in the scene. We
can develop more intuitive interfaces to manipulate objects, to control the
scene, and to navigate the space than we design for 2D monitors.
Each one of the buttons can be ON (pressed) or OFF (not pressed).
Different functions can be assigned to the buttons: grabbing an object,
flying through the environment, starting a particular activity in the
model, etc.
The CAVE Library has all the functions necessary to create a CAVE
program. It deals with the synchronization of all the CAVE devices, the
synchronization of the walls, the calculations of the stereo transformation
and many other CAVE-specific tasks.
The CAVE Simulator is a library that runs on any workstation which
supports SGI's IrisGL library. The simulator allows a programmer to do
preliminary CAVE application development, place objects in a scene, or
choose the rendering model without having to use the actual CAVE hardware.
The simulator is extremely useful for those developing CAVE applications at
remote sites (i.e., sites that don't have a CAVE handy).
The CAVEviewer is similar to the Simulator, except that it provides
a set of Motif controls and can dynamically load the application-specific
code for a CAVE program. It is intended for distributing demonstrations
of CAVE applications through Mosaic.
You should separate frame calculations from drawing (or display)
procedures, or you should have some internal controls in the code to be
sure the calculations are performed only once for each frame (e.g. only when
your rendering function is drawing the left eye's view (see
Section 5.3.2)).
The new version of the CAVE runs on a multiprocessor SGI Onyx; this one
machine performs all the CAVE tasks. When an application is run, it forks
into several concurrent processes. There is an independent rendering process
running for each wall, plus a computation process to perform calculations.
With this system, some method is needed to communicate changes in data
between the application and rendering processes; normally, forked processes
all have separate copies of a program's data, which means that changes made by
the computation process would not be seen by the rendering processes.
One quick solution is to not use the computation process, but instead to
do all computations in the rendering processes. Unfortunately, this is not
the most efficient solution. The computations are done by the same process
as the rendering, so the advantages of parallelism are lost, and the program
will run more slowly than it could. Also, each rendering process will have a
separate copy of the program's data; if a program uses a large amount of data,
the Onyx may run out of free memory.
The preferred method for CAVE applications is to use shared memory. By putting
data in shared memory, computations can be performed in
parallel with the rendering, with all processes using the same
data. The CAVE library provides a utility function to create a shared memory
arena, from which you can then allocate shared data. Initialization of shared
memory should occur before calling CAVEInit, so that all the processes will
have access to the shared data.
Data that does not change does not need to be in shared memory.
If, however, the data is very large, it probably should be stored in
shared memory anyway, so as not to fill up memory with multiple copies
of the data after the program forks.
When porting an existing application to the CAVE, you should design it
for the CAVE's multiprocessing architecture. Calculations should be separated
from graphics code, so that they can be put in the separate processes. Any
global variables used to share data between calculations and rendering will
need to be in shared memory. This typically involves adding an extra level
of indirection, and then amalloc'ing the variables from a shared arena.
All the walls of the CAVE share the same reference coordinate system, as
shown in Figure 2. 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 values of two
global variables in the CAVE library: CAVENear and CAVEFar. Both are
defined in "cave.h".
NOTE: The names of all CAVE functions, macros, and global variables
start with the word CAVE (CAVEDisplay, for example).
New applications should be developed with the names given in this guide;
programs that use older style names (not documented here) should include the
header file "cave.old.h" in addition to "cave.h".
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.
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 8) 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.
The wand movement controls are as follows:
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.
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:
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,
TransmitterOffset, Origin, CAVEWidth, and CAVEHeight.
The possible keywords and their meanings are:
3. CAVE Equipment
3.1. Overall structure
CAVE hardware should be configured by system and video engineers so it is
usable. Under normal operation, CAVE users should only be concerned with
turning on and off the different components (although, due to ongoing CAVE
research, the hardware configuration could change occasionally, at which
time you should be notified by your system administrator.) The following is
a description of the CAVE equipment.
3.2. Projectors and mirrors
The projectors and the mirrors for the side walls are located behind each
wall. The projector for the floor is suspended from the ceiling of the
CAVE. The projectors are very sensitive to almost everything. It takes at
least one hour to align and calibrate each projector and mirror so they
match at the corners of the CAVE. Please be careful not to move the
projectors or mirrors if you have to walk in that area.
3.3. Stereo glasses
To see the virtual environment in stereo, users wear Stereographics'
CrystalEyes stereo glasses made of liquid crystal. The glasses are very
fragile. In order to see stereo properly, they have to be turned on by
pressing a small button located on the right side of the frame. To turn
them off, press the same button. They will not work if the user is
facing away from the emitters.
3.4. Stereo emitters
The stereo emitters are little white boxes placed around the edges of the CAVE.
They are the devices that synchronize the stereo glasses to the screen update
rate of 120Hz or 96Hz. They are always on. You should not have to do anything
with them.
3.5. Wand
A wand (a 3D mouse) with buttons is the interactive
input device.
Currently, EVL has two wands; both wands use the Ascension Flock of
Birds tracking system, but have different control devices.
The primary wand has three buttons and a pressure-sensitive joystick.
It is connected to the CAVE through a PC which is attached to one
of the Onyx's serial ports. A server program on the PC reads data
from the buttons and joystick and passes them to the Onyx.
The older wand just has three buttons, and is
attached to the mouse port of the Onyx. When using the older wand, be sure
that the mouse pointer is on the main screen (or wherever your DISPLAY variable
points) while the CAVE is running, or your program will not be able to
detect the state of the wand buttons.
3.6. Tracking systems
Currently the CAVE supports various tracking systems. The primary
system is an Ascension Technologies Flock of Birds. An alternative
system, used for the Immersadesk and desktop CAVE systems, is a
Logitech sonic tracker. There are also "simulated" tracking options
available, using either the keyboard and mouse or a spaceball.
The use of one or another is transparent to the CAVE programmer,
since it is defined in the CAVE configuration file. All systems have two
sensors, one for tracking the user's head, and another for the wand.
3.7. Audio system
The audio system components are: an Indy workstation, speakers,
a MIDI interface, and synthesizer.
3.8. Workstation
The current implementation of the CAVE runs using a Silicon Graphics Onyx
with three Reality Engine 2s. Each Reality Engine is attached to a CAVE
wall. When using the 120 hz display mode, the front wall is the "master" for
stereo video synchronization in the CAVE. This means that if you are running
the CAVE without using all the walls, at least the front wall should be active.
4. Designing CAVE Applications
4.1. Inside-out paradigm
Designing a program for the CAVE is a little bit different than designing a
graphics program for a workstation. The programmer must be aware of the
differences in order to produce a high quality application for the CAVE.
4.2. Interactive controls
The CAVE currently has a 3D wand with buttons and a joystick for interacting
with the virtual environment. The wand has a sensor at the front tip that
returns its (x, y, z) position inside the CAVE to the application program.
The sensor also returns three orientation angles: azimuth, elevation, and
roll.
4.3. CAVE programming tools
There are several programmings tools to simplify the task of designing,
implementing, and testing a CAVE application. These tools are briefly
mentioned here and explained in detail in later sections of this manual.
4.4. Stereo considerations
For every frame of an animation, two views are produced, one for the left
eye and one for the right eye. The CAVE library display routine will call
your drawing routine twice for every frame. Do not perform
application-related calculations in the same routine that calls the drawing
commands for a scene.
4.5. Graphics details
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.
4.6. Multiprocessing Considerations
In the previous version of the CAVE, we used up to five SGI machines
connected via ScramNet reflective memory. Four of the machines were
dedicated to running the application for each of the four CAVE walls,
and an additional machine was used to synchronize the other four. Under
this scheme, four separate copies of a user's application were running,
one per machine. Applications ran in serial mode; at each frame they
calculated new data and then rendered it for display in the CAVE.
4.7. CAVE Geometry
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 DeskCorners) to use the
new same coordinate system.
5. CAVE Library
5.1. Overview
A library of C functions and macros has been developed to control the
operation of the CAVE. The CAVE library takes care of all the tasks that
have to be performed to correctly operate the CAVE. CAVE functions keep all
the devices synchronized, produce the correct perspective for each wall,
keep track of which walls are in use, and provide the applications with the
current state of all the CAVE elements. This section describes in detail
each of the routines and macros of the CAVE library.
5.2 CAVE Function Calls
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.
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.
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.
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.
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.
5.3. CAVE macros, variables, and miscellaneous functions
CAVE macros simplify access to the wand information.
The global variables provide various information about the state of the CAVE.
5.3.1 Controller macros
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
ON and OFF are macros defined in "cave.h".
ON = TRUE (BUTTONn is pressed).
OFF = FALSE (BUTTONn is not pressed).
5.3.2 Global Variables
The following are global variables used by the CAVE library. CAVENear and
CAVEFar can be changed by an application. The other variables are meant for
information only; your program should not change them.
5.3.3 Miscellaneous Functions
5.3.4. Macros for the wand tracker
pushmatrix();
CAVEWandOrientation;
CAVEGetWand(wx, wy, wz);
translate(wx, wy, wz);
draw_cylinder(); /* Your routine */
popmatrix();
5.3.5. Macros for the head tracker
5.4. Form of a basic CAVE program
5.4.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.4.2. Program flow
CAVEConfigure(): This routine reads the CAVE configuration file, and
parses argc/argv for any user-specified configuration options.
5.5. Compiling a CAVE program
A program that uses the CAVE should include the file "cave.h" and
be linked with the CAVE library, as well as the GL and math libraries
(-lcave -lgl_s -lm). To use audio functions,
include "vssClient.h" (after "cave.h") and link with the
sound library (-lsnd).
All CAVE include files and libraries are in /usr/local/CAVE/include and
/usr/local/CAVE/lib.
6. CAVE Simulation
6.1 Introduction
The CAVE Library provides options to simulate some or all of the
hardware-specific parts of the CAVE environment. This allows
application developers to write and test code on ordinary workstations,
without requiring constant use of the CAVE hardware.
6.2 Simulated tracking
Simulated tracking is selected by the configuration option
"TrackerType simulator". The controls for moving the simulated head
and wand are given below.
6.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:
6.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.
6.3 Simulated wand controls
The simulated wand controls (buttons & joystick) are selected by the
configuration option "Wand simulator".
6.4 Simulated display
The simulated display is selected by using the "simulator" wall (or
"simulator1" or "simulator2") in the Walls configuration option.
When in wall-view mode (mode 0), the following keys select which wall's
display is rendered:
When using the outside-the-CAVE view, you can move the viewpoint around
with the following controls:
7. 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.
8. 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 .caverc in your
home directory; if it is found, it is read, and any entries there will
override the settings from the default configuration. This means that
your .caverc file 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 file.
<pipe#> is the number of the graphics pipe used by
the wall. 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
(note that the CAVE library will be unable to change the
remote machine's video display mode when this option is used).
<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 screen.
The "simulator" wall selects the simulator-style display
(see Section 6.4. The "simulator1"
wall is a simulator display that is always in viewing
mode 1; the "simulator2" wall is always in mode 2.
The "desk" wall is intended for the Immersadesk; if it is
selected, the DeskCorners 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.
9. Sample Programs
9.1. CAVE sample program 1
/* 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();
}
9.2. CAVE sample program 2
#include <cave.h>
void init_shared_data(),gl_init_fn(),draw_fn(),draw_ball();
/* Shared data */
void *shared_arena;
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 (!getbutton(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()
{
shared_arena = CAVEUserSharedMemory(1024);
x = (float *) amalloc(sizeof(float),shared_arena);
y = (float *) amalloc(sizeof(float),shared_arena);
z = (float *) amalloc(sizeof(float),shared_arena);
*x = 0;
*y = 5;
*z = 0;
}
/* initialize the graphics - define the lighting data */
void gl_init_fn()
{
float light_data[] = { LCOLOR, 1.0, 1.0, 1.0,
POSITION, -5.0, 10.0, 5.0, 0.0,
LMNULL };
float material_data[]={ DIFFUSE, 0.6,0.5,0.1,
AMBIENT, 0.2, 0.2, 0.0,
LMNULL };
lmdef(DEFLMODEL,1,0,NULL);
lmbind(LMODEL,1);
lmdef(DEFLIGHT,1,0,light_data);
lmdef(DEFMATERIAL,1,0,material_data);
}
/* 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()
{
cpack(0xffff80); clear(); zclear();
lmbind(LIGHT0,1);
lmbind(MATERIAL,1);
pushmatrix();
translate(*x,*y,*z);
draw_ball();
popmatrix();
lmbind(MATERIAL,0);
if (CAVEBUTTON1)
{
float wand_x,wand_y,wand_z, wand_fx,wand_fy,wand_fz;
CAVEGetWand(wand_x,wand_y,wand_z);
CAVEGetWandFront(wand_fx,wand_fy,wand_fz);
cpack(0xff00ff);
pushmatrix();
translate(wand_x+wand_fx*2,wand_y+wand_fy*2,wand_z+wand_fz*2);
scale(0.25,0.25,0.25);
draw_ball();
popmatrix();
}
}
/* draw a unit sphere */
#define STEP (M_PI/6)
void draw_ball()
{
float lat,lon,v[3];
bgntmesh();
for (lat=-M_PI; lat<M_PI; lat+=STEP)
for (lon=0; lon<=2*M_PI; lon+=STEP)
{
v[0] = sin(lon)*cos(lat);
v[1] = cos(lon)*cos(lat);
v[2] = sin(lat);
n3f(v);
v3f(v);
v[0] = sin(lon)*cos(lat+STEP);
v[1] = cos(lon)*cos(lat+STEP);
v[2] = sin(lat+STEP);
n3f(v);
v3f(v);
}
endtmesh();
}