OpenSG Applications

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 5.4, “vrj::OpenSGApp application class” shows how vrj::OpenSGApp fits into the class hierarchy of an OpenSG-based VR Juggler application.

Figure 5.4. vrj::OpenSGApp application class

vrj::OpenSGApp application class

The two main application methods for vrj::OpenSGApp VR Juggler applications are initScene() and getScene(). 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.

Scene Graph Initialization: vrj::OpenSGApp::initScene()

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.

Scene Graph Access: vrj::OpenSGApp::getScene()

In order for OpenSG to render the application scene graph, it must get access to the scene graph root. The method vrj::OpenSGApp::getScene() 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 (for example when rendering or updating). 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 current scene graph root node.

Possible Misuses

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 getScene()
{
   return mSceneRoot;  // Return the root of the graph
}

To update the scene graph, use either preFrame(), intraFrame(), or postFrame().

Tutorial: Loading a Model with OpenSG

In this section, we present a tutorial that demonstrates model loading with OpenSG. The tutorial overview is as follows:

  • 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: vrj::OpenSGApp::initScene(), vrj::OpenSGApp::getScene()

  • Directory: $VJ_BASE_DIR/share/vrjuggler/samples/OpenSG/simple/OpenSGNav

  • Files: OpenSGNav.h, OpenSGNav.cpp

Class Declaration

The following application class is called OpenSGNav. It is derived from vrj::OpenSGApp and has custom initScene(), getScene(), 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 getScene();
    
       void setModelFileName(std::string filename);
 15 
    private:
       void initGLState();
    
    private:
 20    std::string mFileToLoad;
    
       OSG::NodePtr        mSceneRoot;
       OSG::TransformPtr   mSceneTransform;
       OSG::NodePtr        mModelRoot;
 25 
       OSG::NodePtr  mLightNode;
       OSG::NodePtr  mLightBeacon;
    
    public:
 30    gadget::PositionInterface  mWandPos;
       gadget::DigitalInterface   mButton0;
       gadget::DigitalInterface   mButton1;
       gadget::DigitalInterface   mButton2;
       float  velocity;
 35 };
    

The initScene() Member Function

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"))                      1
  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();                          2
       mLightBeacon = OSG::Node::create();
       OSG::DirectionalLightPtr light_core =
 20       OSG::DirectionalLight::create();
       OSG::TransformPtr light_beacon_core =
          OSG::Transform::create();
    
       // Setup light beacon
 25    OSG::Matrix light_pos;
       light_pos.setTransform(OSG::Vec3f(2.0f, 5.0f, 4.0f));
    
       OSG::beginEditCP(light_beacon_core, OSG::Transform::MatrixFieldMask);
          light_beacon_core->setMatrix(light_pos);
 30    OSG::endEditCP(light_beacon_core, OSG::Transform::MatrixFieldMask);
    
       OSG::beginEditCP(mLightBeacon);
          mLightBeacon->setCore(light_beacon_core);
       OSG::endEditCP(mLightBeacon);
 35 
       // Setup light node
       OSG::addRefCP(mLightNode);
       OSG::beginEditCP(mLightNode);
          mLightNode->setCore(light_core);
 40       mLightNode->addChild(mLightBeacon);
       OSG::endEditCP(mLightNode);
    
       OSG::beginEditCP(light_core);
          light_core->setAmbient   (0.9, 0.8, 0.8, 1);
 45       light_core->setDiffuse   (0.6, 0.6, 0.6, 1);
          light_core->setSpecular  (1, 1, 1, 1);
          light_core->setDirection (0, 0, 1);
          light_core->setBeacon    (mLightNode);
       OSG::endEditCP(light_core);
 50 
       // --- Setup Scene -- //
       // add the loaded scene to the light node, so that it is lit
       // by the light
       OSG::addRefCP(mModelRoot);
 55    OSG::beginEditCP(mLightNode);                                3
          mLightNode->addChild(mModelRoot);
       OSG::endEditCP(mLightNode);
    
       // create the root part of the scene
 60    mSceneRoot = OSG::Node::create();                            4
       mSceneTransform = OSG::Transform::create();
    
       // Set the root node
       OSG::beginEditCP(mSceneRoot);
 65       mSceneRoot->setCore(mSceneTransform);
          mSceneRoot->addChild(mLightNode);                         5
       OSG::endEditCP(mSceneRoot);
    }

1

We begin by loading the file set in OpenSGNav::setModelFileName(). If no file name was set, we default to using a simple torus model. The model object is mModelRoot.

2

Next, we create a node for the light, which we define as a beacon light.

3

The model is added under the light in the scene graph so that it gets lit.

4

Then, the root note for the scene graph is created. This is what will be returned to OpenSG for rendering by OpenSGNav::getScene().

5

Finally, we add the light node as a child of the scene root. Remember that the light node already has the loaded model as a child.

The getScene() Member Function

The method vrj::OpenSGApp::draw() will call the application's getScene() 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::getScene()
{
   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 getScene().