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.
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.
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::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.
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().
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
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 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 =
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);
mLightNode->addChild(mModelRoot);
OSG::endEditCP(mLightNode);
// create the root part of the scene
60 mSceneRoot = OSG::Node::create();
mSceneTransform = OSG::Transform::create();
// Set the root node
OSG::beginEditCP(mSceneRoot);
65 mSceneRoot->setCore(mSceneTransform);
mSceneRoot->addChild(mLightNode);
OSG::endEditCP(mSceneRoot);
}We begin by loading the file set in
| |
Next, we create a node for the light, which we define as a beacon light. | |
The model is added under the light in the scene graph so that it gets lit. | |
Then, the root note for the scene graph is
created. This is what will be returned to OpenSG for
rendering by
| |
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 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().