8.9. Calling Back to the Application

The SoCallbackAction( C++ | Java | .NET ) 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( C++ | Java | .NET ) 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.

An example of creating an instance of SoCallbackAction( C++ | Java | .NET ) is as follows:


C++
SoCallbackAction cbAction;
   

.NET
SoCallbackAction cbAction = new SoCallbackAction();
   

Java
SoCallbackAction cbAction = new SoCallbackAction();
   

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.

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.

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( C++ | Java ) 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( C++ | Java | .NET ) subclass. The detail may contain additional information about the vertex.

[Tip]

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:


C++
if(SoDrawStyleElement::get(action->getState())==
    SoDrawStyleElement::LINES)
      

.NET
if(SoDrawStyleElement.Get(action.GetState())==
    SoDrawStyleElement.Styles.LINES)
   

Java
if(SoDrawStyleElement.get(action.getState())==
    SoDrawStyleElement.Styles.LINES)
      

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

SoCallbackAction( C++ | Java | .NET ) can be applied to a node, a path, or a path list.

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


C++
...
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]);
}
   

.NET
...
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]);
}
      

Java
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");
  }
}