Programmers familiar with the use of scene graphs may prefer to use that data structure rather than writing OpenGL manually. While VR Juggler does not provide a scene graph of its own, its design allows the use of existing scene graph software. In VR Juggler 1.1 and beyond, the supported scene graphs are OpenGL Performer from SGI, OpenSG, and Open Scene Graph. This section explains how to use OpenGL Performer to write VR Juggler applications.
A Performer-based VR Juggler application must derive from vrj::PfApp. Similar to vrj::GlApp presented in the previous section, vrj::PfApp derives from vrj::App. vrj::PfApp extends vrj::App by adding methods that deal with scene graph initialization and access. Figure 4.4, “vrj::PfApp application class” shows how vrj::PfApp fits into the class hierarchy of a Performer-based VR Juggler application.
Two of the methods added to the application interface by vrj::PfApp are initScene() and getScene(). These are called by the Performer Draw Manager to initialize the application scene graph and to get the root of the scene graph respectively. They must be implemented by the application (they are pure virtual methods within vrj::PfApp). Additional methods will be discussed in this section, but in many cases the default implementations of these other methods may be used. A simple tutorial application will be provided to illustrate the concepts presented.
In an application using OpenGL Performer, the scene graph must be initialized before it can be used. The method vrj::PfApp::initScene() is provided for that purpose. Within this method, the root of the application scene graph should be created, and any required models should be loaded and attached to the root in some way. The exact mechanisms for accomplishing this will vary depending on what the application will do.
During the initialization of OpenGL Performer by VR Juggler, vrj::PfApp::initScene() is invoked after the Performer functions pfInit() and pfConfig() but before vrj::App::apiInit().
In order for Performer to render the application scene graph, it must get access to the scene graph root. The method vrj::PfApp::getScene() will be called by the Performer Draw Manager so that it can give the scene graph root node to Performer. Since the job of getScene() is straightforward, its implementation can be very simple. A typical implementation will have a single statement that returns a member variable that holds a pointer to the application scene graph root node.
Make sure that the node returned is not a pfScene object. If it is, then lighting will not work.
Table 4.4. Tutorial Overview
| Description | Simple OpenGL Performer application that loads a model |
| Objectives | Understand how to load a model, add it to a scene graph, and return the root to VR Juggler |
| Member functions |
|
| Directory | $VJ_BASE_DIR/share/samples/Pf/simple/simplePf |
| Files |
|
The following application class is called simplePfApp. It is derived from vrj::PfApp and has custom initScene() and getScene() methods declared. Note that this application uses preForkInit() which will be discussed later. Refer to simplePfApp.h for the implementations of preForkInit() and setModel().
1 class simplePfApp : public vrj::PfApp
{
public:
simplePfApp();
5 virtual ~simplePfApp();
virtual void preForkInit();
virtual void initScene();
virtual pfGroup* getScene();
10 void setModel(std::string modelFile);
public:
std::string mModelFileName;
15 pfGroup* mLightGroup;
pfLightSource* mSun;
pfGroup* mRootNode;
pfNode* mModelRoot;
};The implementation of initScene() is in simplePfApp.cpp. Within this method, we create the scene graph root node, the lighting node, and load a user-specified model. The implementation follows:
1 void simplePfApp::initScene ()
{
// Allocate all the nodes needed
mRootNode = new pfGroup;
5
// Create the SUN light source
mLightGroup = new pfGroup;
mSun = new pfLightSource;
mLightGroup->addChild(mSun);
10 mSun->setPos(0.3f, 0.0f, 0.3f, 0.0f);
mSun->setColor(PFLT_DIFFUSE, 1.0f, 1.0f, 1.0f);
mSun->setColor(PFLT_AMBIENT, 0.3f, 0.3f, 0.3f);
mSun->setColor(PFLT_SPECULAR, 1.0f, 1.0f, 1.0f);
mSun->on();
15
// --- LOAD THE MODEL -- //
mModelRoot = pfdLoadFile(mModelFileName.c_str());
// -- CONSTRUCT STATIC STRUCTURE OF SCENE GRAPH -- //
20 mRootNode->addChild(mModelRoot);
mRootNode->addChild(mLightGroup);
}![]() | First, the root node is constructed as a pfGroup object. |
![]() | Next, some steps are taken to create a light source for the application. |
![]() | Finally, the model is loaded using pfdLoadFile(), and the model scene graph root node is stored in mModelRoot. (The model loader must be initialized prior to calling pfdLoadFile(). This is done in preForkInit().) |
![]() | Finally, the model and the light source nodes are added as children of the root. |
The Performer Draw Manager will call the application's getScene() method to get the root of the scene graph. The implementation of this method can be found in simplePfApp.h. The code is as follows:
pfGroup* simplePfApp::getScene ()
{
return mRootNode;
}The simplicity of this method implementation is not limited to the simple tutorial from which it is taken. All Performer-based VR Juggler applications can take advantage of this idiom where the root node is a member variable returned in getScene().
Besides the two methods discussed so far, there are several other methods in vrj::PfApp that extend the basic vrj::App interface. Each is discussed in this section.
Prototype: public void preForkInit();
This member function allows the user application to do any processing that needs to happen before Performer forks its processes but after pfInit() is called. In other words, it is invoked after pfInit() but before pfConfig().
Prototype: public void appChanFunc(pfChannel* chan);
This method is called every frame in the application process for each active channel. It is called immediately before rendering (pfFrame()).
Prototype: public void configPWin(pfPipeWindow* pWin);
This method is used to initialize a pipe window. It is called as soon as the pipe window is opened.
Prototype: public std::vector<int> getFrameBufferAttrs();
This method returns the needed parameters for the Performer frame buffer. Stereo, double buffering, depth buffering, and RGBA are all requested by default.
Prototype: public void drawChan(pfChannel* chan,
void* chandata);
This is the method called in the channel draw function to do the actual rendering. For most programs, the default behavior of this function is correct. It makes the following calls:
chan->clear(); pfDraw();
Advanced users may want to override this behavior for complex rendering effects such as overlays or multi-pass rendering. (See the OpenGL Performer manual pages about overriding the draw traversal function.) This function is the draw traversal function but with the projections set correctly for the given displays and eye. Prior to the invocation of this method, chan is ready to draw.