FreeVR Library programming in SGI Performer
The Performer FreeVR Library -- an interface of a full-feature rendering system for VR
Features of Performer
- High-level Scene-graph interface
- Efficient rendering algorithms
- 3-D file format loading
- Intersection testing
Online documentation
- iiv -v Perf_GetStarted -- Getting Started Guide
- iiv -v Perf_PG -- Programmer's Guide
- iiv -v PerfRef_CC -- C++ Class Reference Guide
- iiv -v PerfRef_C -- C Class Reference Guide
- xman -- select from:
- (3) Subroutines (Performer_libpf_c++)
- (3) Subroutines (Performer_libpr_c++)
The FreeVR library still:
- handles the I/O necessary for creating a VR experience.
- User tracking
- User input controls
- Visual rendering perspective calculations
- handles the graphics windowing interface
- It does the window placement (and deletion)
- It calculates the proper projection matrix for each window
- It calls the render routine each frame for each viewpoint
- is configurable.
- Can be configured to run almost any type of VR display.
- Can be interfaced to a wide variety of tracker systems.
- Can be adjusted for any placement of screens and trackers.
- Many other features can be controlled.
- leaves how to render the scene to the application developer.
- has a built in simulator display allowing any
FreeVR application to be run on any SGI workstation desktop screen.
This is generally used for development and testing, but also
allows for additional people to join in a multiperson VR
experience.
- allows VR users and developers to share applications and code.
Basic structure of a Performer FreeVR Library application
The Performer and FreeVR Libraries run many tasks in parallel.
The forking of the render processes is handled by Performer
instead of the FreeVR library.
Performer also has other duties that it distributes into separate processes:
Performer library processes:
- Simulation process
- Rendering processes (one for each screen)
- Cull processes (one for each screen)
- Intersection process
NOTE: unlike the OpenGL version of the FreeVR library, Performer
synchronizes between the application-simulation process and the
render process.
FreeVR library processes:
- Input process(es)
- Telnet process (if needed)
Display callbacks are no longer handled through the FreeVR library interface.
Example 0: The simple pure-Performer application (pfex0)
- Creates a simple scenegraph with a cube and a pyramid.
- Demonstrates the four key Performer system functions:
- Requires some effort to open the GLX window
(though not nearly as much as standard OpenGL-X programs).
Example 1: The bare necessities FreeVR-Performer application
- Adds a set of axes to the simple scenegraph.
- Demonstrates the two new Performer system functions:
- pfConfig()
- statistics settings
- Shows how to integrate the FreeVR-PF functions:
- Also checks for the escape key to quit.
Example 2: A (civilized, but) basic pf-FreeVR application (pfex2)
Example 3: A world with some action (pfex3)
Adds the familiar movement to two of the objects in the scene.
- World dynamics are done in the Simulation (main) process.
- Data is passed between processes via Performer's shared memory.
- The additional PF & pf-FreeVR functions in this application are:
- pfMalloc() -- allocation of shared memory
- pfGetFrameTimeStamp() -- get the time of the current frame
- pfDCS -- a node for dynamically repositioning objects
- Example 3 (pfex3_dynamic.c++) is
such an application -- with a slightly more complex world.
Example 4: Rendering objects in alternate coordinate systems (pfex4)

- One of the more difficult aspects of FreeVR programming is
dealing with coordinate systems.
- There is a coordinate system (CS) for the real world,
the virtual world, the head, the hand, each eye, etc.
- The pf-FreeVR Library provides some relief:
- vrPfDCSTransform6sensor() -- changes a DCS matrix to move from
virtual space to where the given sensor is in virtual space
(often used for the wand).
- Example 4 (pfex4_pointer.c++)
uses vrPfDCSTransform6sensor() to render a pointer "attached" to the
wand.
Example 5: Object Selection (pfex5)
Highlights the object touched by the wand.
- Before we can manipulate an object, we need to specify which one.
- By determining where the wand is, we can put it to use.
- The FreeVR Library helps with:
- vrPointGetVWFromUser6sensor() -- tells us where things are
located in virtual space.
- vrEulerGetVWFromUser6sensor() -- tells us how things are
oriented in virtual space.
- Example 5 (pfex5_contact.c++)
lets the user select one of the objects in the virtual world.
- This form of selection is called "selection by contact."
Example 6: Manipulating the world (pfex6)
Allows an object to be grabbed and moved with the left button.
NOTE: quirk in object selection always jumps to highest numbered object
in range.
- Doing something with the selected object.
- The FreeVR Library gives us:
- vrGet2switchValue(1) -- a user controlled input variable
- With this we can tell the world simulation when we want the
object to mimic our movement.
- Example 6 (pfex6_manip.c++)
does just that.
Example 7: Traveling through the world (pfex7)

Uses a Performer application-traversal callback to handle travel input.
- Another common way to "interact" with a virtual world is to move
through it.
- The FreeVR Library provides a collection of utilities to accomplish this:
- vrGetValuatorValue() -- user inputs from the "joystick" on the wand.
- vrVectorGetRWFrom6sensorDir() -- a direction from one of the tracked objects.
- vrPfDCSTransformUserTravelCB() -- a callback that moves from the
real world position to a virtual world position of each user.
- vrUserTravelTranslate3d(), vrUserTravelRotateId(),
vrUserTravelReset() -- functions to specify the relationship
between the real and virtual worlds.
- pfNode::setTravFuncs() -- use a traversal callback function
- Example 7 (pfex7_travel.c++)
lets the user fly through the virtual world.
- There are many ways to fly:
- Point and fly with a joystick (this example)
- Pointing and pressing a button
- Grabbing the world and moving it relative to yourself
- Scaling the world down and up about different points
- etc. etc. etc.
Example 8: Using traversal callbacks to handle behaviour and interactions (pfex8)
The Performer Library provides useful functions:
- pfNode::setTravFuncs() -- use a traversal callback function
Traversal callbacks can be executed in any specific Performer
process:
- PFTRAV_APP -- for operations that modify the scenegraph
and expect the change to be seen in all renderings.
- PFTRAV_CULL -- for operations that modify the scenegraph,
(usually a pfDCS node) but expect each rendering
to have different values.
- PFTRAV_DRAW -- for embedding OpenGL operations inside
the scenegraph.
Example 8 (pfex8_callback.c++)
uses a traversal callback function.
Example 9: Rendering a miniature world (WIM) (pfex9)

Example 9 adds a world-in-minature representation when the middle button is pressed.
Mini-world is attached to the secondwand.
Also shows how to render 3D text in the world.
The Performer Library provides useful functions:
- pfdLoadFile()/pfFilePath() -- Functions to load object models
- pfSwitch -- Node to selectivly render objects
- pfText/pfString/pfFont -- Node & Data classes for rendering 3D text
- pfNode::setTravData() -- Function to pass data to traversal callbacks
Example 9 (pfex9_wim.c++)
demonstrations a world-in-miniature display.
In order to use the 2nd wand in FreeVR, a small addition will need
to be added to you FreeVR configuration file:
inputdevice "simulator-indev" += {
input "sensor6[hand2]" = "sensor6(sim[2])";
control "setsensor(2)" = "switch2(keyboard:key[g])";
}
Example 10: Intersection testing (pfex10)
Sends a ray out from the wand and highlights the object it is
pointing at. A sphere indicates the point of contact, and some
text informs us when we aren't pointing at anything.
The Performer Library provides useful functions:
Example 10 (pfex10_isect.c++)
demonstrations intersection testing in Performer.
Example 11: Scenegraph debugging (pfex11)
Having the world contained in a scenegraph can sometimes make it
more difficult to debug an application.
Thus, having a means to visualize the scenegraph is important.
In this example application, we introduce a source file that
enables the user to view the scenegraph in text form, displayed
on a separate terminal.
- Source file pfdebug.c++ contains all the necessary
routines for rendering a Performer scenegraph in text on
a separate terminal.
NOTE: ideally, these routines would normally be divided
into three separate source files.
- Adds only three new function calls to make use of this tool:
- int pfdebug_initialize_display(char *tty);
- void pfdebug_terminate_display();
- void pfdebug_display_sg(pfScene *scene);
- We now name most of the nodes in our example to make
browsing the scenegraph easier.
- To make browsing a little better, the pfNode::setName() function
is used to assign names to all the nodes.
This example uses the some of the function keys to manipulate control
of the scenegraph textual view:
- But first: need to set the terminal window.
This is accomplished by setting the DBGTTY environment variable.
NOTE: use the "tty" unix command to discover the terminal device
for the window which is to be used for rendering the text.
- F10 - move down a layer
- F11 - move up a layer
- F9 - move left a child (sibling)
- F12 - move right a child (sibling)
- page down - advance down the list of children by 10
- page up - move up the list of children by 10
- '+' - advance down the list of children by 1
-->
- '-' - move up the list of children by 1
- home - move to the first child
- end - move to a page with the last child
- F8 - hide/unhide the current node (toggle)
- backspace - cut the current node from the scenegraph
- ` - paste the previously cut node into the scenegraph
(to just before current node)
- F5 - move the current node one to the left among its siblings
- F6 - move the current node one to the right among its siblings
- print screen - print the scenegraph from the current node down
- Keybindings can be changed at the top of pfdebug_display_sg().
NOTE: In FreeVR, keypresses are (currently) treated as separate
2-switch button presses.
So the configuration file needs to have all the interaction keys added
as button inputs.
To do so, add the following to your ~/.freevrrc file:
inputdevice "simulator-indev" += {
input "switch2[F1]" = "switch2(keyboard:key[F1])"; # 4
input "switch2[F2]" = "switch2(keyboard:key[F2])"; # 5
input "switch2[F3]" = "switch2(keyboard:key[F3])"; # 6
input "switch2[F4]" = "switch2(keyboard:key[F4])"; # 7
input "switch2[F5]" = "switch2(keyboard:key[F5])"; # 8
input "switch2[F6]" = "switch2(keyboard:key[F6])"; # 9
input "switch2[F7]" = "switch2(keyboard:key[F7])"; # 10
input "switch2[F8]" = "switch2(keyboard:key[F8])"; # 11
input "switch2[F9]" = "switch2(keyboard:key[F9])"; # 12
input "switch2[F10]" = "switch2(keyboard:key[F10])"; # 13
input "switch2[F11]" = "switch2(keyboard:key[F11])"; # 14
input "switch2[F12]" = "switch2(keyboard:key[F12])"; # 15
input "switch2[HOME]" = "switch2(keyboard:key[HOME])"; # 16
input "switch2[END]" = "switch2(keyboard:key[END])"; # 17
input "switch2[PRINTSCREEN]" = "switch2(keyboard:key[PAUSE])"; # 18
input "switch2[EQUAL]" = "switch2(keyboard:key[EQUAL])"; # 19
input "switch2[MINUS]" = "switch2(keyboard:key[MINUS])"; # 20
input "switch2[PLUS]" = "switch2(keyboard:key[PLUS])"; # 21
input "switch2[PAGEUP]" = "switch2(keyboard:key[PAGE_UP])"; # 22
input "switch2[PAGEDOWN]" = "switch2(keyboard:key[PAGE_DOWN])"; # 23
input "switch2[BACKSPACE]" = "switch2(keyboard:key[BACKSPACE])";# 24
input "switch2[ACCENTGRAVE]" = "switch2(keyboard:key[GRAVE])"; # 25
}
Example 12: Creating geometries by hand (pfex12)
Example 12 gives an example of how to create a pfGeoSet and pfGeode
through code (rather than existing Performer library calls).
In this case, we create a cube of dimensions +/-1 in X,Y & Z -- ie.
twice as big in each dimension as the standard Performer cube object.
Example 12 (pfex12_geode.c++)
demonstrations how to build a pfGeoSet/pfGeode by hand.
Example 13: Adding texture maps to a pfGeoSet/pfGeode (pfex13)
Example 13 gives an example of how to create and assign a texture map
for a pfGeoSet and pfGeode through code (rather than existing Performer library calls).
In this case, we reuse the cube of dimensions +/-1 in X,Y & Z from pfex12.
Example 13 (pfex13_texture.c++)
demonstrations how to add a texture to a pfGeoSet/pfGeode by hand.
Last modified 28 October 2003.
Bill Sherman, wsherman@ncsa.uiuc.edu
This tutorial code, images and documentation are Copyright © William R. Sherman, 2004.
All rights reserved.