8.9. Calling Back to the Application

The SoCallbackAction SoCallbackAction SoCallbackAction allows you to traverse the scene graph and accumulate state. It includes methods for calling back to application functions whenever nodes of a specified type are encountered during the traversal. At every node, the callback function has access to the entire Inventor traversal state. It can thus query any element in the state, such as the current coordinates, current normals, or current material binding. See the Open Inventor C++ Reference Manual on SoCallbackAction SoCallbackAction SoCallbackAction for a description of all state query functions.

The callback action also allows you to register callback functions that are called whenever certain shape nodes are traversed. The primitives used to draw the shape are passed to the callback function for use by the application.

This action provides a convenient mechanism for adding your own action to Inventor without subclassing (see The Inventor Toolmaker for information on creating a new action). It is particularly useful for C programmers who want to add functionality to scene graph traversal.

Create an Instance of the Action

An example of creating an instance of SoCallbackAction SoCallbackAction SoCallbackAction is as follows:

SoCallbackAction cbAction;
   
SoCallbackAction cbAction = new SoCallbackAction();
   
SoCallbackAction cbAction = new SoCallbackAction();
   

Register Callback Functions

Inventor provides a number of methods for setting callback functions for a node. Each method takes a node type, a pointer to the user callback function, and a pointer to user data. The function is called whenever a node of the specified type or a subclass of that type, is encountered during traversal of the scene graph.

General-Purpose Callback Functions

The following functions are set for any type of node:

addPreCallback()

adds a callback function that is called just before a node of a particular type is traversed

addPostCallback()

adds a callback function that is called just after a node of a particular type is traversed

addPreTailCallback()

adds a callback function that is called just before the last node in the path is traversed

addPostTailCallback()

adds a callback function that is called just after the last node in the path is traversed

In the case of a separator node, the addPreCallback() method is called before the children are traversed, and the addPostCallback() method is called after the children are traversed but before the state is restored. The addPreTailCallback() and addPostTailCallback() methods are used only when you apply the callback action to a path.

A general-purpose callback function must return one of three values:

SoCallbackAction::CONTINUE

continue traversal of the scene graph.

SoCallbackAction::PRUNE

do not go any lower in the scene graph; continue traversal of the rest of the scene graph above and to the right.

SoCallbackAction::ABORT

stop traversal of the scene graph and pop state back up to the root.

Primitive Generation

The following callback functions are set for a particular type of shape node. When these callback functions are set and the shape is traversed, primitives for the shape are generated, the callback function is invoked, and the primitives are passed to the callback function. You might use addTriangleCallback(), for example, if you are writing your own renderer and you want to tessellate all filled objects into triangles.

addTriangleCallback()

adds a callback function to a node that generates triangles, such as SoFaceSet SoFaceSet SoFaceSet or SoNurbsSurface SoNurbsSurface SoNurbsSurface

addLineSegmentCallback()

adds a callback function to a node that generates line segments, such as SoLineSet SoLineSet SoLineSet or SoIndexedLineSet SoIndexedLineSet SoIndexedLineSet (but not to SoFaceSet SoFaceSet SoFaceSet or related classes even when the draw-style is LINES)

addPointCallback()

adds a callback function to a node that generates points, such as SoPointSet SoPointSet SoPointSet (but not to SoFaceSet SoFaceSet SoFaceSet or SoLineSet SoLineSet SoLineSet even when the draw-style is POINTS)

For triangles, the associated callback is of the following form:

void SoTriangleCB(void *userData, SoCallbackAction *action, const SoPrimitiveVertex *v1, const SoPrimitiveVertex *v2, const SoPrimitiveVertex *v3);

Here, the callback function is called once for each triangle the shape generates. An example of using this callback function would be if you are writing a ray tracer and want to deal with only one type of data structure for all polygonal shapes. A triangle callback function can be registered on spheres, cones, cylinders, and NURBS surfaces, as well as on face sets and quad meshes.

An SoPrimitiveVertex SoPrimitiveVertex is a vertex of a primitive shape (triangle, line segment, or point) that is generated by a callback action. It contains an object-space point, normal, texture coordinate, material index, and a pointer to an instance of an SoDetail SoDetail SoDetail subclass. The detail may contain additional information about the vertex.

Tip: Your callback function can use the value of the draw-style element from the state if you want to determine if the triangles would be rendered as points or lines. For example:

if(SoDrawStyleElement::get(action->getState())==
    SoDrawStyleElement::LINES)
      
if(SoDrawStyleElement.Get(action.GetState())==
    SoDrawStyleElement.Styles.LINES)
   
if(SoDrawStyleElement.get(action.getState())==
    SoDrawStyleElement.Styles.LINES)
      

...//do something See The Inventor Toolmaker for more information on elements.

Apply the Action

SoCallbackAction SoCallbackAction SoCallbackAction can be applied to a node, a path, or a path list.

Using a Callback for Generated Primitives

Example 8.3, “ Using a Triangle Callback Function shows using the callback action to decompose a sphere into a set of triangle primitives.

Example 8.3.  Using a Triangle Callback Function

...
SoSphere *mySphere = new SoSphere;
mySphere->ref();
printSpheres(mySphere);
...
void
printSpheres(SoNode *root)
{
   SoCallbackAction myAction;

   myAction.addPreCallback(SoSphere::getClassTypeId(), 
            printHeaderCallback, NULL);
   myAction.addTriangleCallback(SoSphere::getClassTypeId(), 
            printTriangleCallback, NULL);

   myAction.apply(root);
}

SoCallbackAction::Response
printHeaderCallback(void *, SoCallbackAction *, 
      const SoNode *node)
{
   printf("\n Sphere ");
   // Print the node name (if it exists) and address
   if (! !node->getName())
      printf("named \"%s\" ", node->getName());
   printf("at address %#x\n", node);

   return SoCallbackAction::CONTINUE;
}

void
printTriangleCallback(void *, SoCallbackAction *,
   const SoPrimitiveVertex *vertex1,
   const SoPrimitiveVertex *vertex2,
   const SoPrimitiveVertex *vertex3)
{
   printf("Triangle:\n");
   printVertex(vertex1);
   printVertex(vertex2);
   printVertex(vertex3);
}

void
printVertex(const SoPrimitiveVertex *vertex)
{
   const SbVec3f &point = vertex->getPoint();
   printf("\tCoords     = (%g, %g, %g)\n", 
               point[0], point[1], point[2]);

   const SbVec3f &normal = vertex->getNormal();
   printf("\tNormal     = (%g, %g, %g)\n", 
               normal[0], normal[1], normal[2]);
}
   
...
void printSpheres(SoNode root)
{

    SoCallbackAction myAction = new SoCallbackAction();
    Console.WriteLine(typeof(SoSphere));
    myAction.AddPreCallback(typeof(SoNode),
        new SoCallbackAction.CallbackActionCB(printHeaderCallback));
    myAction.AddTriangleCallback(typeof(SoNode),
        new SoCallbackAction.TriangleCB(printTriangleCallback));

    myAction.Apply(root);
}

SoCallbackAction.Responses printHeaderCallback(SoCallbackAction action, SoNode node)
{

    if (node.GetName() != "") Console.WriteLine("Sphere named " + node.GetName());

    return SoCallbackAction.Responses.CONTINUE;
}

void printTriangleCallback(SoCallbackAction action,
              ref SoPrimitiveVertex vertex1,
              ref SoPrimitiveVertex vertex2,
              ref SoPrimitiveVertex vertex3)
{
    Console.WriteLine("Triangle:");
    printVertex(vertex1);
    printVertex(vertex2);
    printVertex(vertex3);
}

void printVertex(SoPrimitiveVertex vertex)
{
  float x, y, z;
  vertex.Point.GetValue(out x, out y, out z);
  SbVec3f point = new SbVec3f(x, y, z);
  Console.WriteLine("\t Coords     = ({0}, {1}, {2})\n", point[0], point[1], point[2]);

  vertex.Normal.GetValue(out x, out y, out z);
  SbVec3f normal = new SbVec3f(x, y, z);
  Console.WriteLine("\tNormal     = ({0}, {1}, {2})\n", normal[0], normal[1], normal[2]);
}
      
private void printSpheres(SoNode root)
{
  SoCallbackAction myAction = new SoCallbackAction();
  myAction.addPreCallback(SoSphere.class, new PrintHeaderCallback(), null);
  myAction.addTriangleCallback(SoSphere.class, new PrintTriangleCallback(), null);
  myAction.apply(root);
}

class PrintHeaderCallback extends SoCallbackActionCB
{
  public int invoke(SoCallbackAction s, SoNode node)
  {
    String str = "Sphere ";

    // Print the node name (if it exists) and address
    if ( node.getName().length() != 0 )
      str += "named " + node.getName() + " ";
    m_txtArea.append(str + "\n");
    //
    return SoCallbackAction.Responses.CONTINUE.getValue();
  }
}

class PrintTriangleCallback extends SoTriangleCB
{
  public void invoke(SoCallbackAction s, SoPrimitiveVertex v1,
                     SoPrimitiveVertex v2, SoPrimitiveVertex v3)
  {
    m_txtArea.append("Triangle:\n");
    printVertex(v1);
    printVertex(v2);
    printVertex(v3);
  }

  private void printVertex(SoPrimitiveVertex vertex)
  {
    SbVec3f point = vertex.getPoint();
    m_txtArea.append("\tCoords     = (" + point.getX() + ", " + point.getY() +
                                      ", " + point.getZ() + ")\n");

    SbVec3f normal = vertex.getNormal();
    m_txtArea.append("\tNormal     = (" + normal.getX() + ", " + normal.getY() +
                                      ", " + normal.getZ() + ")\n");
  }
}