This section explains how to use the OpenSG scene graph in a VR Juggler application. OpenSG is an open source scene graph that is available at www.opensg.org.
An OpenSG-based VR Juggler application must derive from vrj::OpenSGApp. The vrj::OpenSGApp class is derived from the vrj::GlApp presented previously, which in turn derives from vrj::App. vrj::OpenSGApp extends vrj::GlApp by adding methods that deal with scene graph initialization and access. Figure 4.5, “vrj::OpenSGApp application class” shows how vrj::OpenSGApp fits into the class hierarchy of an OpenSG-based VR Juggler application.
The two main application methods for vrj::OpenSGApp VR Juggler applications are initScene() and getSceneRoot(). These are called by the OpenSG application class wrapper 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::OpenSGApp). The rest of this section gives a more detailed description of these methods and some sample code to illustrate the concepts presented.
In an application using OpenSG, the scene graph must be initialized before it can be used. The method vrj::OpenSGApp::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 API initialization, vrj::OpenSGApp::initScene() is invoked. This happens after OSG::osgInit() has been called, so OpenSG should be fully initialized and ready to be used.
In order for OpenSG to render the application scene graph, it must get access to the scene graph root. The method vrj::OpenSGApp::getSceneRoot() will be called by the OpenSG application class wrapper so that it can get access to the currently active scene graph whenever the wrapper needs to use it (ex. rendering, updating). Since the job of getSceneRoot() 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 current scene graph root node.
Do not do any CPU-heavy processing in this method. Because this method is called frequently, it should only do the minimum amount of processing necessary to return the root scene graph node. In most cases this method should only be one line of code. See the following code for an example.
virtual OSG::NodePtr getSceneRoot()
{
return mSceneRoot; // Return the root of the graph
}If you need to update the scene graph, you should use either preFrame(), intraFrame(), or postFrame().
Table 4.5. Tutorial Overview
| Description | Simple OpenSG 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/vrjuggler/samples/OpenSG/simple/OpenSGNav |
| Files |
|
The following application class is called OpenSGNav. It is derived from vrj::OpenSGApp and has custom initScene(), getSceneRoot(), init(), contextInit(), and preFrame() methods declared. Refer to OpenSGNav.h for the implementation of setModelFileName().
1 class OpenSGNav : public vrj::OpenSGApp
{
public:
OpenSGNav(vrj::Kernel* kern);
5 virtual ~OpenSGNav();
virtual void init();
virtual void contextInit();
virtual void preFrame();
10
virtual void initScene();
virtual OSG::NodePtr getSceneRoot();
void setModelFileName(std::string filename);
15
private:
void initGLState();
private:
20 std::string mFileToLoad;
OSG::NodePtr mRootNode;
OSG::TransformPtr mSceneScale;
OSG::NodePtr mSceneRoot;
25 OSG::TransformPtr mSceneTransform;
OSG::NodePtr mModelRoot;
OSG::NodePtr mLightNode;
OSG::NodePtr mLightBeacon;
30
public:
gadget::PositionInterface mWandPos;
gadget::DigitalInterface mButton0;
gadget::DigitalInterface mButton1;
35 gadget::DigitalInterface mButton2;
float velocity;
};
The implementation of initScene() is in OpenSGNav.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 OpenSGNav::initScene()
{
// Load the model to use
if (mFileToLoad == std::string("none"))
5 {
mModelRoot = OSG::makeTorus(.5, 2, 16, 16);
}
else
{
10 mModelRoot =
OSG::SceneFileHandler::the().read(mFileToLoad.c_str());
}
// --- Light setup --- //
15 // - Add directional light for scene
// - Create a beacon for it and connect to that beacon
mLightNode = OSG::Node::create();
mLightBeacon = OSG::Node::create();
OSG::DirectionalLightPtr light_core = OSG::DirectionalLight::create();
20 OSG::TransformPtr light_beacon_core = OSG::Transform::create();
// Setup light beacon
OSG::Matrix light_pos;
light_pos.setTransform(osg::Vec3f( 2.0f, 5.0f, 4.0f));
25
OSG::beginEditCP(light_beacon_core, OSG::Transform::MatrixFieldMask);
light_beacon_core->setMatrix(light_pos);
OSG::endEditCP(light_beacon_core, OSG::Transform::MatrixFieldMask);
30 OSG::beginEditCP(mLightBeacon);
mLightBeacon->setCore(light_beacon_core);
OSG::endEditCP(mLightBeacon);
// Setup light node
35 OSG::addRefCP(mLightNode);
OSG::beginEditCP(mLightNode);
mLightNode->setCore(light_core);
mLightNode->addChild(mLightBeacon);
OSG::endEditCP(mLightNode);
40
OSG::beginEditCP(light_core);
light_core->setAmbient (.9, .8, .8, 1);
light_core->setDiffuse ( 0.6, 0.6, 0.6, 1);
light_core->setSpecular ( 1, 1, 1, 1);
45 light_core->setDirection ( 0, 0, 1);
light_core->setBeacon (mLightNode);
OSG::endEditCP(light_core);
// --- Setup Scene -- //
50 // add the loaded scene to the light node, so that it is lit by the light
OSG::addRefCP(mModelRoot);
OSG::beginEditCP(mLightNode);
mLightNode->addChild(mModelRoot);
OSG::endEditCP(mLightNode);
55
// create the root part of the scene
mRootNode = OSG::Node::create();
mSceneScale = OSG::Transform::create();
mSceneRoot = OSG::Node::create();
60 mSceneTransform = OSG::Transform::create();
// Set the root node
OSG::beginEditCP(mRootNode);
mRootNode->setCore(mSceneScale);
65 mRootNode->addChild(mSceneRoot);
OSG::endEditCP(mRootNode);
OSG::beginEditCP(mSceneRoot);
mSceneRoot->setCore(mSceneTransform);
70 mSceneRoot->addChild(mLightNode);
OSG::endEditCP(mSceneRoot);
}The Performer Draw Manager will call the application's getSceneRoot() method to get the root of the scene graph. The implementation of this method can be found in OpenSGNav.h. The code is as follows:
OSG::NodePtr OpenSGNav::getSceneRoot()
{
return mRootNode;
}The simplicity of this method implementation is not limited to the simple tutorial from which it is taken. All OpenSG-based VR Juggler applications can take advantage of this idiom where the root node is a member variable returned in getSceneRoot().