





















































Create high-performance virtual reality applications with OpenSceneGraph, one of the best 3D graphics engines.
The osg::Group type represents the group nodes of an OSG scene graph. It can have any number of child nodes, including the osg::Geode leaf nodes and other osg::Group nodes. It is the most commonly-used base class of the various NodeKits—that is, nodes with various functionalities.
The osg::Group class derives from osg::Node, and thus indirectly derives from osg::Referenced. The osg::Group class contains a children list with each child node managed by the smart pointer osg::ref_ptr<>. This ensures that there will be no memory leaks whenever deleting a set of cascading nodes in the scene graph.
The osg::Group class provides a set of public methods for defining interfaces for handling children. These are very similar to the drawable managing methods of osg::Geode, but most of the input parameters are osg::Node pointers.
You will be able to handle the child interface of osg::Group with ease because of your previous experience of handling osg::Geode and drawables.
We have already learnt that osg::Group is used as the group node, and osg::Geode as the leaf node of a scene graph. Additionally, both classes should have an interface for managing parent nodes.
OSG allows a node to have multiple parents. In this section, we will first have a glimpse of parent management methods, which are declared in the osg::Node class directly:
The osg::NodePath is actually a std::vector object of node pointers, for example, assuming we have a graphical scene:
The following code snippet will find the only path from the scene root to the node child3:
osg::NodePath& nodePath = child3->getParentalNodePaths()[0]; for ( unsigned int i=0; i<nodePath.size(); ++i ) { osg::Node* node = nodePath[i]; // Do something... }
You will successively receive the nodes Root, Child1, and Child2 in the loop.
We don't need to use the memory management system to reference a node's parents. When a parent node is deleted, it will be automatically removed from its child nodes' records as well.
A node without any parents can only be considered as the root node of the scene graph. In that case, the getNumParents() method will return 0 and no parent node can be retrieved.
In the past examples, we always loaded a single model, like the Cessna, by using the osgDB::readNodeFile() function. This time we will try to import and manage multiple models. Each model will be assigned to a node pointer and then added to a group node. The group node, which is defined as the scene root, is going to be used by the program to render the whole scene graph at last:
#include <osg/Group>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile( "cessna.osg" ); osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile( "cow.osg" );
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild( model1.get() );
root->addChild( model2.get() );
osgViewer::Viewer viewer; viewer.setSceneData( root.get() ); return viewer.run();
Both osg::Group and osg::Geode are derived from the osg::Node base class. The osg::Group class allows the addition of any types of child nodes, including the osg::Group itself. However, the osg::Geode class contains no group or leaf nodes. It only accepts drawables for rendering purposes.
It is convenient if we can find out whether the type of a certain node is osg::Group, osg::Geode, or other derived type especially those read from files and managed by ambiguous osg::Node classes, such as:
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile( "cessna.osg" );
Both the dynamic_cast<> operator and the conversion methods like asGroup(), asGeode(), among others, will help to convert from one pointer or reference type to another. Firstly, we take the dynamic_cast<> operator as an example. This can be used to perform downcast conversions of the class inheritance hierarchy, such as:
osg::ref_ptr<osg::Group> model =
dynamic_cast<osg::Group*>( osgDB::readNodeFile("cessna.osg") );
The return value of the osgDB::readNodeFile() function is always osg::Node*, but we can also try to manage it with an osg::Group pointer. If, the root node of the Cessna sub graph is a group node, then the conversion will succeed, otherwise it will fail and the variable model will be NULL.
You may also perform an upcast conversion, which is actually an implicit conversion:
osg::ref_ptr<osg::Group> group = ...;
osg::Node* node1 = dynamic_cast<osg::Node*>( group.get() );
osg::Node* node2 = group.get();
On most compilers, both node1 and node2 will compile and work fine.
The conversion methods will do a similar job. Actually, it is preferable to use those methods instead of dynamic_cast<> if one exists for the type you need, especially in a performance-critical section of code:
// Assumes the Cessna's root node is a group node. osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg"); osg::Group* convModel1 = model->asGroup(); // OK! osg::Geode* convModel2 = model->asGeode(); // Returns NULL.
A typical traversal consists of the following steps:
Different updating and rendering operations will be applied to all scene nodes during traversals, which makes traversing a key feature of scene graphs. There are several types of traversals, with different purposes:
In the common sense, these traversals should be executed per frame, one after another. But for systems with multiple processors and graphics cards, OSG can process them in parallel and therefore improve the rendering efficiency. The visitor pattern can be used to implement traversals.
The osg::Group nodes do nothing except for traversing down to their children. However, OSG also supports the osg::Transform family of classes, which is created during the traversal-concatenated transformations to be applied to geometry. The osg::Transform class is derived from osg::Group. It can't be instantiated directly. Instead, it provides a set of subclasses for implementing different transformation interfaces.
When traversing down the scene graph hierarchy, the osg::Transform node always adds its own transformation to the current transformation matrix, that is, the OpenGL model-view matrix. It is equivalent to concatenating OpenGL matrix commands such as glMultMatrix(), for instance:
This example scene graph can be translated into following OpenGL code:
glPushMatrix();
glMultMatrix( matrixOfTransform1 );
renderGeode1(); // Assume this will render Geode1
glPushMatrix();
glMultMatrix( matrixOfTransform2 );
renderGeode2(); // Assume this will render Geode2
glPopMatrix();
glPopMatrix();
To describe the procedure using the concept of coordinate frame, we could say that Geode1 and Transform2 are under the relative reference frame of Transform1, and Geode2 is under the relative frame of Transform2. However, OSG also allows the setting of an absolute reference frame instead, which will result in the behavior equivalent to the OpenGL command glLoadMatrix():
transformNode->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
And to switch back to the default coordinate frame:
transformNode->setReferenceFrame( osg::Transform::RELATIVE_RF );