The uppermost abstract base class is Traverser . A parameter to it's constructor controls whether it's a read-only traverser or a read-write traverser. Some derived traversers like the RenderTraverser use it as a read-only traverser. Others like the AppTraverser use it as a read-write traverser. Traverser provides iterating functionality as well as per-object locking functionality, described as follows:
// Example of a custom read-only traverser #include "nvtraverser/Traverser.h" class MyTraverser : public Traverser { public: MyTraverser() : Traverser( true ) {} // use Traverser in read-only mode // ... };
To add a new operation for a certain NVSG object, the developer needs to overload the matching handler routine for that object.
// MyTraverser.h // Example for adding a custom operation for a Group #include "nvtraverser/Traverser.h" // base class definition // forward declare class Group to satisfy includees namespace nvsg { class Group; } class MyTraverser : public nvtraverser::Traverser { // ... protected: // handleGroup override NVSG_API virtual void handleGroup(const nvsg::Group * p); };
The base classes provide a handler routine for each known and concrete NVSG object. The base implementations of these handler routines do not operate on the particular object but instead iterate over that object in a correct manner. For iterating purposes, it is strongly recommended to always fall back on the base implementation for overloaded handler routines.
// MyTraverser.cpp implementation file #include "MyTraverser.h" #include "nvsg/Group.h" // definition of Group using namespace nvtraverser; using namespace nvsg; void MyTraverser::handleGroup(const Group * p) { // ... add custom functionality // iterate subsequent objects by calling the base implementation Traverser::handleGroup(p); // ... add some more custom functionality }
The new traverser must provide a new handler routine for the new object, and add it to the base class' handler routine table by means of the base class' member function addObjectHandler .
Example of a custom traverser that handles a newly invented NVSG object that inherits from Group:
// NewObject.h // #include "nvsg/Group.h" // A NewGroup 'is a' Group class NewGroup : public nvsg::Group { // ... }; // MyTraverser.h file #include "nvtraverser/Traverser.h" // base class definition // forward declare class NewGroup to satisfy includees class NewGroup; // forward declare class CullData to satisfy includees namespace nvsg { class CullData; } class MyTraverser : public nvtraverser::Traverser { // ... protected: // constructor to deal with initial work MyTraverser(); // clean up virtual ~MyTraverser(); // overloadable handler for NewGroup NVSG_API virtual void handleNewGroup(const NewGroup * p); private: // implementation details that handle iterating over a NewObject void a_private_function_that_handles_iteration(const NewGroup * p); void a_private_function_that_handles_conditional_iteration(const NewGroup * p, const nvsg::CullData * p); }; // - - - - - - - - - - - - - - - - - - - - - - - - // MyTraverser.cpp file #include "MyTraverser.h" #include "NewObject.h" // NewObject definition MyTraverser::MyTraverser() { // ... // add the new handler routine to the base classes handler table addObjectHandler( OC_NEWGROUP // object code for the new object NewGroup // this must be provided by the author of NewGroup , &MyTraverser::handleNewGroup // handler routine ); // ... }
A handler routine for a newly invented NVSG object must correctly iterate over the new object in order to ensure that the new object is traversed in a correct manner as part of the NVSG. If, for example, the new NVSG object inherits from Group , the handler routine must take care of iterating through the child nodes. Or, for a concrete Node , the actual cull tree also needs to be correctly maintained while iterating over the new object.
// MyTraverser.cpp // ... void MyTraverser::handleNewGroup(const NewGroup * p) { // at entry, call overloadable preTraverseGroup preTraverseGroup(p); // get the topmost cull tree const CullData * cd = m_cullStack.top(); if ( ! cd ) { // if there's no cull data, just traverse // ... traverse children a_private_function_that_handles_iteration(p); } else if ( cd->getCullCode() == CC_IN ) { // this group is trivial in, so just push a NULL pointer on cull stack and traverse m_cullStack.push( NULL ); // ... traverse children a_private_function_that_handles_iteration(p); m_cullStack.pop(); } else { // this group is at least partly visible, so traverse through all children that are not out a_private_function_that_handles_conditional_iteration(p, cd); } // call overloadable postTraverseGroup right before leaving postTraverseGroup(p); }
If, as in the previous example, the newly invented NVSG object inherits from Group, the corresponding handler routine must iterate or conditionally iterate over the Group's child nodes. In order to keep the child nodes locked while being traversed, it is strongly recommended to call the base class' member function traverseObject for each child node that should be traversed, instead of just calling the corresponding handler routine for the particular child node.
// MyTraverser.cpp // bad implementation: void MyTraverser::a_private_function_that_handles_iteration(const NewGroup * p) { // unconditionally traverse all child nodes for ( size_t i=0; i<p->getNumberOfChildren(); ++i ) { // bad idea: identifying a child node's type this way doesn't perform well! switch ( p->getChild(i)->getObjectCode() ) { case OC_GEONODE: // handle a GeoNode child handleGeoNode(p->getChild(i)); // bad idea: child GeoNode will not be locked while being traversed! // dangerous in multithreaded environments! break; case ... // handle other typed children ... } } } // good implementation: void MyTraverser::a_private_function_that_handles_iteration(const NewGroup * p) { // unconditionally traverse all child nodes for ( size_t i=0; i<p->getNumberOfChildren(); ++i ) { traverseObject(p->getChild(i)); // good idea for two reasons: // (1) fast lookup the corresponding handler routine // (2) the child node is locked while beeing traversed } }
Back to