9.5. Rendering Only Selected Objects

Examples 9-3 and 9-4 show a new class that renders only the selected objects. Rather than drawing a surrogate object as in Examples 9-1 and 9-2, these examples draw the selected objects themselves.

Example 9.4.  ShowSelectionRA.h

#include <Inventor/actions/SoGLRenderAction.h>

class SoPath;

class ShowSelectionRenderAction : public SoGLRenderAction {
   SO_ACTION_HEADER(ShowSelectionRenderAction);
  public:
   ShowSelectionRenderAction();
   virtual ~ShowSelectionRenderAction();

   // Applies action to the graph rooted by a node,
   // only drawing selected objects.
   virtual void    apply(SoNode *node);
   
   // Applies action to the graph defined by a path or path
   // list.
   // These simply invoke the parent class apply() methods.
   // These do NOT highlight the path, whether selected or not.
   // They are implemented to keep the compiler happy.
   virtual void    apply(SoPath *path);
   virtual void    apply(const SoPathList &pathList,
                         SbBool obeysRules = FALSE);
   static void initClass();
   
  protected:
   // We will cache the path to the first selection node.
   SoPath           *selPath;
};


Example 9.5.  ShowSelectionRA.c++

#include <Inventor/SoPath.h>
#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/nodes/SoBaseColor.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/nodes/SoNode.h>
#include <Inventor/nodes/SoSelection.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoTexture2.h>

#include "ShowSelectionRA.h"

SO_ACTION_SOURCE(ShowSelectionRenderAction);

// Initializes the ShowSelectionRenderAction class.
void
ShowSelectionRenderAction::initClass()
{
   SO_ACTION_INIT_CLASS(ShowSelectionRenderAction,
                        SoGLRenderAction);
}

//  Constructor

ShowSelectionRenderAction::ShowSelectionRenderAction()
      : SoGLRenderAction(SbVec2s(1, 1)) 
                        // pass a dummy viewport region
{
   selPath = NULL;
}    

//  Destructor

ShowSelectionRenderAction::~ShowSelectionRenderAction()
{
   if (selPath != NULL)
     selPath->unref();
}    

//  Render the passed scene by searching for the first
//  selection node, then rendering only the selected objects.

void
ShowSelectionRenderAction::apply(SoNode *node)
{
   node->ref();
   
   // Do we have to search for the selection node?
   // Only if our cached path is NULL, 
   // or the action is being applied to a different scene,
   // or the tail of our existing path is no longer a selection
   // node (for instance if that node was removed from the
   // scene).
   if ((selPath == NULL) ||
      (selPath->getHead() != node) ||
      (! selPath->getTail()->isOfType(
           SoSelection::getClassTypeId()))) {
   
     // Find the first selection node under the passed root.
     SoSearchAction sa;
     sa.setFind(SoSearchAction::TYPE);
     sa.setInterest(SoSearchAction::FIRST);
     sa.setType(SoSelection::getClassTypeId());
     sa.apply(node);
   
     // Cache this new path.
     if (selPath != NULL)
       selPath->unref();
     selPath = sa.getPath();
     if (selPath != NULL) {
       selPath = selPath->copy();
       selPath->ref();
     }
   }
   
   // Render the selected paths!
   if (selPath != NULL) {       
     SoSelection *sel = (SoSelection *) selPath->getTail();
     if (sel->getNumSelected() > 0) {
       // Keep the length from the root to the selection
       // as an optimization so we can reuse this data
       int reusablePathLength = selPath->getLength();

       // For each selection path, we need the full path from
       // the passed root to render, else we may not have a
       // camera.
       for (int j = 0; j < sel->getNumSelected(); j++) {
         // Continue the path down to the selected object.
         // No need to deal with p[0] since that is the sel
         // node.
         SoPath *p = sel->getPath(j);
         for (int k = 1; k < p->getLength(); k++)
            selPath->append(p->getIndex(k));

         // Render the selected shape.
         SoGLRenderAction::apply(selPath);
      
         // Restore selPath for reuse.
         selPath->truncate(reusablePathLength);
       }
     }
   }
   
   node->unref();
}    

// Function stubs: we do not highlight paths and pathLists.

void
ShowSelectionRenderAction::apply(SoPath *path)
{ SoGLRenderAction::apply(path); }

void
ShowSelectionRenderAction::apply(const SoPathList &pathList, SbBool obeysRules)
{ SoGLRenderAction::apply(pathList, obeysRules); }


Example 9.6, “ Main Program for ShowSelectionRenderAction shows a main program that uses this new highlight class. It creates two viewers that share the same selection node. One viewer uses the GLRenderAction, while the other uses the ShowSelectionRenderAction. Selection changes in one viewer are reflected in the other viewer as well.

Example 9.6.  Main Program for ShowSelectionRenderAction

#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>

#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoSelection.h>

#include "ShowSelectionRA.h"

void
main(int , char *argv[])
{
   // Initialization
   Widget mainWindow = SoXt::init(argv[0]);
   ShowSelectionRenderAction::initClass();
   
   // Open the data file
   SoInput in;   
   char *datafile = "monitor.iv";
   if (! in.openFile(datafile)) {
     fprintf(stderr, "Cannot open %s for reading.\n", datafile);
     return;
   }

   // Read the input file
   SoNode *n;
   SoSeparator *sep = new SoSeparator;
   while ((SoDB::read(&in, n) != FALSE) && (n != NULL))
     sep->addChild(n);
   
   // Create a selection root to show off our new highlight.
   SoSelection *sel = new SoSelection;
   sel->addChild(sep);

   // Create two viewers, one to show the scene, the other
   // to show the selected objects.
   SoXtExaminerViewer *viewer1 = 
                             new SoXtExaminerViewer(mainWindow);
   viewer1->setSceneGraph(sel);
   viewer1->setTitle("Scene");

   SoXtExaminerViewer *viewer2 = new SoXtExaminerViewer();
   viewer2->setSceneGraph(sel);
   viewer2->setGLRenderAction(new ShowSelectionRenderAction());    
   viewer2->redrawOnSelectionChange(sel);
   viewer2->setDecoration(FALSE);
   viewer2->setTitle("Selection");

   viewer1->show();
   viewer2->show();
   
   SoXt::show(mainWindow);
   SoXt::mainLoop();
}