Open Inventor FAQs - General Reference Info


How do I get Open Inventor to do flat shading?

The creaseAngle field of the SoShapeHints node is one factor in whether one gets flat shading or not. The crease angle is the angle between the normals of two adjacent faces. If the angle between the normals is greater the defined creaseAngle then Inventor computes separate normals for each face and the object appears faceted.

  • To get flat shading,
    • Specify a MaterialBinding of PER_FACE (see SoMaterialBinding)
    • Specify one color per face (see SoMaterial, SoVertexProperty)
    • Specify a creaseAngle of zero (see SoShapeHints)
  • To get Gouraud (smooth) shading, set the creaseAngle to pi.

The default crease angle is 0.5.

A material binding of PER_VERTEX_INDEXED allows a different color at each vertex, but shared vertices between faces will have the same color. If the angle between faces is less than the crease angle and Open Inventor is computing the normals, the faces will share the same normal, and the faces will be smooth shaded.

Note the "note" of the Light Model node : A shading model is not explicitly specified: shading is dictated by a combination of

  1. the material specification
  2. the lighting model (Phong by default), and
  3. the normal bindings

How can I change the appearance of a manipulator? For example, I am using an SoHandleBoxDragger in my app and I only want it to scale and translate in the X and Y directions. I also want to get rid of the extruders in the middle of the box. How can I restrict the dragger to the xy direction and also to get rid of the unwanted extruders?

See Customization a Dragger


How do I do a background image? I want a background image to be fixed in a scene with all graphics rendered on top of it. For example, Mona Lisa as a background, with spinning cubes on top.

Starting in Open Inventor 5.0, there is a node, SoImageBackground, that provides a convenient way of rendering an image in the background of the scene. See the Reference Manual for details. An example program is provided in the examples/features directory.


For Large Model Viewing (especially SoSimplify Action), which geometry nodes get decimated by SoDecimator? Which don't?

Some geometry nodes can be decimated, and others can't depending on the algorithm used by the decimator. Here is the list of geometry nodes that can be decimated:

  • SoFaceSet
  • SoIndexedFaceSet
  • SoIndexedNurbsSurface
  • SoIndexedTriangleStripSet
  • SoNurbsSurface
  • SoTriangleStripSet
  • SoVRMLIndexedFaceSet

Here is the list of geometry nodes that cannot be decimated:

  • SoAnnotText3
  • SoAsciiText
  • SoCone
  • SoCube
  • SoCylinder
  • SoIndexedLineSet
  • SoIndexedNurbsCurve
  • SoLineSet
  • SoNurbsCurve
  • SoPointSet
  • SoQuadMesh
  • SoSphere
  • SoText2
  • SoText3
  • SoVRMLBox
  • SoVRMLCone
  • SoVRMLElevationGrid
  • SoVRMLExtrusion
  • SoVRMLIndexedLineSet
  • SoVRMLPointSet
  • SoVRMLSphere
  • SoVRMLText
  • Any geometry under an SoLOD
  • Any geometry under an SoLevelOfDetail
  • Any geometry under an SoLevelOfSimplification
  • Any geometry under an SoVRMLLOD

The decimator does not decimate nodes, such as SoCone, that adjust their complexity automatically (based on SoComplexity and/or the viewer-requested decimation level). The decimator also does not decimate nodes such as SoVRMLElevationGrid where decimation would not preserve the type of geometry. Decimated geometry is always an indexed face set.


Does Open Inventor support Boolean operations? For example, how can I create a model of a cube with a cylindrical hole, something like myGraph = Cube - Cylinder?

Starting with Open Inventor 3.0, the SolidViz extension is available. It provides solid modeling nodes for describing 3D shapes as combinations of other shapes using Boolean operations such as merge, intersection, and subtraction.

In Open Inventor 8.6, a new node SoCSGShape can be used for doing the same kind of thing. This node is a shape that can be used as clipping region for volumeViz.

Please note: SolidViz implements a visual simulation of CSG (Constructive Solid Geometry) shapes. A visual simulation of a CSG shape can be achieved in many cases at interactive speed by a special rendering method taking advantage of accelerated OpenGL rendering and the stencil buffer.


I am using SoCoordinate3 and SoFaceSet to represent some polygons. When the draw style is "wireframe", the tessellation of the polygons appears and is not very pretty. Wireframe mode should show the only the edges of each polygon. Would using an SoVertexProperty node solve this problem?

This is a well known "feature" of Open Inventor. Using an SoVertexProperty node would not produce a different result.

The effect occurs because Inventor uses the same data for both Filled and Wireframe, just changing the OpenGL polygon mode. This is more efficient because Inventor does not have to recreate cached display lists and also does not have to include additional code in the primitive nodes to generate GL_LINES instead of GL_TRIANGLES.

It would be a lot of work to change Inventor's behavior.

The best thing we can recommend is to use a Switch node with an IndexedFaceSet and an IndexedLineSet by sharing the actual coordinate data in a vertex property node.


How does Open Inventor convert a quaternion into a rotation matrix?

Here is the algorithm:

void SbRotation::getValue(SbMatrix &matrix) const
{
  SbMat m;
  m[0][0] = 1 - 2.0 * (quat[1] * quat[1] + quat[2] * quat[2]);
  m[0][1] =     2.0 * (quat[0] * quat[1] + quat[2] * quat[3]);
  m[0][2] =     2.0 * (quat[2] * quat[0] - quat[1] * quat[3]);
  m[0][3] = 0.0;

  m[1][0] =      2.0 * (quat[0] * quat[1] - quat[2] * quat[3]);
  m[1][1] = 1 - 2.0 * (quat[ 2] * quat[2] + quat[0] * quat[0]);
  m[1][2] =      2.0 * (quat[1] * quat[2] + quat[0] * quat[3]);
  m[1][3] = 0.0;

  m[2][0] =      2.0 * (quat[2] * quat[0] + quat[1] * quat[3]);
  m[2][1] =      2.0 * (quat[1] * quat[2] - quat[0] * quat[3]);
  m[2][2] = 1 - 2.0 * (quat[1] * quat[1] + quat[0] * quat[0]);
  m[2][3] = 0.0;

  m[3][0] = 0.0;
  m[3][1] = 0.0;
  m[3][2] = 0.0;
  m[3][3] = 1.0;

  matrix.setValue(&m[0][0]);
}

Is there any way to suppress fill using SoVRMLIndexedFaceset? I was hoping to achieve a "hidden line" effect using VRML.

No, there is not. In VRML, face sets are always filled solid and line sets are always "see through". There does not appear to be any reasonable way to specify a "hidden line" effect using the VRML file format. The closest you could come would be to insert the geometry twice -- once as a face set with the fill color set to the background color and once as a line set. Theoretically this would give the desired result, but in practice it is likely to produce lots of z-buffer artifacts ("stitching").

Any hidden line effect would have to be an option in the viewer. We do not know of any VRML viewers with this option, but all of our Inventor-based viewers have this option. Note however that only a face set can "hide" lines. Line sets are "see through" by definition.


Which VRML 2.0 (now officially VRML97) nodes does Open Inventor support?

Go to the Table of Contents of the Open Inventor on-line Reference Manual. Select Modules/Inventor/VRML/VRML2.0 nodes to go to the VRML 2.0 page which lists all of the VRML nodes. Some nodes are marked with "Action not implemented" or "Action partially implemented."

If a node is marked "Action not implemented", Open Inventor can read and write VRML files containing that node. However, when it reads that node, it will not perform any action.

If a node is marked "Action partially implemented", see the help file to find out which features are implemented.

Unmarked nodes are fully implemented.


I have read my VRML scene into Open Inventor. Now how do I access the VRML scene to perform actions such as moving objects from one location to another?

First you should read the Inventor Mentor and the Open Inventor User's Guide to understand the basic concepts of the Open Inventor scene graph. However this might not give you the "big picture" for this specific situation. Here is a mini-tutorial. But please note there are *many* ways to organize a scene graph, based on your application's requirements, so don't assume this is the only possible way! Generally this discussion is applicable to both VRML and Open Inventor files.

The important question is: In your VRML file, what distinguishs one "object" from another? You need to be able to answer this question in order to manipulate the objects as separate entities.

It's almost certain that each "object" will be a collection of nodes under some sort of grouping node. For example, in a VRML file:

DEF Object27 Transform {
  children [
    Cone {}
  ]
 }

might be one (trivial) object in the incoming file.

Typically the file creator will assign a name to each grouping node that represents an independent object. For example, the file creator might simply assign them names "Object0000" through "Object0953". Since you are modeling the scene, you should adopt some useful naming convention. The node names will show up as "DEF name" in the output VRML file, as above.

When you read the file into Open Inventor, each node in the file will result in a corresponding node in the scene graph. The grouping node above is an SoVRMLTransform, for example.

Let's say you wanted to find a particular object programmatically. If you know the objects all have unique names, you can do this:

SoNode *pNo de = SoNode::getByName( "Object51" );

However this only gives you a pointer to the node, it doesn't tell you anything about where it is in the scene graph. To get that info, use a SearchAction like this (assuming sceneRoot is the root of the entire scene graph, i.e. what SoDB::readAll returned to you):

SoSearchAction sa;
sa.setName( "Object43" );
sa.apply( sceneRoot );
SoPath *pPath = sa.getPath();
 SoNode *pNode = pPath->getTail();

If you want to be able to select objects by picking, look at the discussion about SoSelection and redrawOnSelectionChange in the Mentor (and other places). Remember though, that picking will select an actual geometry node, so the node at the tail of the selection path will be (for example) an SoVRMLIndexedFaceSet. To get a path to the grouping node that represents the entire object (which is presumably an ancestor of the geometry node), you have two choices. You can implement a pickFilterCallback (see the Mentor for details) or you can "manually" go up the path from the tail looking for a grouping with an appropriate name.

Now you have the objects identified by some naming scheme and can get a pointer (or path) to a specific object.

In order to control "object attributes" like position, scale, rotation, material, etc. in a classic Open Inventor scene graph you might put a set of attribute nodes as the first children of each object. For example:

DEF Object35 Separator {
  Transform {}
  Material {}
  ... other object nodes ...
 }

These nodes don't have to exist when the file is read in. You can create them when (and if) they are needed, for example, given a pointer to an object we want to rotate:

// given pNode points to the object's Separator
SoTransform *pTra n;
SoNode *pFirstChild = pNode->getChild( 0 );
if (pFirstChild->isOfType(SoTransform::getClassTypeId()))
  pTran = (SoTransform*)pFirstChild;  // Already got one
else {
  pTran = new SoTransform;        // Make one
  pNode->insertChild( pTran, 0 ); // Put as first child
}
pTran->rotation.setValue( SbRotation( ...   // set rotation

There are many variations on this theme, for example giving the attribute nodes meaningful names. You could also use node kits for this purpose since they automatically create nodes (that are part of their catalog) when they are needed.

The position/rotate/scale part is actually easier with VRML nodes! The grouping node that defines an object will typically be an SoVRMLTransform node. This node is effectively the combination of an SoSeparator and an SoTransform. So given a pointer to an object you want to rotate, you only have to do this:

SoVRMLTransform *pNode = ... // somehow we got this pointer
pNode->rotation.setValue( SbRotation( ... // set rotation 

I was trying to map a 3D point in world coordinates to a 2D point in screen coordinates. I used a function called ProjectToScreen in class SbViewVolume, but could not get the desired results. Do you have an example to do that?

You are on the right track using the ProjectToScreen method. Usually people want to go the other way -- from 2D screen coordinates to 3D modeling coordinates, and the boxZoom example in the src\Inventor\examples\Ivf folder (Win32 only) is a good example.

The only example we distribute that uses ProjectToScreen is the ToolMaker example in the src\Inventor\examples\ToolMaker\09.Highlights folder. The purpose of the code is to render selected objects as 2D rectangles in the overlay planes. The updateBbox method in the code projects the 3D bounding box for the selected object onto the screen and renders it as a 2D rectangle.


Does Open Inventor support "hidden line removal" mode? I know Open Inventor has HLR mode but when you set it, you see all the triangles. I am interested in having an HLR mode where there are no triangles, just the contours.

We presume you are referring to the hidden line drawing mode that the viewers support. Yes, you do see the triangles. Open Inventor first draws the polygonal faces in the background color (OpenGL Polygon mode=GL_FILL) then renders the same vertices using OpenGLPolygon mode=GL_LINE. The faces are tessellated into triangles prior to being sent to OpenGL, hence the triangle edges that you see.

We do not currently have plans to implement the kind of hidden line removal you are describing. However, you can simulate the hidden line removal effect you are seeking as follows:

In your scene graph, insert the geometry twice -- once as a face set with the fill color set to the background color and once as a line set to define the edges. In order to avoid stitching, you might want to apply a slight offset using SoPolygonOffset.

Note: It is not necessary to insert the geometry coordinates twice. With a little bit of forethought, you can use the same coordinate node for the face set and the line set -- if you use indexed face sets and line sets.


I'm having difficulty trying to understand the screenArea parameter of the SoLevelOfDetail node. The Inventor Mentor states that it is in "square pixels", but I'm having trouble relating that to anything else. It does not seem to be related to the getSize of the Render Area from what I have tested, and I don't see how to determine it through any boundingBoxAction calls. How should I be computing the screen area?

The short answer is to use SoLOD rather than SoLevelOfDetail.

The long answer follows below:

SoLevelOfDetail is a deprecated class. We don't recommend its use. The cost of computing the screen area (which involves quite a bit work -- described below) tends to overwhelm the benefits of using SoLevelOfDetail. The computation may take more time than it would have taken to just render the geometry.

We would recommend the use of SoLOD instead because the computation it uses to choose a particular child (distance to the camera) is much simpler and yields the same kind of results: close to the camera, you get a more detailed picture; further back, you get fewer details.


We want to render 3D files that have 2 million points (i.e. 200 by 200 by 50) where each point will have a certain 3D coordinate and a value at that coordinate. The coordinates will not be equidistant. Can you do this with Open Inventor for Win32?

Yes. The main component that Open Inventor provides over OpenGL are viewers that allow the viewing and manipulation of your data set. You can either have your application read your data and place it in Open Inventor primitives and display the resulting graphics database that is generated (called the scene graph) using a render area or viewer (and optionally write out the scene graph to a file in the Inventor file format, which can be read by the application or other viewer application such SceneViewer, which is supplied with Open Inventor), or you can manually or programmatically create an Inventor file with your data in it which can be read by any Inventor viewer application such as SceneViewer.

Open Inventor has a PointSet primitive which is an efficient way of displaying point data. Your description of the application implies that you may also need to display text (the data value) at each point. In order to do this efficiently you might want to subclass the PointSet object and create a custom Open Inventor node for displaying your data. The Open Inventor class library is highly extensible for this reason. Custom nodes inherit the powerful built-in methods for displaying, picking, searching, etc., in the scene graph.

Also note that Open Inventor automatically optimizes its use of the OpenGL library for rendering. For example, when appropriate, Open Inventor will create OpenGL display lists or vertex buffer objects so that rendering can proceed at full speed with (almost) no overhead for traversing the scene graph.


How can I prevent a callback node from being cached?

The recommended method is:

#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/elements/SoGLCacheContextElement.h>

void myCallback( void *userData, SoAction *action ) {
  SoState *state = action->getState() ;
  if (action->isOfType(SoGLRenderAction::getClassTypeId())) {
    SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
    // Other rendering code
  }
}

The call to "shouldAutoCache" shown above will prevent Open Inventor's automatic render caching mechanism from including this node in a render cache. In other words, no Separator (or other caching node) above this Callback node will be able to create a render cache. You should make sure that any "sibling" geometry that should be cached is under its own Separator.

If necessary you can use this call:

SoCacheElement::invalidate( action->getState() ); 

to completely eliminate the possibility of caching this node.


[Win32] Is there a way to suppress Open Inventor automatic redraw on Windows when the window including the render area is exposed or de-iconified?

Sure, more than one. Here are some possibilities:

  • Override Inventor's redraw method and make your own decision about whether to actually do the redraw.
  • In an MFC program, use the scheme illustrated in the gdiRect example to synchronize the Inventor repaint on expose with the app's OnPaint method, but instead of calling Inventor's redraw method, just call ValidateRect (Win32) on the Inventor window to remove its pending WM_PAINT message from the queue.
    The gdiRect example is available here:
    $OIVHOME/src/Inventor/contrib/FAQ/gdirect.zip

How do I create an array of objects such as SoCone? I tried to do:
  SoCone *cones[]=new[] SoCone;
but it doesn't work!

Create cones:

const int numCones = 10;
SoCone **cones = new SoCone* [numCones];
for (int i = 0; i < numCones; i++) {
  cones[i] = new SoCone;
  cones[i]->ref();  // So we control lifespan
} 

Clean up cones:

for (int i = 0; i < numCones; i++) {
  cones[i]->unref();  // destroy
}
delete [] cones; 

Note that you cannot allocate an array of SoCone objects or of any other reference counted object. The reason is that reference counted objects will "automagically" destroy themselves when their ref count goes to zero. If an object in the middle of your array destroyed itself and tried to free its memory (in the middle of the allocated block of memory), at best your app would crash.


How do I prevent an SoCallback node from being render cached? I need to make sure my callback is called on every render traversal.

By default the callback node may become part of a render cache (OpenGL display list). In this case, so long as the cache remains valid, the callback node will not be traversed by the render action. If the callback function always makes the same OpenGL calls, this might be OK since the OpenGL calls will be part of the cache. But if the callback function interacts with the application or performs different actions at different times, then it should disable render caching. For example:

SoState *state = action->getState();
SoGLCacheContextElement::shouldAutoCache(state,  SoGLCacheContextElement::DONT_AUTO_CACHE);

What happens if an Open Inventor geometry file contains multiple file headers?

When Open Inventor reads the file, it processes the first file header as the file header. Subsequent file headers in the file are treated as comments.


How can I save a scene graph such that it is "self-contained"?

See also the related FAQ question "Can you give me more info about writing a 'self-contained' file?"

There are a couple of general approaches to this problem.

  1. You can apply a search action or a callback action to the scene graph for each of the types of nodes that might have an external file reference, and when a node is found, get the filename and add it to your list of files to be saved. Open Inventor does not keep a list of dependent files.
  2. You can create a portable iv file which contains all the information from referenced files. This is a somewhat less efficient way to store the data, however.

    Once the original .iv file has been read, all of the information is in memory. You can tweak the scene graph so that when the scene graph is saved, everything is saved in one file.

    For instance, an SoTexture2 node has the mutually exclusive filename and image fields. If the filename field was used in the original file, then normally when you save the file, the filename field would again be used, and the texture file would be external to the .iv file. However, if the touch method is called on the image field, Open Inventor detects the change and will use the image field when the file is saved, thereby saving the texture in the .iv file. Note that texture images embedded in a .iv file are not compressed. The default ASCII format for images is very verbose.

Whether making a list of filenames or causing the data to all be saved in the .iv file, the first step is the same -- that is, finding the nodes with the external file references.


Can you give me more info about writing a "self-contained" file?

See also the related FAQ question "How can I save a scene graph such that it is 'self-contained'?"

CAN BE SELF-CONTAINED
The following nodes have image fields as well as filename fields. If you call the "touch" method on the image field, Open Inventor detects the change and will use the image field when the file is saved. This way the texture (i.e., the contents of the image field) is written to the .iv file.

  • SoImage
  • SoTexture2
  • SoTexture3
  • SoFile -- This node reads children from a named file. In order to make a self-contained file, you will need to search the scene graph, find each SoFile node, query its children, then replace the node with its children.

ivcat is a utility that can be used to replace SoFile nodes with their children and to expand SoTexture2 nodes to explictly include the texture data rather than a file name. See the Open Inventor help file for information on ivcat.

CANNOT BE MADE COMPLETELY SELF-CONTAINED
Some nodes intrinsically get their data from external files. A scene graph that contains any of these nodes cannot be made completely self-contained. Here's a list of them. (As new nodes are added, this list may need to be expanded.)

  • SoVRMLAnchor
  • SoVRMLAudioClip
  • SoVRMLBackground
  • SoVRMLImageTexture
  • SoVRMLInline
  • SoVRMLMovieTexture
  • SoVRMLScript
  • SoWWWAnchor
  • SoWWWInline
  • SoPattern -- The pattern definition is always specified in a separate file or programmatically. SoPattern does not have a field for storing the pattern. Thus an .iv file that uses an SoPattern node will not be fully self-contained -- unless the default pattern (defined in the Open Inventor DLL) is used.
  • SoExtTexture2 -- It has a filename field but no image field. It is not possible to store the image data with the node.

How can I tell Open Inventor to ignore a particular node? Is there a way to set an ignore flag on a scene node (object) to disregard it, in the same manner that we can ignore a field?

The brute force way is set the ignore flags of all of the node's fields to TRUE. The more elegant way is to replace the node with a switch node and place the node under it. By "switching" the node out of the scene graph traversal, one essentially removes its effect on traversal.


How can I customize the behavior of SoWriteAction? For example, how can I prevent certain nodes from being written out?

The following file:

$OIVHOME/src/Inventor/contrib/FAQ/writev21action.zip

contains a customized SoWriteAction that disables writing for nodes added after V2.1. (Note that it does not disable the VRML nodes. However, it would easy to add them following the pattern in the attached code.)

Also included is a simple "test drive" program.


How does Open Inventor transform normalized device coordinates (0..1) to pixel coordinates?

Open Inventor's conversion is the most straightforward one and is based on the "window" size specified to SbViewportRegion (this is the width and height of the window on screen in pixels). So given width and height in pixels:

int x_pixelVal;
float x_ndcVal;
x_pixelVal = (int)(x_ndcVal * width);
x_ndcVal = (float)x_pixelVal / (float)width; 

If I set "override" on a Material node, how can I turn off override later in the traversal?

Once you set override on a Material node, for example:

pMaterial->setOverride(TR UE);

the override is in effect for all nodes at or below this level in the scene graph. Simply calling setOverride(FALSE) on a subsequent Material node has no effect because the previous override causes this node to be ignored. However you can turn off override by accessing the SoOverrideElement directly in a callback (see the SoCallback node). Note that some actions do not enable all the traversal state elements, so your callback function should check that the current action is an SoGLRenderAction.

See SoOverrideElement.h for the methods to disable override on other traversal state elements.

Example callback function:

void disableOverrideCallback( void *myData, SoAction *action )
{
  if (!action->isOfType(SoGLRenderAction::getClassTypeId()))
    return;

   SoState *state = action->getState();
  SoNode *node = action->getCurPathTail(); 

  SoOverrideElement::setAmbientColorOverride(state, node, FALSE);
  SoOverrideElement::setDiffuseColorOverride(state, node, FALSE);
  SoOverrideElement::setEmissiveColorOverride(state, node, FALSE);
  SoOverrideElement::setSpecularColorOverride(state, node, FALSE);
  SoOverrideElement::setShininessOverride(state, node, FALSE);
  SoOverrideElement::setTransparencyOverride(state, node, FALSE);
  }

Picking - If my primitives (e.g. SoIndexedFaceSet) contain multiple faces, how can I determine which face was selected?

First, get the SoPickedPoint object. You can get this directly from an SoRayPickAction. If you use SoSelection, you have to provide a pickFilter callback to get the SoPickedPoint.

Once you have an SoPickedPoint, call getPath() and look at the typeId of the tail node. That will tell you which subclass of SoDetail will be returned from getDetail() (you have to cast the return value). In this case, an IndexedFaceSet, it would be an SoFaceDetail.

The getFaceIndex() method tells you which face in the primitive was picked.

Something like this:

const SoPickedPoint *pPickedPt = ...

SoPath *pPickPath = pPickedPt->getPath();
SoNode *pTailNode = pPickPath->getTail();
if (pTailNode->isOfType(SoIndexedFaceSet::getClassTypeId())) {
  const SoFaceDetail *pFaceDetail = (SoFaceDetail*)pPickedPt->getDetail();
  int faceIndex = pFaceDetail->getFaceIndex();
  }

How do I do a gradient background for my viewer window?

SoGradientBackground provides a convenient way of rendering a gradient in the background of the scene. See the Reference Manual for details. An example program is provided in the examples/features directory.


Is there a way to "capture" the normals computed by Open Inventor and save them for future use?

Yes! We have a sample (VC++) program showing how to do this:

$OIVHOME/src/Inventor/contrib/FAQ/getnormals.zip

Only vertex shapes, classes derived from SoVertexShape, have explicit normals. It is not currently possible to get the normals used for SoCone, SoSphere, etc. In general, there are three cases:

  1. First, normals may have been specified in an SoVertexProperty node directly associated with the vertex shape. Simply get the value of the vertex shape's vertexProperty field.
  2. Second, normals may have been inherited from an SoNormal or SoVertexProperty node in the scene graph. An easy way to get these is to apply an SoCallbackAction, possibly using a path to the vertex shape. In the callback function, use the SoCallbackAction's getNumNormals and getNormal methods.
  3. Third, the normals may have been computed by Open Inventor. If you are using Open Inventor 3.0 or later, call the vertex shape's getNormalCache method. If using an older version, see the example program. Use the SoNormalCache's getNum and getNormal methods. (This class is considered an "extender" tool and is not documented in the Open Inventor help file.)

How can I programmatically take a "screen shot" of an SoExaminer viewer window?

The Mentor example in %OIVHOME%\src\Inventor\examples\Mentor\Cxx\09.1.Print.cxx does pretty much what you are requesting.

You will notice one difference between the view you see on the display and the view you get in the output file -- the background color. As noted in the comments in the code, a white background is used instead of black -- it uses up less ink or toner :-)


How can I get SoExtSelection to work in "additive" mode?

BACKGROUND INFO:
Additive mode means that if you select an object using SoExtSelection and then you select it again, it doesn't get deselected (the usual behavior). This way you can (for example) first select objects A and B, then B and C, resulting in the cumulative selection of objects A, B and C. Normally object B would be *de*selected by SoExtSelection the second time it was selected.

Internally, this deselection happens as follows: When you pick one or more objects, SoExtSelection checks to see if each picked object is already in the selection list and if it is, it removes it from the selection list. The result is that any object selected twice is deselected.

CODE TO USE:
To get around this behavior, you will need to use a pick filter callback. This callback allows you to take specific actions depending on what was picked.

In the pick filter callback, you will check to see if the picked object is already in SoExtSelection's selection list.

  • If it is not, the callback should return the path to the picked object.
  • If it is, the callback should return an empty path to SoExtSelection. SoExtSelection will not be aware that the object has been repicked, and therefore will not deselect it. The result will be additive selection by SoExtSelection.

Here's what the callback might look like:

#include <Inventor/SoPickedPoint.h>

SoPath *PickCB(void *userData,const SoPickedPoint *pick) {
  SoExtSelection *pSel = (SoExtSelection*)userData;
  SoPath *pPath = pick->getPath();
  SoNode *pTail = pPath->getTail();
  if (pSel- >isSelected( pTail )) {
    return new SoPath;
  }
  else {
    return pPath;
  }
}

Here's the code to add the callback to your scene graph:

root->setPickFilter Callback( PickCB, (void*)root );