FreeVR: Virtual Reality Integration Library |
|
FreeVR: VR Administrator's GuideFebruary 29, 2024 for FreeVR Version 0.7eWritten by Bill Sherman Introduction
One of the primary features of FreeVR is the configuration flexibility. However, this boon can be the bane of the person responsible for administering the VR system. Flexibility necessitates a somewhat complex configuration file. The plan for the future is to aid the administrative process with GUI and other tools that can create a config file based on a set of parameters. For the moment however, the creating of the configuration file is largely a manual process, though some assistance can be garnered by looking at the freevr.bnf file (which describes the "language" of the configuration file). There are also many example configuration files in the etc directory named rc_sample_... something. Each has a specific flavor — the rc_sample_ks is the "Kitchen Sink" version which just has a lot of examples. One tool that is available to help debug configurations as well as the library and some applications is the ability to communicate with a running FreeVR application via a socket port. This provides administrators and developers the ability to telnet or otherwise connect to a socket and view and manipulation information on the running FreeVR application. This process is described in the auxiliary Telnet/Socket Interface Manual. Another aid for honing a configuration file is to use what we will refer to as a "live" simulator view — a simulator window with live tracking. The Default ConfigurationFreeVR contains a default configuration that provides some reasonable control and display features for testing a FreeVR application on a typical desktop environment (keyboard, mouse & monitor). This will have a simulator view under user control. The inputs of the default configuration, and some of the configurations that can be specified operate in a way that mimics the presence of body-tracking hardware to allow the program developer to move the user's head and hand(s) (with wand(s)) around as well as push buttons, and move the joystick. The default system assumes a typical CAVE[tm] hardware setup, which includes two body trackers (head and wand), three buttons, and a 2-valuator joystick. The typical CAVE working volume is a 10x10x10' cube. The primary key to know is the '?' (or '/') key. Pressing this key should display to the shell a usable collection of information to allow you to control the simulated inputs. The rest of the default inputs is:
The simulated window also has some controls to change the view into the virtual world:
The Configuration FileFreeVR currently looks in three places for configuration files that can override the built-in default configuration. Each subsequent file can override the previously read configuration files, so order matters. The order is:
When the FreeVR distribution is first untarred, it is very likely that none of these files exist (the distribution does not contain any file called .freevrrc. However, one of the sample configuration files (rc_sample_<type>), can be copied or renamed to .freevrrc to run in some way other than the default. There is one file ("freevrrc_home") that is an example of what one might put in the "~/.freevrrc" file which is a common place for users to make custom tweaks to the overarching configuration -- especially users who are trying lots of configuration styles. The file "freevr.bnf" (in the source directory) is an extended Baukus-Naur Form (eBNF) describing the syntax of configuration statements. Legal configuration files should fit this form. All the available, and currently planned options for each of the statements are listed in this file. Options that are not yet implemented are denoted by the ASCII frowny-face icon (":-("). The basic syntactical form is a group of statements that define configuration "objects" that can be linked together to describe a complete "system." The generic format of an object description is: <object type> <object name> { "=" | "+=" } "{" <option 1> { "=" | "+=" | "*=" } <option settings> ";" ... "}"Where <object type> is one of: system, proc, window, eyelist, user, inputdevice, inputmap, or prop. The <object name> can be any valid string. The assignment operators ("=", "+=" or "*=") signify what type of assignment should take place. The basic equals ("=") assignment will override any previous value for the object type or option. The plus-equals ("+=") will append or add the given value to the object description, or option value. The times-equals ("*=") assignment is only valid for options with numeric values (including arrays and matrices), and will multiply the current value by the given value. A comma-separated list of <option settings> is terminated by a semicolon. Statements other than object definition statements are also possible in the configuration description. Such statements include the usesystem statement, the set and setdefault statements, plus some miscellaneous commands such as echo and readconfig.
Separation of tokens in the configuration description is based only
on generic whitespace.
The use of newlines or tabs is never required.
if <condition> then <statement> [else <statement>]where:
Thus portions of the configuration can be skipped or included based on factors external to the application and perhaps changing for particular circumstances — such as using monoscopic rendering for a VR photo-shoot. Possible variables and functions available within the configuration system are discussed in the following two sections. One example of where a conditional might be used in a configuration file meant to augment earlier configurations (and therefore can't know a-priori whether, in this case, a "telnet" process is already in the "system" configuration): # Add a "telnet" process to the current system if there isn't one already if (numberprocsoftype("$system, telnet") == 0) system $system += { procs += "default-telnet"; }Note: configuration functions will be described below, but notice that the configuration process only provides a single argument to the function, so even though it takes two arguments, they are provided as a single string which is split by the function handling routine. Configuration VariablesOne of the unique features (among VR libraries) of FreeVR is the ability to set and make use of variables in the FreeVR configuration file. The two primary uses of configuration variables are 1) allow a single setting to be used in multiple places within the configuration (including in mathematical expressions); and 2) for conditional settings within the configuration file, using the conditional statement. Another use can be for outputting information about the system to the terminal during initialization. Configuration variables are of two types: built-in system variables, and standard shell environment variables. The environment variables may be set in the shell prior to running the VR application, or they may be set within the config file itself with the "setenv" command. The built-in variables are typically used by the VR administrator to create a configuration system that can respond to specific conditions of the FreeVR setup. Some variables are not useful within the configuration, but can be used in the socket/telnet interface. Here is the current list of built-in variables
Variables are used by prepending their name with the dollar symbol ($). As of FreeVR Version 0.7e, string concatenation has been fully tested, and can be used to combine variables with other strings. (This feature existed in previous versions, but didn't work in all circumstances.)
Note that a complete list of debug levels can be found in the debug.1fv manpage.
There are a limited number of functions available in the FreeVR configuration parsing system. Two that run external tools and return a result, two that report whether there is a type of inputDevice or Visren process object defined in the configuration file (up to that point), a similar one that reports the number of a type of process in the given system configuration object, and one that converts numbers into strings (for the purpose of string concatenation). Specifically:
The use of these functions would mostly be in the circumstance where a personal configuration file ("~/.freevrrc") alters the configuration based on how the general system configuration files have been parsed. The string() function was created to make string concatenation work better when involving numerical values, though the concatenation operation itself was improved to handle numbers, diminishing the need for string().
Note: Because of the way the configuration parsing is handled, these
configuration-functions only take a single argument, but in the case of
numberprocsofType, there actually are two arguments, but they are
passed together as a single string, which is then divided inside the
function itself.
The basis of FreeVR configuration is a collection of config-objects related in a directed graph. There are eight defined types of objects within the graph, two of which presently have very limited configurability ("Input-map" and "Prop"). The general organization of the directed graph is: System / \ / \ / \ Processes Input-map / | \_ \ (Vis-Ren) (Input) (Telnet) \... Windows Input-devices | Eye-list / | \ Props Eye Eye Eye ... \ / \ User User
A VR "system" is built up from objects describing a portion
of the overall layout.
Multiple systems can be defined in the same configuration file.
The usesystem config statement is used to select from among
the available systems.
Several systems are defined in the example freevr.rc file.
The system object is used to bring together the main components that describe how the current hardware to be used. The system object links to a group of process objects, and a single inputmap object. The system object has options for running an independent program at the beginning and end of FreeVR operation. These can be any program commands that might be typed into a shell. These options are: ExecAtStart and ExecAtStop. The are followed by the equal sign (=) and a string with the command (usually within quotation marks). Prior to execution, the string will replace the constants #N, and #P with the name and process-id of the system respectively. These two options are also available on a per-process basis. One possible use of these is to ensure external tracking is operational before starting, and perhaps putting it into hiatus when stopped. If problems with the configuration are encountered during system initialization an error flag is set for each problem. Each flag is assigned a separate bit in the overall error code, and when any error is encountered, a shell command can be executed, and/or the system can prematurely terminate operations. To execute a command, the ExecUponError option can be set to a string in the same manner as ExecAtStart and ExecAtStop. The ExecUponError option has one additional string replacement — a #E in the command will be replaced with the decimal representation of the error code. This is useful to warn the user that the system may not be functioning properly. A related option is the ExitUponError which takes a bitmask of the error types and if there are any errors, and the error code anded with the bitmask is non-zero, then the system will exit immediately — because things probably weren't going to work anyway. The ExitUponError value is set by given an equal sign and a number. [NOTE: ideally this number can be specified in hex or binary, but a bug in the current config parsing prevents this — or maybe it would work if given inside quotations.] The types of errors currently encoded in the startup error code (and their bit values) are:
system "cave" = { procs = "cave-visren1", "cave-visren2", "cave-visren3", "cave-visren4"; inputmap = "default"; exituponerror = 2; execuponerror = "xmessage 'Problem found in the configuration — #E' > /dev/null"; execatstart = "xmessage 'Visbox system #N starting. PID is #P' > /dev/null &"; execatstop = "xmessage 'Visbox system #N stopping.' > /dev/null"; } The "process" ObjectA FreeVR process is an operation that can be forked off of the main process and run independently. FreeVR processes are each defined to serve a particular role in the execution of the application. Thus far, the process types that have been implemented are: the visual rendering process (visren) and the input device communication process (input). The two main options that need to be set for all processes are the <process type> (chosen from above), and a list of objects associated with the process. For visren processes, these should be window objects, and for input processes, they should be inputdevice objects. These are the only two options that are mandated. Three other useful options are the sync, execAtStart, and execAtStop options. The execAtStart and execAtStop options work the same as their counterparts in the system object configuration. For debugging purposes, there are three additional useful options: printcolor, printstring, and printfile. These all affect how (and where) text printed by the process will appear. The printcolor option is used to specify a number that fits in the ANSI terminal sequence for changing the text color on that terminal. Any ANSI mode number can be used. Some common ones are:
setenv ANSI_BLUE 34; [...] process "visren-default" += { printcolor = $ANSI_BLUE; } The printstring option is used to specify a string that is prepended to all messages printed by the process. (For example, the name of the process.) The printfile option is used to specify a file that all messages should be sent to instead of the startup-terminal. Note that if a terminal device is given, then the text will appear on that terminal instead. In fact, this was the initially desired use. Note that the three print options can be used together in any way the configurer desires. A basic visual-rendering "process" object definition might appear: process "cave-visren2" = { type = visren; objects = "left-cave"; # the left screen of the CAVE sync = 1; # sync with all processes that use barrier #1 printcolor = $ANSI_BLUE; # print in blue printstring = "visren2: "; printfile = "/dev/console"; } A basic input "process" object definition might appear: process "xwinsim-input" = { type = input; printcolor = $ANSI_MAGENTA; objects = "xwinsim"; #DebugThisToo = $debug:xwin; # uncomment for Xwindow debugging details } The "window" ObjectA window in FreeVR is the basic means of presenting the visual aspects of a virtual world to the user. At the moment, the type of graphics rendering that will occur within the window needs to be defined for each window (eg. "glx", "java3d", "performer"). At the moment, only type "glx" is implemented. It is uncertain if this choice will continue to be specifically linked to the window definitions. The only bits of information that are dependent on the type of graphics rendering are display options such as multi-sampling, and the method of determining which physical screen to render to (although on UNIX systems, the X-windows method can generally be assumed). The type of graphics window is set via the graphicstype option. Window objects can be of four major types: in a fixed position in the real world, as is typically of CAVEs and other projection-based VR displays; attached to a user's head, as is the case for HMDs and BOOMs; attached to a user's hand, a less common but valid form of display; and finally as a "simulated" view into the virtual world. Currently, the only three types of window displays that have been implemented are the "fixed", "headmount" and "simulator" types. The nature of the window is set with the mount option, with potential values of: fixed, fixed, handheld and simulator. Currently, the "headmount" option has not been thoroughly tested, but at least partially works. The position of the window in the real world, relative to the (RW) origin, is specified using the rw2w.translate, rw2w.rotate and rw2w.transform options. (NOTE: formerly, an underscore ('_') was used to link rw2w with the type of transformation, but to make configuration files more readable, and follow a more programmatic style convention, dots ('.') are now used — though for now, the underscore will still work.) Operations on the rw2w (ie. real-world to window) transformation matrix can be combined using the "*=" operation. Each method of transforming the matrix takes a different number of arguments. The rw2w.translate option takes three numbers — an X,Y, and Z displacement. The rw2w.rotate option takes four numbers — an axis specified by X,Y, and Z, followed by a rotation in degrees. The rw2w.transform options simply takes the 16 numbers that make up the complete matrix, in column-major ordering. The eyelist and visrenmode options control how rendering to multiple eyes is handled. This is explained in more detail under the description of the eyelist objects. Finally, window objects can have arguments specific to the type of rendering format being used (eg. "glx"). The args option is basically just a long string that is passed to the window-specific routines, and parsed therein. To keep things short, the three major window-argument options for "glx" are display, geometry, and decoration. The display and geometry arguments are the basic values for X-windows, and decoration should be set to either "none" or "title." Here is a typical description of a window: window "left-cave" = { GraphicsType = "glx"; args = "display=:0.0; geometry=1024x768+0+0; decoration=none"; # These two options specify where the window is located in the RW mount = "fixed"; rw2w.translate = 5.0, 0.0, -5.0; rw2w.rotate *= 0.0, 1.0, 0.0, 90.0; # Note that by defining the eyes and visrenmode to "default" will # specifically override any settings in the "system" option, so # they should only be included when necessary. # eyelist = "default"; # visrenmode = default; } The "eyelist" ObjectThe FreeVR eyelist object is the key to making the system extremely flexible with regards to the many ways stereoscopic and multi-user rendering can be configured. The object however is nothing more than six lists of eyes, where each eye specifies a user; the left, right or other eye of that user; and a colormask (which defaults to full color when not specified). The six lists correspond to four different visual rendering modes that can be selected by the visrenmode option of a "system," "window," or the global default. If no visrenmode is specified for a window, then it uses the default value for the system. If no visrenmode is specified for the system, then the global default is used. If no global default visrenmode is specified by the configuration, then the library default is used, which is the eyelist named "default." The four choices for visrenmode are: "mono," "dualfb," "dualvp" and "anaglyphic." The selected visrenmode choice then determines which of the lists of eyes will be used for rendering. For "mono" rendering, the "monofb" list is used. For "dualfb" (ie. dual frame buffer) rendering, the "leftfb" and "rightfb" lists are used. For "dualvp" (ie. dual view-port) rendering, the "leftvp" and "rightvp" lists are used. For the "anaglyphic" method, the "anaglfb" list is used. The format of specifying an eye is: <user-name>:<eye-type>:<colormask> — with the final colon, and colormask being optional. Choices for eye-type are: "lefteye," "righteye" and "cyclops." The latter is best choice for monoscopic rendering. These two examples will hopefully shed some light on this: eyelist "default example" = { monofb = default:cyclops; leftfb = default:lefteye; rightfb = default:righteye; leftvp = default:lefteye; rightvp = default:righteye; anaglfb = default:lefteye:red, default:righteye:blue; }; # The following eyelist renders stereoscopic images to two tracked # people by combining the dual frame buffer method of stereo with # anaglyphic rendering. eyelist "specialA" = { leftfb = default:lefteye:blue, user2:lefteye:red ; rightfb = default:righteye:blue, user2:righteye:red; }; The "user" ObjectCurrently, the Freevr "user" object is used to specify just two things. The most important of these things is the interocular distance. The other is the color the user's head will be represented as in simulator display windows. Here is an example: user "greenuser" = { iod = 0.5; # interocular distance (in feet) color = 84, 134, 72; # a forest-green hue }Note: the "iod" can be altered via the telnet process, as described in the Telnet/Socket Interface Manual. The "inputdevice" ObjectInput devices are one of the keys to providing a flexible VR library. A handful of input devices have already been integrated into FreeVR, with instructions for how developers can add new ones as needed — though because FreeVR can read VRPN and Vrui's Device Daemon, most devices are already covered. One of the goals of input configuration is that the application shouldn't have to know what types of inputs are available, in fact, applications should be runnable in as many environments as possible, and thus input devices need to be able to mimic other devices. The main option of inputdevice specification is the type, The authoritative list of possible types can be found in the vr_input.opts.h file. This is the file used by FreeVR to access the routines necessary for communicating with the input device based on the type selected. An unauthoritative list of inputs types can be found in these web pages, and in the freevr.7fv manpage. Input devices providing 6-dof position input information have two matrix transformations used to describe the relationship between the input hardware and the real-world. The "t2rw" transformation describes the relationship from the hardware's transmitter to the real-world origin. the "r2e" transformation describes the shift from the hardware's receiver to some entity (a user's bodypart, or some prop). The operations (t2rw.translate, t2rw.rotate, t2rw.transform, r2e.translate, r2e.rotate and r2e.transform) are all specified in the same manner as "rw2w" is for windows (above). As with windows, a device independent argument string can be set with the args option. Arguments typically at least include the information necessary with communicating with the device — network ports, serial ports, baud rates, shared memory keys, etc. The source code of many of the input devices lists the available arguments at the comment at the top of the file. Also, the example rc_sample_<&ellipses;> files include "inputdevice" object for each of the implemented input devices. Finally, "inputdevice" object must specify the inputs available from that device, and how those inputs are related to the physical hardware. The input and control options are used to specify this information. The format of the input option is: "input" <arbitrary input name> = <input type>(<input source>);Where, the "arbitrary input name" is the string that will be used to describe inputs to the user in the help-screen, and to specify a particular input in "inputmap" objects Input type is one of "2switch," "valuator" or "6sensor"; and input source is what part of the hardware (or virtual device) should be used to generate the input. For example, for the magellan 6-dof input device, a button press input can be generated by pressing the "1" button on the hardware using: input "first magellan button" = "2switch(button[1])"; The format of the control option is: "control" <device control> = <input type>(<input source>);In this case, the "device control" is a specific string that is particular to the type of input, and activates some internal change in the input device. For example, to get the "magellan" device to reset the current 6-dof sensor when the "8" button is pressed: control "reset_sensor" = "2switch(button[8])";In this case "reset_sensor" is defined in the "magellan" source code, with a list of possible controls given at the top of the file. Here is a simple "inputdevice" object example for the Magellan. For a more complete example, see the rc_sample_6dof file: inputdevice "mag-default" = { type = "magellan"; args = "baud= 9600; port = /dev/input/magellan"; t2rw.translate = 0.0, 4.0, 0.0; r2e.rotate = 0.0, 0.0, 1.0, -90.0; r2e.translate *= 1.0, 0.0, 0.0; input "2switch[1]" = "2switch(button[1])"; input "2switch[2]" = "2switch(button[2])"; input "2switch[3]" = "2switch(button[3])"; input "valuator[x]" = "valuator(mag[tx])"; input "valuator[y]" = "valuator(mag[ty])"; input "6sensor[0]" = "6sensor(6dof[0], r2e)"; input "6sensor[1]" = "6sensor(6dof[1])"; input "6sensor[2]" = "6sensor(6dof[2])"; control "toggle_space_limit" = "2switch(button[4])"; control "toggle_relative" = "2switch(button[7])"; control "next_sensor" = "2switch(button[5])"; control "reset_sensor" = "2switch(button[6])"; control "toggle_valuator" = "2switch(button[8])"; } Here is a table of the current list of controls available to various input devices:
The "inputmap" Object
In the current version of FreeVR, the "inputmap" system has not yet
been implemented, with the exception of a couple of hard-coded settings.
For now, the only inputmap that should be selected in the
system configuration objects is the "default" map.
In this mapping, every (non self-control) input from all the input devices
in the selected system are included in the mapping.
They are simply ordered by the order of the devices as they
appear in the objects list of the input-processes, and the
order in which they appear in the input options within
those devices.
How props will be involved in FreeVR has not been completely
worked out yet, so little will be said about them in this draft.
Very little.
Once again, let me reiterate that the boon of FreeVR is the wide range of VR setups that can be configured — all at runtime. Yet, this means the configuration description will be a little complicated. Hopefully we'll have some more tools to aid in this process in the near future, but for now, it's easiest to work from one of the provided examples. Good luck. (But the best advice is to start with an example configuration and work from there.) © Copyright William R. Sherman, 2024. |