Vrui's overriding goal is to support development of correct, portable, and usable applications. In this context, portable means that an application is developed in one environment – typically a desktop environment – but runs correctly in any environment. Usable means that a Vrui application run in a desktop environment is exactly as effective as a native desktop application, and that the same Vrui application run in a CAVE or other immersive environment type is exactly as effective as an application developed natively for that specific environment.
Unlike glut, Vrui is portable to non-desktop environments. Here, portable means that a Vrui application is only written once, and then runs in any environment, without even having to be recompiled. This portability is achieved by shielding application writers much more from the underlying display system as glut does: Vrui applications do not have to open their windows or set up their OpenGL rendering contexts, and they do not directly receive input from mouse or keyboard. In fact, handling of input is probably the biggest difference between Vrui and glut (see How do I receive input from the keyboard?).
An additional difference between Vrui and glut is that Vrui consists of an entire hierarchy of layered libraries that work together to support developers in writing correct, portable, and usable applications. For example, Vrui contains a comprehensive cluster-transparent file I/O handling library, explicit high-performance intra-cluster communication, a comprehensive library for affine and projective 3D geometry, OpenGL support classes supporting generic programming, an OpenGL-based GUI widget set, and a scene graph library. While all these are completely optional, and Vrui is intentionally designed to be as compatible as possible with third-party libraries, developers are encouraged to use the highest-level available abstractions provided by the entire Vrui package.
Ignoring everything else, though, the widget set offered by Vrui is comparable to those offered by Qt or Gtk+ or other 2D GUI toolkits, albeit not as complete (yet). Vrui's GUI widgets are three-dimensional, since they are intended to work in a virtual three-dimensional display space, but their functionality and layout is very similar to 2D GUI widgets. There are dialogs, menus, buttons, sliders, etc., just as usual. From a programming point of view, Vrui's GUI widgets more or less follow the approach of OSF/Motif, in that they primarily rely on automatic layout based on a hierarchical description, and on (C++-style) callbacks to connect widgets to application behavior. One could say that Vrui's widget set is a complete rip-off of OSF/Motif, translated to C++ and OpenGL.
In other words, Vrui is primarily a display and interaction engine, and comparable to the lower levels contained in any high-level game engine. As a result, given a game engine that has a clear separation of responsibilty between its layers, it can be possible to layer an existing game engine on top of Vrui, for the added benefit of being able to run that game engine in non-desktop environments. However, because most commercial or free game engines do not make this clear distinction, doing so might be difficult in practice.
GUI toolkits like Qt or Gtk+ remedied that problem by introducing a higher level. Instead of working with windows and mouse events, applications could now use "widgets" with specific purposes and widget events, leading to much better user interfaces, and, more important, consistent interfaces between applications.
Vrui attempts to do to immersive 3D graphics what Qt et al. did to 2D GUIs. It provides higher-level interfaces, to prevent individual applications from "reinventing the wheel," and instead foster consistent user interfaces. Instead of dealing directly with OpenGL windows and 4x4 matrices representing input devices, Vrui applications render into a toolkit-provided 3D application space, and receive higher-level events from input devices. As a concrete example, in other toolkits navigation, i.e., the mapping from 3D application space to display space, is handled by each application individually, whereas in Vrui it is handled by the toolkit. Overall, the result is that Vrui applications with widely differing purposes "look & feel" the same.
An intended side effect of Vrui's higher-level abstractions is that Vrui applications are portable between vastly different types of display environments. A single Vrui application will run in a CAVE like a native CAVE application, and will run on the desktop very similarly to a native desktop application. In comparison, other toolkits require applications to be developed for a narrower range of target environments, sometimes requiring different code for single-screen and multi-screen environments, and none that we are aware of support running applications on desktop environments in any way that is useful beyond basic debugging.
An additional difference is that Vrui consists of an entire hierarchy of layered libraries that work together to support developers in writing correct, portable, and usable applications. For example, Vrui contains a comprehensive cluster-transparent file I/O handling library, explicit high-performance intra-cluster communication, a comprehensive library for affine and projective 3D geometry, OpenGL support classes supporting generic programming, an OpenGL-based GUI widget set, and a scene graph library. While all these are completely optional, and Vrui is intentionally designed to be as compatible as possible with third-party libraries, developers are encouraged to use the highest-level available abstractions provided by the entire Vrui package.
For example, Vrui's underlying geometry library is used throughout Vrui's API instead of passing positions or orientations as flat 3- or 4-element arrays or 4x4 matrices. Vrui uses abstract geometry classes such as Point and Vector for affine points and vectors, respectively, and a hierarchy of abstract transformation classes from translations or rotations only over rigid body transformations to fully-general affine or projective transformations. All these classes have full sets of algebraic operations, which means application code can in almost all cases use them as black boxes. These higher-level classes significantly reduce the burden on application developers to either write their own algebraic operations, such as matrix inversion, or continuously convert back-and-forth between the toolkit's flat representation and the representation of a third-party geometry library they want to use. An intended side-effect of the use of higher-level abstractions at the API is that implicit constraints can be made explicit. For example, tracked 6-DOF input devices can only change position and orientation, and instead of representing those as generic projective transformations, i.e., 4x4 matrices, Vrui represents them as rigid-body transformations, i.e., a translation vector plus a unit quaternion. This makes arithmetic involving input devices easier, more efficient, and more robust. That said, all classes have methods to convert from/to flat array representations to be backwards-compatible with third-party libraries an application developer might want to use.
Vrui also builds and runs on Mac OS X, but there are currently some Linux-only features, particularly handling of sound and video, some non-standard required libraries – libpng, libjpeg, libtiff, libusb – that need to be installed from source or using software management systems such as homebrew, and Vrui is generally more thoroughly tested on Linux.
Vrui does not build on Windows. Microsoft's Visual C++, at least the most recent version tested, cannot deal with some of the C++ template constructs used in Vrui. Other C++ compilers, such as Intel's, can do those, but they are typically quite expensive. Even with a proper C++ compiler, several of the underlying architecture decisions in Vrui are deeply rooted in the UNIX paradigm. Porting Vrui would take significant initial effort, and continual effort to maintain a split codebase.
Vrui does build and run under UNIX emulation systems such as cygwin, but it will not be particularly useful because no version of cygwin we have tried offers hardware-accelerated 3D graphics. As Vrui applications rely heavily on high-performance graphics, they will barely be usable under cygwin.
The same problem applies if Linux is run inside a virtual machine. No virtual machine hypervisor we have tried offers hardware-accelerated 3D graphics inside the virtual machine, and Vrui applications will barely be usable as a result.
Go to the Vrui download web site and make note of the most recent Vrui release, Vrui-<major>.<minor>-<build>, e.g., Vrui-4.2-001.
Download and unpack the current Vrui source tarball into a src directory:
$ cd ~ $ mkdir src $ cd src $ wget -O - http://idav.ucdavis.edu/~okreylos/ResDev/Vrui/Vrui-<major>.<minor>-<build>.tar.gz | tar xfz - $ ls Vrui-<major>.<minor>-<build>
This created Vrui's build directory, named by major and minor version number and build number, such as /home/alice/src/Vrui-4.2-001. Enter the build directory, and build and install Vrui:
$ cd Vrui-<major>.<minor>-<build> $ make ... lots of text... $ sudo make install (enter administrator user's password) ... lots of text...
This created several directories inside /usr/local containing all Vrui header and library files, and some utility programs. To build Vrui's example programs and run a test, follow this directly by:
$ cd ExamplePrograms $ make ... lots of text... $ ./bin/ShowEarthModel
Most Vrui-based applications are configured to work with the default installation, so building an application is typically as simple as downloading and unpacking the tarball and running "make."
To install optional system packages for additional Vrui functionality see the "quick installation guide" on the Vrui download page.
$ make INSTALLDIR=/usr/local ... lots of text... $ sudo make INSTALLDIR=/usr/local install [sudo] password for <user>: (enter password) ... lots of text...Long answer:
By default, Vrui installs itself in ~/Vrui-<major>.<minor>, i.e., in the installing user's home directory. For legacy reasons, Vrui does not use the common ./configure, make, make install toolchain, but Vrui's build system offers the same level of flexibility. Installation targets can be defined as a whole or individually by editing Vrui's makefile, or by passing VARIABLE=<value> arguments on make's command line. When overriding variables via make's command line, it is important to use the same arguments during make and make install (but see Creating Binary Packages for an exception).
The most basic installation override is the INSTALLDIR variable defined at the very top of Vrui's makefile (to the default value $(HOME)/Vrui-<major>.<minor>). Unless detail changes are made, Vrui installs itself into $(INSTALLDIR), using the canonical include, lib(64), bin, etc, and share subdirectories. The easiest (and most easily un-installed) way to install Vrui system-wide is to set INSTALLDIR to /usr/local/Vrui-<major>.<minor>:
$ make INSTALLDIR=/usr/local/Vrui-<major>.<minor> ... lots of text... $ sudo make INSTALLDIR=/usr/local/Vrui-<major>.<minor> install [sudo] password for <user>: (enter password) ... lots of text...In this case, "make install" has to be run as super-user because it will create a subdirectory in a system directory. This simple installation procedure will create a system-wide installation in /usr/local/Vrui-<major>.<minor>, and un-installing Vrui will be as simple as sudo rm -rf /usr/local/Vrui-<major>.<minor>, but it will not follow the POSIX conventions of installing software in /usr/local. According to POSIX, include files go into /usr/local/include, library files into /usr/local/lib(64), executables into /usr/local/bin, configuration files into subdirectories of /usr/local/etc, shared files into subdirectories of /usr/local/share, configuration files for pkg-config into /usr/local/lib(64)/pkgconfig, and documentation files into subdirectories of /usr/local/share/doc. Vrui's build system has a variable for each of these destinations: HEADERINSTALLDIR, LIBINSTALLDIR, EXECUTABLEINSTALLDIR, ETCINSTALLDIR, SHAREINSTALLDIR, PKGCONFIGINSTALLDIR, and DOCINSTALLDIR, respectively. For example, to install Vrui-4.2-<build> in a POSIX-compliant fashion:
$ make HEADERINSTALLDIR=/usr/local/include/Vrui-4.2 LIBINSTALLDIR=/usr/local/lib64/Vrui-4.2 EXECUTABLEINSTALLDIR=/usr/local/bin \ ETCINSTALLDIR=/usr/local/etc/Vrui-4.2 SHAREINSTALLDIR=/usr/local/share/Vrui-4.2 PKGCONFIGINSTALLDIR=/usr/local/lib64/pkgconfig \ DOCINSTALLDIR=/usr/local/share/doc/Vrui-4.2 $ sudo make HEADERINSTALLDIR=/usr/local/include/Vrui-4.2 LIBINSTALLDIR=/usr/local/lib64/Vrui-4.2 EXECUTABLEINSTALLDIR=/usr/local/bin \ ETCINSTALLDIR=/usr/local/etc/Vrui-4.2 SHAREINSTALLDIR=/usr/local/share/Vrui-4.2 PKGCONFIGINSTALLDIR=/usr/local/lib64/pkgconfig \ DOCINSTALLDIR=/usr/local/share/doc/Vrui-4.2 installThose command lines are quite a mouthful (and it's probably more practical to make these changes directly in Vrui's makefile), but they are appropriate for shell scripting, such as when creating binary packages. By the way, Vrui's build system prints all installation directories at the beginning of its build output. A quick way to check is to run make <arguments> config.
That said, Vrui's build system contains logic for several common installation cases. Specifically, there is a shortcut for the exact installation structure spelled out in detail above:
$ make INSTALLDIR=/usr/local $ sudo make INSTALLDIR=/usr/local installAnd there is yet another shortcut, for deep system-wide installations in /usr/include/Vrui-<major>.<minor>, /usr/lib(64)/Vrui-<major>.<minor>, /usr/bin, /etc/Vrui-<major>.<minor>, /usr/share/Vrui-<major>.<minor>, /usr/lib(64)/pkgconfig, and /usr/share/doc/Vrui-<major>.<minor>:
$ make SYSTEMINSTALL=1 INSTALLDIR= $ sudo make SYSTEMINSTALL=1 INSTALLDIR= install(That's not a typo, the value after INSTALLDIR= must be the empty string.) However, the Linux file system standard states that software locally installed from source should not go into /usr and /etc, but into /usr/local. Unless one is...
The tricky bit here is that the software has to be configured and built for its final destination, such as /usr/bin etc., but has to be installed into the fake root directory for packaging. Vrui's build system simplifies that by using the combination of the SYSTEMINSTALL and INSTALLDIR variables. To build and configure Vrui for the final destination, run
$ make SYSTEMINSTALL=1 INSTALLDIR=and to install the built software into a fake root for packaging, run
$ make SYSTEMINSTALL=1 INSTALLDIR=/path/to/fake/root install(sudo is not necessary here because the fake root directory is assumed to be under the user's home directory.)
Then, the final step is the distribution-specific equivalent of
$ cd /path/to/fake/root $ tar cfz Vrui-<major>.<minor>.binary.tar.gz *
The Vrui run-time environment itself is configured via the Vrui.cfg configuration file. This file defines the complete display environment, including the positions, orientations, and sizes of all screens, all viewers present in an environment, the OpenGL windows used to render 3D views to those screens, the number and types of 3D input devices presented by the low-level device driver, which tools are available, etc.
Concretely, VRDevices.cfg is read when the low-level device driver is started, whereas Vrui.cfg is read whenever a Vrui application is started. Both configuration files together define the complete set-up of the display environment in which Vrui runs, and need to be created and/or adapted carefully based on the details of a concrete environment. In a shared display environment like a CAVE, this task would typically fall to a dedicated system integrator / administrator, whereas configuration is more or less unnecessary in single-user desktop environments, where the default setup will cover the common cases.
In the first scenario, where all displays have comparable resolutions, but X lies, you will have to disable automatic screen size detection (by setting autoScreenSize to false in your window section), and enter the correct screen size in your screen section. In single-desktop setups, the screen size is the combined size of all screens, not that of a single screen.
This will not work if the screens' resolutions are different. If that's the case, it will not be possible to drag Vrui windows between screens without introducing major distortions -- after all, the Vrui run-time now has to deal with a single (virtual) display that suddenly changes resolution in the middle. You will have to decide which of the screens is the main screen, and configure Vrui for that screen. Then, if an application window is dragged to another screen, partially or completely, behavior is undefined.
To set up a main screen, add a panningDomain setting to the appropriate window section in Vrui.cfg, and set the panning domain to the position and size of the main screen. For example, if the main screen has a resolution of 1920x1080, and is to the right of a secondary screen with resolution 1440x900, panningDomain would be (1440, 0), (1920, 1080). Then set autoScreenSize to false, and set the proper size of the main screen in the screen section associated with the window. This will correlate the pixel size of the panning domain with the physical size of the screen, and Vrui will render to scale. If Vrui windows are dragged outside the panning domain, things will get weird, however.
Here is an example configuration fragment for a 30 dpi 1920x1080 main screen to the right of a 1440x900 100dpi secondary screen:
section Screen0 # 100dpi 17" laptop screen, left of main screen name Screen0 origin (-46.4, 0.0, 9.0) width 14.4 height 9.0 endsection section Screen1 # 30 dpi 72" HDTV, centered around the origin name Screen1 origin (-32.0, 0.0, -18.0) width 64.0 height 36.0 endsection section Window windowPos (1800, 300), (800, 600) autoScreenSize false screenName Screen1 # Use HDTV as main screen panningViewport true panningDomain (1440, 0), (1920, 1080) endsection
The "Screen0" section is not actually used in this example.
As far as application programmers are concerned, the details of managing multiple windows, sharing or replicating OpenGL contexts, and parallel rendering are completely hidden by the Vrui API. See the GLContextData document for a detailed explanation of the underlying abstraction mechanism.
After these initial steps, developers are encouraged to use several existing Vrui applications to see how they look and feel, especially as they are run in different display environment types, and then peruse the HTML documentation that does exist. A good starting point is the "Library Overview" section, which lists the component library layers that make up the whole Vrui package, and in turn list all the header files and classes contained in those libraries. The interfaces of all those classes are defined in their respective header files, including detailed comments on all interface methods and functions.
Another important starting point is the Vrui kernel API in include/Vrui/Vrui.h, which declares all core functions applications use to communicate with Vrui. Due to Vrui's microkernel architecture, most of its functionality is not provided by the kernel itself, but by delegate classes such as Vrui::VRWindow, or by external so-called managers such as the input device manager. References to these managers are retrieved through the kernel API, but afterwards applications communicate directly with those managers. The bottom line is that all of Vrui's functionality is accessed through the kernel API, either directly or indirectly via managers or delegate classes.
While the low-level documentation of classes and interfaces provided by the source code comments are reportedly rather good, they don't tell in which scenarios a developer might use a certain class. This will hopefully be addressed by higher-level development guides in the near future. In the meantime, another important resource is the Development Rules document, which lists common pitfalls stemming from Vrui's difference in philosophy to other VR or 3D graphics toolkits, and its focus on portability and usability. For example, the way Vrui applications are supposed to handle user input is Vrui's one feature most distinct from other toolkits.
Let's say you want to create a new project called "Foo," containing an executable called "Foo" created by compiling and linking together source files Bar.cpp and Foo.cpp.
First, create a new directory called "Foo" somewhere, and copy Vrui's template makefile into it:
$ mkdir Foo $ cd Foo $ cp <Vrui share dir>/make/makefile .where <Vrui share dir> is the share directory underneath Vrui's installation directory, such as ~/Vrui-4.2/share or /usr/share/Vrui-4.2 or /usr/local/share/Vrui-4.2. Then edit the makefile as follows:
PACKAGES = MYVRUIThis will tell the build system that all executables in this project will use the Vrui package.
ALL = $(EXEDIR)/Foo$(EXEDIR) is a pre-defined variable pointing to the destination directory for executables, i.e., ./bin.
$(EXEDIR)/Foo: $(OBJDIR)/Bar.o $(OBJDIR)/Foo.o$(OBJDIR) is a pre-defined variable pointing to the destination directory for compiled object files. The root object file directory is ./o, but it contains an entire subdirectory hierarchy to keep object files for different compiler options separate, such as debug and non-debug versions.
For executables with many object files, the above is somewhat inconvenient. A better way is to create a list of names of source files needed for an executable, and let make figure out the object file names automatically:
FOO_SOURCES = Bar.cpp Foo.cpp $(EXEDIR)/Foo: $(FOO_SOURCES:%.cpp=$(OBJDIR)/%.o)The code after $(EXEDIR)/Foo is a function that changes all names in FOO_SOURCES ending in .cpp into their appropriate object file names in the $(OBJDIR) hierarchy.
As an aside, Vrui's build system can deal with source files in multiple subdirectories without any issues. Just use a project directory-relative path name for each source or object file. A source file Baz/Baz.cpp will turn into an object file $(OBJDIR)/Baz/Baz.o.
$ makeThis will recompile all changed source files, and relink all executables. Vrui's build system automatically takes care of additional dependencies such as included header files. There is no need to explicitly enter source dependencies into the makefile.
$ make bin/FooJust running make Foo will not do the right thing. To fix that, add the following lines after the build rule for $(EXEDIR)/Foo in the makefile:
.PHONY: Foo Foo: $(EXEDIR)/FooAfter that addition, make Foo will work as expected.
For all this work, you'll get these major benefits: you will never have to worry about source file dependencies, you will probably never have to use make clean, and make will never waste time rebuilding files that don't need rebuilding.
$ cp <Vrui share dir>/make/makefile .where <Vrui share dir> is the share directory underneath Vrui's installation directory, such as ~/Vrui-4.2/share or /usr/share/Vrui-4.2 or /usr/local/share/Vrui-4.2.
Then compile Foo.cpp into ./bin/Foo and execute it:
$ make PACKAGES=MYVRUI Foo $ ./bin/Foo
Creating a new package to use some external library in a project based on Vrui's build system is straightforward. As an example, we will use a locally-built version of the ImageMagick library, installed underneath /usr/local/Magick6. First, we need to pick some unique name for the resulting package, say IMAGEMAGICK_LOCAL. We will then add a section for that package somewhere in the makefile of the project using it:
IMAGEMAGICK_LOCAL_BASEDIR = IMAGEMAGICK_LOCAL_DEPENDS = IMAGEMAGICK_LOCAL_INCLUDE = IMAGEMAGICK_LOCAL_CFLAGS = IMAGEMAGICK_LOCAL_LIBDIR = IMAGEMAGICK_LOCAL_LIBS = IMAGEMAGICK_LOCAL_LINKLIBFLAGS =
This is the resulting package section for our example non-standard ImageMagick package:
IMAGEMAGICK_LOCAL_BASEDIR = /usr/local/Magick6 IMAGEMAGICK_LOCAL_DEPENDS = IMAGEMAGICK_LOCAL_INCLUDE = -I$(IMAGEMAGICK_LOCAL_BASEDIR)/include/ImageMagick-6 IMAGEMAGICK_LOCAL_CFLAGS = -fopenmp -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16 IMAGEMAGICK_LOCAL_LIBDIR = -L$(IMAGEMAGICK_LOCAL_BASEDIR)/lib IMAGEMAGICK_LOCAL_LIBS = -lMagick++-6.Q16 IMAGEMAGICK_LOCAL_LINKLIBFLAGS = -Wl,-rpath $(IMAGEMAGICK_LOCAL_BASEDIR)/libThe _LINKLIBFLAGS variable is only necessary because the ImageMagick library is in a non-standard place where the dynamic linker would otherwise not find it.
By convergent evolution, Vrui's package structure is very similar to that of the pkg-config utility. Another way to populate the package section for packages supported by pkg-config is the following:
IMAGEMAGICK_LOCAL_BASEDIR = IMAGEMAGICK_LOCAL_DEPENDS = IMAGEMAGICK_LOCAL_INCLUDE = $(pkg-config --cflags-only-I) IMAGEMAGICK_LOCAL_CFLAGS = $(pkg-config --cflags-only-other) IMAGEMAGICK_LOCAL_LIBDIR = $(pkg-config --libs-only-L) IMAGEMAGICK_LOCAL_LIBS = $(pkg-config --libs-only-l) IMAGEMAGICK_LOCAL_LINKLIBFLAGS = $(pkg-config --libs-only-other)
To use a package in a project, simply append its name to the PACKAGE variable in the project's makefile. If the package is only required by some executables in a project, its name can be listed in a specific executable's build section. For example:
$(EXEDIR)/Foo: PACKAGES += IMAGEMAGICK_LOCAL $(EXEDIR)/Foo: $(OBJDIR)/Foo.o
To use a concrete example, assume an application that wants to do something when the user presses a specific key. In glut et al., this would be handled by registering a keyboard event callback, which will in turn check the identity of the just-pressed key when called, and invoke the proper application behavior if the desired key is pressed. In Vrui, application behaviors are implemented as tools. If an application has some behavior X that is to be invoked when some key is pressed, the developer would create a corresponding tool class X and hand it to Vrui's tool manager during start-up. After that, a user can dynamically bind a tool object of class X to any button she desires; after that, if she presses that button, the bound tool object will be called, and can in turn invoke behavior X.
While this approach sounds more complicated, it has important benefits. For one, it works in non-desktop environments. Even if there is no keyboard, there will still be some input device that has some ways to signal that a user wants to initiate an event (which could be an actual button, or a gesture, or a voice command, etc.), and Vrui's dynamic tool binding mechanism allows the user to bind a tool of class X to any such event source, without the application developer having to code any support for that. Even if constrained to the desktop, this allows users to map application behaviors to any keys they desire – in other words, the keyboard remapping functionality common in PC games comes for free in Vrui.
Additionally, in actual code, setting up a tool is no more complicated than writing a callback. For simple cases like the one above, where a key press invokes some behavior, Vrui offers a convenience shortcut that creates a tool class, passes it to Vrui's tool manager, handles dynamic binding, and invokes an arbitrary application callback when an event happens, in a single line of code (see VruiEventToolDemo.cpp in the ExamplePrograms subdirectory for several concrete examples). Only in more complex cases, such as when behaviors require their own internal states, will a developer have to implement an actual tool class, which would be essentially the same amount of work as in other toolkits.
Another common example is an application needing to know how big to make a display, like a text label, or how big to make the influence zone around an interaction event, based on display resolution. In Vrui, these parameters are configured by the system integrator/administrator appropriately for a concrete environment, and can be queried directly via the Vrui kernel API.
In other words, Vrui applications should always render in 3D application space in any unit of measurement they want, advertise that unit to Vrui's coordinate manager, and let Vrui take care of the rest. Any questions about appropriate display sizes, interaction fuzz values, optimal font sizes, etc. should be answered by querying the Vrui kernel API whenever needed.
Let us assume that some module of your project, say a single class, wants to use some OpenGL extension, say GL_ARB_vertex_buffer_object to store some geometry in the GPU's own memory. In Vrui, each supported OpenGL extension is represented as an individual class, with header files in the GL/Extensions subdirectory.
To use an extension, the source file defining the class first has to include the appropriate header file:
#include <GL/Extensions/GLARBVertexBufferObject.h>(note the conversion from separate_words to CamelCase). Including this file makes all #define constants and function prototypes provided by the extension available to the source file's code. All declarations will use the extension's name space suffix (ARB, EXT, etc.), such as GL_ARRAY_BUFFER_ARB or glGenBuffersARB.
After that, the code has to initialize the extension before any of its functions can be used by calling the extension class's static initExtension method:
GLARBVertexBufferObject::initExtension();This is typically done once per lifetime of each created object, typically in the class's OpenGL context initialization method (see initContext in GLContextData). Vrui's extension manager is designed such that multiple redundant calls to initExtension are safe and fast; developers do not have to worry about managing extension initialization and can leave it completely decentralized, improving code modularity.
The initExtension method throws a run-time error exception if an extension is not supported by the current OpenGL context. For more flexibility, especially when using new and rarely-supported extensions, application code can query the availability of an extension via the static isSupported method:
if(GLARBVertexBufferObject::isSupported()) GLARBVertexBufferObject::initExtension(); else { /* Set up a fallback rendering path etc. */ }This pattern only makes sense if there is a reasonable alternative to using the requested extension, such as a fallback rendering path. If a module cannot meaningfully function without a certain extension, it is probably best to let initExtension throw an exception and let the module's client code decide what to do.
An important fact about OpenGL extensions is that they are context-dependent state. Due to Vrui's multi-machine, multi-pipe rendering architecture, it is entirely possible (albeit rare) that a certain extension is supported in one OpenGL context used by the application, and not in another. This is normally handled transparently by Vrui's GLContextData mechanism (see GLContextData), but an important effect is that isSupported, initExtension, and all extension functions can only be called when there is a current OpenGL context, i.e., during a class's initContext or rendering method, or when a class's method is called from inside another class's initContext method etc. The GLContextData document contains a more detailed treatment of application/OpenGL state separation and multi-context rendering.