Open Inventor

Mr. Hartley shows how to do interactive 3-D programming using Open Inventor, Release 2, which he used to create the images on our cover.
How does it work?

Inventor contains mostly 3-D objects and their associated attributes: geometric shapes, colors, lights and 3-D object manipulators. These are rendered with OpenGL or a similar API, such as Brian Paul's Mesa (see Resources).

The Inventor Xt Component and Utility Library provides widgets and utilities for event handling, rendering, viewers and editors that can manipulate the scene graph directly. These events include selection, picking and highlighting of nodes, keyboard and mouse handling, and processing Xt and Motif callback functions.

What else do I need?

For standard GUI design, we can resort to Motif (MFC under Windows NT/95), or whatever happens to be our favorite 2-D GUI API. The standard components such as the predefined viewer classes have been set up to make use of Motif and Xt under UNIX/Linux using the SoXt classes. These classes have been emulated under Windows, so that we have a somewhat easier time of porting code between platforms.

I find Motif to be a little cumbersome to program, so I've been using Viewkit, available from, in conjunction with RapidApp running on Irix to build the GUI and generate the C++ code stubs. Viewkit is freely available for SGI and Linux workstations, and the RapidApp code compiles cleanly under Linux. The GUI could have been built by hand, totally under Linux, but it helps the design process to be able to work interactively. Also, building on both platforms may help to make sure that portability issues spring up earlier in the design process.

Motif is not an absolute prerequisite under UNIX. Mark J. Kilgard, of SGI until recently, illustrates using Open Inventor without Motif in his book Open GL Programming for the X Window System (see Resources).

“Hello Cone”, an Inventor Sample

Let's look at a quick illustration of how easy it is to set up a scene graph and viewer. Example 2-4 of The Inventor Mentor presents “Hello Cone” using a standard scene viewer. (See Figure 1.) The figure shows three main areas of the SceneViewer: three thumbwheels, eight side buttons and a render area.

Figure 1. Scene Viewer with “Hello Cone”

The eight buttons handle object selection/picking, viewpoint manipulation, help, returning to home viewpoint, setting a new home viewpoint, executing viewAll to see the whole scene, seeking to a point and toggling the camera type between orthographic and perspective.

The three thumbwheels handle manipulation of the scene's viewpoint by rotating the camera angle about the X and Y axes and traversing along the Z axis to obtain a zoom effect.

The render area is the most interesting. The mouse can be used to get the same effect as the thumbwheels. Mouse button one will allow the user to select the object, and if the mouse is moving when it is released, the object will continue spinning in the direction that the mouse cursor was moving. Mouse button two will allow the user to pan up/down and left/right, and if the control key is pressed, zoom in and out. Mouse button three causes a pop-up menu to appear that allows the user to set various attributes (such as rendering “as-is”, hidden-line, wire frame, points-only and bounding box), preferences and displays of the thumbwheels and side buttons (known as decorations).

Listing 1 shows how simple it is to create this program. The first seven lines are the minimum header files. Inventor has 553 different include files. This may seem like a lot; however, each is very specialized, and selecting only the needed ones will speed up compilation time. If I wanted to, I could have simply included Inventor/So.h and let the compiler process all of the “So” prefixed files.

The first two executing lines after main create the main window widget and invoke SoXt::init. This is an essential part of the program, because here, Inventor is bound to Xt event handling so that its sensors will work properly. SoXT::init is also where the licensing code is called. Failing to invoke init will result in a core dump.

To be visible, each scene graph must have a node to attach to a viewer. In the listing, I am using a SoSeparator which saves the traversal state before being entered and restores it afterwards. This serves to prevent the attributes of its child nodes from affecting other parts of the scene graph that follow. A separator can include lights, cameras, coordinates, normals, bindings and all other properties. Separators also provide caching and culling of their children based on bounding box calculations during picking and rendering.

Once you create a node and pass it to the scene graph, Inventor takes over. Inventor nodes are always created dynamically with the C++ new command—never on the stack. Each node has a reference count, starting with zero when created, and incrementing and decrementing as nodes are added and removed as children to other parent nodes. When this reference count drops from one to zero, Inventor automatically deletes the node.

During traversal, the node is referenced and then dereferenced as the scene graph is traveled over. If we had not done a ref, the first time we traversed the scene graph its reference count would have incremented as it was entered, moving its reference count to one, and then decremented it back to zero as it was left, automatically deleting the node and any children whose reference counts had also dropped to zero. We would have been left wondering where our node(s) went.

Next, we add a material property as a child to the root node. It has a diffuse color or (1.0,0.0,0.0), which corresponds to full red, with red, green and blue (RGB) quantities being expressed as floating-point values between 0.0 and 1.0.

A cone is now added to the root node. Its default values are one unit for base and one for the length. It is located at the origin 0,0,0, and when unrotated, points one unit up from the base along the Y axis. Since the cone comes after the material property specifying the color red mentioned above, the cone inherits its attributes and is also red.

Inventor traverses its scene graph by starting at the root node and traveling down and to the right. Since OpenGL is a state machine, once we set an attribute, it will retain that value until changed.

Now we create the SceneViewer, passing the widget of our parent window, hooking our scene graph to it and setting the window title.

The show and hide methods call XtManageChild and XtUnmanageChild if a sub-widget is passed to it. If the widget is a shell widget, show will call RealizeWidget and XMapWindow, and if it is iconised, it will raise and de-iconify it. The hide method will call XUnmapWindow.