A typical use of an SoCallback SoCallback SoCallback node is to make calls to OpenGL. At the beginning of the callback function, you need to check the action type and then proceed based on the type of action that has been applied to the node. Typically, you are interested in the render action:
if(action->isOfType(SoGLRenderAction::getClassTypeId())) { ...execute rendering code .. }
if(action is SoGLRenderAction) { ...execute rendering code .. }
if(action instanceof SoGLRenderAction) { ...execute rendering code .. }
The effects of a callback node may not be cacheable, depending on what it does. For example, if the callback node contains shapes whose geometry is changing, it should not be cached. In Example 28.1, “ Using a Callback Node ”, the callback node creates a checked background, which can be cached because it is not changing.
If a callback node relies on any information outside of Inventor that may change (such as a global variable), it should not be cached. To prevent Inventor from automatically creating a cache, use the SoCacheElement::- invalidate() method from within a callback. For example:
void myCallback(void *myData, SoAction *action) { if (action->isOfType(SoGLRenderAction::getClassTypeId())) { SoCacheElement::invalidate(action->getState()); //makes sure this isn't cached //...make OpenGL calls that depend on a global variable...// } }
void MyCallback(SoAction action) { if (action is SoGLRenderAction)) { SoCacheElement.Invalidate(action.GetState()); //makes sure this isn't cached //...make OpenGL calls that depend on a global variable...// } }
callback.setCallback(new SoCallback.CB() { public void invoke(SoAction action) { if ( action instanceof SoGLRenderAction ) { SoCacheElement.invalidate(action.getState()); //makes sure this isn't cached //...make OpenGL calls that depend on a global variable...// } } }
Be careful when opening an OpenGL display list inside an SoCallback SoCallback SoCallback node. Recall from Chapter 8, Applying Actions that the Inventor render cache contains an OpenGL display list. Only one OpenGL display list can be open at a time, and a separator node above the callback node may have already opened a display list for caching. If your callback node opens a second display list, an error occurs. Use the SoCacheElement::anyOpen() method to check whether a cache is open.
Example 28.1, “ Using a Callback Node ” creates an Inventor render area. It uses Inventor to create a red cube and a blue sphere and then uses an SoCallback SoCallback SoCallback node containing GL calls to draw a checked “floor.” The floor is cached automatically by Inventor. Note that the SoXtRenderArea automatically redraws the scene when the window is resized. Example 28.2, “ Using a GLX Window ”, which uses a GLX window, does not redraw automatically.
Both Examples 17-2 and 17-3 produce the same image, shown in Figure 28.1, “ Combining Use of Inventor and OpenGL ”.
Example 28.1. Using a Callback Node
#include <GL/gl.h> #include <Inventor/SbLinear.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/SoXtRenderArea.h> #include <Inventor/nodes/SoCallback.h> #include <Inventor/nodes/SoCube.h> #include <Inventor/nodes/SoDirectionalLight.h> #include <Inventor/nodes/SoLightModel.h> #include <Inventor/nodes/SoMaterial.h> #include <Inventor/nodes/SoPerspectiveCamera.h> #include <Inventor/nodes/SoSphere.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoTransform.h> float floorObj[81][3]; // Build a scene with two objects and some light void buildScene(SoGroup *root) { // Some light root->addChild(new SoLightModel); root->addChild(new SoDirectionalLight); // A red cube translated to the left and down SoTransform *myTrans = new SoTransform; myTrans->translation.setValue(-2.0, -2.0, 0.0); root->addChild(myTrans); SoMaterial *myMtl = new SoMaterial; myMtl->diffuseColor.setValue(1.0, 0.0, 0.0); root->addChild(myMtl); root->addChild(new SoCube); // A blue sphere translated right myTrans = new SoTransform; myTrans->translation.setValue(4.0, 0.0, 0.0); root->addChild(myTrans); myMtl = new SoMaterial; myMtl->diffuseColor.setValue(0.0, 0.0, 1.0); root->addChild(myMtl); root->addChild(new SoSphere); } // Build the floor that will be rendered using OpenGL. void buildFloor() { int a = 0; for (float i = -5.0; i <= 5.0; i += 1.25) { for (float j = -5.0; j <= 5.0; j += 1.25, a++) { floorObj[a][0] = j; floorObj[a][1] = 0.0; floorObj[a][2] = i; } } } // Draw the lines that make up the floor, using OpenGL void drawFloor() { int i; glBegin(GL_LINES); for (i=0; i<4; i++) { glVertex3fv(floorObj[i*18]); glVertex3fv(floorObj[(i*18)+8]); glVertex3fv(floorObj[(i*18)+17]); glVertex3fv(floorObj[(i*18)+9]); } glVertex3fv(floorObj[i*18]); glVertex3fv(floorObj[(i*18)+8]); glEnd(); glBegin(GL_LINES); for (i=0; i<4; i++) { glVertex3fv(floorObj[i*2]); glVertex3fv(floorObj[(i*2)+72]); glVertex3fv(floorObj[(i*2)+73]); glVertex3fv(floorObj[(i*2)+1]); } glVertex3fv(floorObj[i*2]); glVertex3fv(floorObj[(i*2)+72]); glEnd(); } // Callback routine to render the floor using OpenGL void myCallbackRoutine(void *, SoAction *) { glPushMatrix(); glTranslatef(0.0, -3.0, 0.0); glColor3f(0.0, 0.7, 0.0); glLineWidth(2); glDisable(GL_LIGHTING); // so we don't have to set normals drawFloor(); glEnable(GL_LIGHTING); glLineWidth(1); glPopMatrix(); } main(int, char **) { // Initialize Inventor utilities Widget myWindow = SoXt::init("Example 17.1"); buildFloor(); // Build a simple scene graph, including a camera and // a SoCallback node for performing some GL rendering. SoSeparator *root = new SoSeparator; root->ref(); SoPerspectiveCamera *myCamera = new SoPerspectiveCamera; myCamera->position.setValue(0.0, 0.0, 5.0); myCamera->heightAngle = M_PI/2.0; // 90 degrees myCamera->nearDistance = 2.0; myCamera->farDistance = 12.0; root->addChild(myCamera); SoCallback *myCallback = new SoCallback; myCallback->setCallback(myCallbackRoutine); root->addChild(myCallback); buildScene(root); // Initialize an Inventor Xt RenderArea and draw the scene. SoXtRenderArea *myRenderArea = new SoXtRenderArea(myWindow); myRenderArea->setSceneGraph(root); myRenderArea->setTitle("OpenGL Callback"); myRenderArea->setBackgroundColor(SbColor(.8, .8, .8)); myRenderArea->show(); SoXt::show(myWindow); SoXt::mainLoop(); }