Viewers are subclassed from SoXtRenderArea and can be thought of as smart rendering windows that respond to events and modify the camera. You can use one of the following classes as a base class for your own viewer:

SoXtViewer , the lowest base class for viewers, adds the notion of a camera to an SoXtRenderArea . The camera through which the scene is viewed is either found in the scene or created automatically by the viewer. SoXtFullViewer adds a decoration trim around the rendering area, which includes thumbwheels, a zoom slider, and push buttons. This class also creates a pop-up menu and a preference sheet with generic viewer functionality built into them. SoXtConstrainedViewer , the last base class, is used for viewers that have the notion of a world up-direction. These viewers constrain the camera orientation to prevent the user from being upside down relative to the given world up-direction (which defaults to +y).

The SoXtViewer class provides basic viewer functionality. This base class provides methods for changing the camera, including setCamera(), getCamera(), viewAll(), saveHomePosition(), and resetToHome- Position(). SoXtViewer also adds a headlight, as well as drawing styles, buffering types, and autoclipping. In addition,SoXtViewer adds support for seek, copy, and paste, enabling subclasses to redefine how a seek or a paste is performed.

Overview

If you create a viewer that is a subclass of SoXtViewer , you perform the following steps. Examples 10-3 and 10-4 illustrate each step.

Examples 10-3 and 10-4 show how to create a simple viewer derived from SoXtFullViewer , similar to the SoXtPlaneViewer . The left mouse button is used to translate the camera in the viewer plane and to seek objects. This new viewer redefines the decoration thumbwheels to translate the camera. It also defines mouse cursors to reflect the viewer's current state (viewing, seeking, or picking).

Defining the Constructor

The constructor for the viewer takes an

SoXtViewer::Type

parameter, which specifies whether the viewer is of type BROWSER (the default) or

Defining the Event-Processing Routines

Any new viewer must implement the processEvent() routine, defined in SoXtRenderArea , to send events directly to the scene graph. When viewing is turned on, the new viewer uses those events to manipulate the camera.

The base-class routine processCommonEvents(), defined in SoXtViewer , is first called when an event is received. This routine is used to handle a set of events that should be common across all viewers. These events are as follows:

Pressing the Escape key (toggles a viewer between Viewing and Picking mode)

Pressing the Home key (resets the viewer to Home Position)

Pressing the S key (toggles Seek mode on and off)

Pressing any of the four arrow keys (moves the camera left, right, up, or down)

The viewer calls the base class interactiveCountInc() and interactiveCountDec() methods when the left mouse button is pressed and released. These methods enable the viewer base class to call the user interactive start and finish callbacks and are also used to change the drawing styles and buffering types when interactive styles are chosen (for example, move wireframe).

The code to translate the camera using the new mouse position is called within the processEvent() routine (see the translateCamera() method in Example 10.4, “
SimpleViewer.c++
”).

In addition to showing how the camera can be translated given the mouse events received, the processEvent() routine also shows how seek functionality is supported in the viewer. This topic is explained in detail in the following section.

Implementing the Seek Function

The seek function moves the camera to the picked point (when detail seek is ON) or to the picked object (when detail seek is OFF). The seek functionality for viewers is provided by the base class SoXtViewer . The following public methods can be called to set seek parameters:

setDetailSeek()

specifies whether to orient the camera toward the picked point (detail seek is ON), or toward the center of the object's bounding box (detail seek is OFF). The default is ON.

isDetailSeek()

returns whether detail seek is ON.

setSeekTime()

sets the time for a seek to animate the new camera location. The default time is 2 seconds.

getSeekTime()

returns the seek time.

Subclasses can then simply call seekToPoint(), passing the mouse location, and the base class SoXtViewer performs the seek animation. By default, detail seek is ON, and the base class changes the camera to be the focal distance away from the picked point and aligned to the point normal. When detail seek is OFF, the camera centers itself on the object's bounding box and keeps its orientation the same (the picked point has no real importance; only the picked object is used in this case).

Our simple viewer example uses the seek functionality defined in the base class. If a viewer needs to redefine how seek is performed (SoXtFlyViewer and SoXtWalkViewer redefine it), the viewer can redefine the interpolateSeekAnimation() routine, which changes the camera.

The following protected variables are defined in SoXtViewer to help you redefine interpolateSeekAnimation() for a new viewer:

seekPoint, seekNormal

point and normal to seek

oldCamOrientation, newCamOrientation

old and new camera orientation

oldCamPosition, newCamPosition

old and new camera position

seekDistance

seek distance (either a percentage or an absolute value)

seekDistAsPercentage

whether the seek distance is a percentage or an absolute value

computeSeekVariables

whether the final camera seek values have been computed. This flag is set to FALSE when interpolateSeekAnimation() is first called on a new seek animation.

Using the Cursor for Feedback

It is often desirable to have a viewer change the cursor to reflect the viewer's state. The file SoXtCursors.h defines a set of X bitmaps that can be used for defining cursors. Some of these bitmaps were created for specific viewers, whereas others are generic enough to be reused across viewers. Most viewers have a different cursor during viewing and nonviewing modes. A generic viewer cursor is supplied in the file SoXtCursors.h.

To have a different cursor when viewing is on, the viewer needs to redefine the setViewing() method to set the correct cursor on the window. Similarly, if the viewer supports the seek functionality, it also needs to redefine the setSeekMode() method to change the cursor. Example 10.4, “
SimpleViewer.c++
” shows how to change the cursor.

X cursors can be defined only when the widget is actually mapped onto the screen. It is thus a good idea to define the cursors whenever the first event is received in the processEvent() routine, since an event guarantees that the window is mapped onto the screen.

Cursors should be defined on the render-area window, not on the window found in the X event structure. This is because the actual window events are received from changes when the viewer switches between single- and double-buffering. The render-area window, however, stays constant, so the cursor is correctly specified.

The following sections deal with more advanced features of viewers, such as the trim decoration around the render area and the viewer pop-up menu.

Using the SoXtFullViewer Trim Decoration

SoXtFullViewer is used as the base class for most viewers. This abstract class adds a decoration around the render area, a pop-up menu with viewer functions, and a preference sheet that can be used to customize a specific viewer. The decoration around the render area includes thumbwheels that duplicate direct viewing manipulation, a slider to change the camera zooming factor, and viewer/application push buttons. By default, the base class creates push buttons for viewing, home, set home, view all, and seek. Subclasses can easily add viewer-specific push buttons, as well as change the look of the decoration and the preference sheet. The creation of the decoration and preference sheet is accomplished by many small routines, so subclasses can redefine as much or as little as necessary.

SoXtFullViewer provides three thumbwheels around the render area. By default, these thumbwheels do nothing in the SoXtFullViewer base class and should therefore be implemented by each subclass. The subclass should implement functions so that the bottom and left thumbwheels duplicate the right-left and up-down functionality of the mouse during direct viewing. The right thumbwheel is used to dolly the camera (move forward and backward).

For convenience, when you are defining thumbwheel functionality and redefining the decoration layout, the base class SoXtFullViewer provides the following thumbwheel variables. These variables include thumbwheel widgets, previous values (helpful for obtaining incremental rotation), and labels:

rightWheel, bottomWheel, leftWheel

thumbwheel widget variables

rightWheelStr, bottomWheelStr, leftWheelStr

string label for each thumbwheel

rightWheelVal, bottomWheelVal, leftWheelVal

previous value of each thumbwheel

rightWheelLabel, bottomWheelLabel, leftWheelLabel

widget label for each thumbwheel

When a viewer is derived from SoXtFullViewer , it should set the correct labels on the thumbwheels, the pop-up menu, the preference sheet, and the window title. This needs to be done only once and therefore should be done in the constructor. Example 10.4, “
SimpleViewer.c++
” shows the code to fully support the SoXtFullViewer base class.

Adding Push Buttons

By default, the base class SoXtFullViewer creates a list of push buttons (XmPushButton widgets with pixmaps). The method buildViewer- Buttons(), which subclasses do not need to redefine, uses a list of push buttons to construct all the buttons within a form widget. The button's form widget is then laid out within the right-trim form widget. Subclasses can easily add or remove any number of buttons from the existing list of buttons by redefining the createViewerButtons() method and appending, inserting, and removing from the SbPList of buttons.

Our simple viewer example does not add any new viewer buttons, but here is some sample code that adds a push button to the existing list.

void
SoXtExaminerViewer::createViewerButtons(Widget parent)
{
// Get the default buttons
SoXtFullViewer::createViewerButtons(parent);
// Allocate our buttons - this simple case doesn't// set the XmNlabelType to be a pixmap, just a simple letter.
Arg args[2];
int n = 0;
XtSetArg(args[n], XmNshadowThickness, 2); n++;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
Widget button = XmCreatePushButtonGadget(parent, "P", args,
n);
XtAddCallback(button, XmNactivateCallback,
(XtCallbackProc)
SoXtExaminerViewer::pushButtonCB,
(XtPointer) this);
// Add this button to the list to have it laid out by the// parent class (removing a widget from the list will// prevent the corresponding push button from being laid// out and managed; therefore it will not show up in the// decoration).
viewerButtonWidgets->append(button);
}

Look at the file SbPList.h for a description of methods available on the SoXtFullViewer::viewerButtonWidgets protected variable.

The viewer default push buttons all have a 24-by-24-pixel size, and the decoration trim is designed with that in mind. It is therefore recommended that you create pixmaps of this size for viewer or application push buttons.

Changing the Preference Sheet

Preference sheets allow the user to customize the behavior of a viewer. A default preference sheet is created by the SoXtFullViewer class. Subclasses typically make changes to the default preference sheet to give the user control over viewer-specific parameters. Like the decoration in SoXtFullViewer , the preference sheet is made up of many small building blocks to make it easier for subclasses to redefine it. The following protected methods are used to build the different parts of the preference sheet:

When a subclass creates its own preference sheet, it only needs to redefine the createPrefSheet() routine and write it like the base class routine. The simple viewer example redefines the preference sheet to omit some of the default parts. Example 10.4, “
SimpleViewer.c++
” shows the createPrefSheet() method for simpleViewer.

Subclasses can easily add new items to the preference sheet by adding them to the widget list that is passed to the layoutPartsAndMapPrefSheet() method, just like the default parts. The custom items should all be built within a form widget that is automatically laid out and managed within the layoutPartsAndMapPrefSheet() method. The layout is from top to bottom in the shell widget.

The preference-sheet widget and all of its child widgets are destroyed when the preference-sheet window is closed by the user. This behavior is intended, since the preference sheet is only a temporary window, and we don't want to carry the unwanted widget around when it is no longer needed.

Changing the Pop-up Menu

The SoXtFullViewer pop-up menu, which includes a rich set of viewer functions, can be changed in subclasses by redefining any of the following pop-up menu build routines:

void setPopupMenuString(const char *name);

virtual void buildPopupMenu();

Widget buildFunctionsSubmenu(Widget popup);

Widget buildDrawStyleSubmenu(Widget popup);

To change the pop-up menu title, use the setPopupMenuString() method. To change the pop-up menu, subclasses can redefine the buildPopupMenu() method. Subclasses can also append new entries to the pop-up menu by directly adding a Motif-compliant xmToggleButton or xmPushButton to the pop-up menu widget.

Changing the Decoration Layout

On rare occasions, you may want to change the decoration surrounding the rendering area. The SoXtWalkViewer class, for example, adds an extra thumbwheel and label in the left-hand trim. To simplify the redefining of the decoration, the base class SoXtFullViewer constructs the decoration in many small and manageable steps. The following functions are used to create the decoration and can be redefined by subclasses at any level:

SoXtWalkViewer redefines only the buildLeftTrim() routine in order to build the default parts as well as the extra thumbwheel and label. The viewer then simply returns a form containing its new left trim to the buildDecoration() routine, and everything works as before. Only the new trim has to be modified.

FEI Visualization Sciences Group is the leading provider of advanced 3D visualization and analysis software tools for developers, engineers and scientists in natural resources, medical and life sciences, and engineering.