
Abstract This paper describes DIVER, a Virtual Environment (VE) development system which transparently distributes rendering and input processes, implicitly decoupling the application computations from the rendering computations. DIVER provides the developer with a familiar C-library module, which runs on a remote workstation. One complex system, Alice [PAUS93], has already been developed on top of DIVER. DIVER's graphics-independent module spawns server processes on other machines and communicates function calls from the application using Remote Procedure Calls (RPCs). To the developer, there appears to be one thread of control, when actually several asynchronous processes are involved. This abstraction relieves the developer from the responsibility of managing multi-process control, asynchronous communication and multi-process information queuing.
By implicitly divorcing the application from the rendering processes and placing the input devices on the rendering side, DIVER maintains an immersive environment for the user, while the application may run at slower or more aperiodic rates. Because the input devices (head trackers, gloves, etc.) feed directly into the rendering database, the user receives immediate updates as he or she turns around, even if the application is busy performing animation computations to update objects in the scene. The glove data goes directly into the rendering database as well, rather than passing through the application, and as a consequence, responds immediately to user actions. This creates a low-level response, much like dedicated hardware tracking of a two-dimensional cursor.
DIVER also provides an extended hierarchical graphics database, letting the developer worry less about maintaining objects and more about manipulating them. A significant amount of VE application code involves transforming objects within the virtual world. In DIVER, programmers may transform objects with respect to any other object's coordinate system (e.g. place a lamp at (0,0,1) above the table, regardless of where the table is in the room). DIVER allows the programmer to place the virtual camera (and any other input devices) anywhere in the tree. This allows the developer to:
Here is a dummy application which creates a simple Virtual Environment -- the user can look around in a "room" and see their virtual glove. There is no real application running.
When the application programmer makes the VR_Init() call, the function transparently spawns off a process called DIVER on one of the two rendering workstations. Because we are using stereo, DIVER spawns a second process to handle the other eye on a second rendering workstation. The VR_Associate() call sends an RPC over to DIVER, instructing it to start a tracker server process -- this process continually reads tracker data so that DIVER may request the most current information quickly. The VR_InitGloveDefault() call creates a glove as an animated three-dimensional hardware cursor, directly in the rendering database. The second VR_Associate() call attaches a tracker to the glove. Since the tracker process is already started, this results in an RPC that is propagated to the tracker server, telling it to initialize a second tracker and send back two trackers worth of information. The last call, VR_LoadSubtree() is also an RPC to DIVER, instructing DIVER to read in a hierarchical world from disk.
Although transparent to the user, the following process configuration has been created:
This application creates a hierarchical graphics object database in DIVER. Programmers can access this database using returned handles (note the hand variable, a DIVER handle into the tree), or by asking for handles by name (e.g. VR_GetNodeByName()). With these handles, programmers can change the attributes of objects, or manipulate the structure of the tree itself:
For example, if a bird object were flying under program control, repositioning the camera and the hand nodes in the tree as children of the bird would suddenly give the user a bird's eye view of the world.
The programmer can also transform objects in other objects coordinate systems, without altering the tree structure. For example, the programmer can translate a lamp to a location above a table but not change the connectivity of the tree, simply by moving the lamp to (0,0,1) in the table's coordinate system. This works regardless of where the table is in the room or in the tree hierarchy.
DIVER is currently being used as a substrate for a Virtual Environment Management System (VEMS) called Alice, which emphasizes rapid prototyping. Alice links in the DIVER C-library and runs on a SUN, but is actually written in C++ and an interpreted language called Python [GUID93]. DIVER is also being used in conjunction with the UVa Psychology Department for collaborative research, providing a synthetically controlled environment for psychology experiments. Vision researchers at UVa are also using DIVER for active-vision/robotics research.
By providing a C-library of VE function calls, the programmer does not perceive the DIVER system as a set of asynchronous processes running on multiple machines, but as a single process. This model makes it very easy to add more processes to the system, unbeknownst to the programmer or user. It also provides a graphics-independent firewall for the application. The C-library can easily be ported to new architectures.
Note that this is different from other systems, such as the MR toolkit [GREE91], where it is the responsibility of the programmer to spawn computation servers. Programmers may not understand the distributed model, or want to deal with the extra programming burden. Informally, we have observed graduate student users ignore the distributed capability with systems like the MR toolkit. With DIVER, this has been abstracted away from the programmer, and occurs implicitly as the result of RPC calls. In fact, it is almost impossible to not benefit from the separation, since the rendering frame rate happens behind the programmer's back. The application is free to run at slower or more aperiodic rates, while the rendering process maintains an immersive environment for the user.
DIVER differs fundamentally from this model. The computations are performed in the application's domain, keeping the rendering tree as light as possible. Each node in the tree represents the attributes of a graphics object: color, visibility, a transformation matrix and a list of geometry to render, etc.. Children inherit these attributes, so for example, if a node is invisible, that subtree is not traversed (allowing programmers to maintain multiple worlds or multiple representations of objects without incurring render-time costs). DIVER extends this model in two ways: specifying transformations in any coordinate system, and allowing camera placement anywhere in the tree.
Each node in the tree represents its own coordinate system, nested along the ancestor-path.
For Virtual Environment applications, it proves useful to be able to transform objects with respect to other object's coordinate systems, without changing the structure of the tree.
In the introduction section, we provided an example of moving a lamp on
top of a table, using the table's coordinate system. Another useful case
involves moving the camera (where the user is looking) in the direction
the user is pointing with his or her finger. By moving the
virtual camera forward relative by some amount in the coordinate system
of the user's hand, the user flies in the direction he or she is
pointing. DIVER transformation calls provide parameters which specify
what coordinate system to use, and whether to transform the object by some amount (relative) or to some absolute coordinate. For example:
VR_Translate (table, VR_ABSOLUTE, lamp, position);
This moves the lamp to an absolute position in the table's coordinate system. DIVER accomplishes this by replacing the lamp's transformation matrix with the matrix derived from ascending the tree, then descending the tree into the table's coordinate system.
As Robinett and Holloway state [ROBI92], if an object node is repositioned to become a child of the hand tracker node (when the user grabs an object), then when the hand moves, the object moves implicitly. In DIVER, this directly implies that the object moves along with the hand at the rendering frame rate, not the application computation frame rate.
DIVER supports a general function, MoveSubTreePreservingPosition(), which allows programmers to reposition an object in the tree, but not have its position or orientation change as the result of inheriting a new set of transformation matrices. This call is also useful when flattening hierarchical objects, but not changing their appearance in the Virtual environment. Thus an interactive user can detach a sub-component and attach it to another object without realizing that the object has changed inheritance chains.
This abstraction allows the programmer to create offset nodes, which adjust for tracker placement on the user's head (trackers are usually worn on top of the head, rather than exactly on the eyeballs). It also allows the programmer to create a "vehicle" node, so that the program can move the user while the user is still able to move and turn around within that vehicle. The program can place the user inside a car and, update the car's position while the tracker on the user's head concurrently updates the camera position and orientation within the car. If the vehicle is scaled to be smaller, then the camera shrinks, and like Alice in Wonderland, the user finds his or her movements very small with respect to the world.
Additionally, the camera can be attached to other objects in the scene. By repositioning the camera as a child of a moving object, such as a bird flying in the scene, the camera inherits the bird's position, effectively becoming a bird-cam, allowing the user to ride along with the bird.
And just as the camera can be placed underneath objects, objects can be placed underneath the camera. This allow the programmer to create Head Up Displays(HUDs). Objects such as dashboards, selection cross-hairs and interactive sliders may be placed directly in the user's view, simply by making them children of the camera.