18.3. Microsoft Windows

Introduction

Background:

Open Inventor consists of a portable core component and various window system-specific components:

This chapter describes the Open Inventor window system-specific components for the Win32 environment, specifically the SoWin SoWin classes and the IVF™ extension to the Microsoft Foundation Classes (MFC).

Architecture:

Open Inventor was designed to be portable and window system independent. It uses a strategy similar to the one used in OpenGL (which Open Inventor uses as its rendering engine). OpenGL is divided into a large core of system-independent functions and a small set of window (and operating system) specific functions. The system-specific functions generally have similar functionality but different parameters. These OpenGL functions are identified by a unique prefix, for example:

System

OpenGL prefix

Unix/Linux X11

glX

Win32 (Windows)

wgl

OS/2

pgl

Macintosh

agl

This has been a successful strategy. Some modification of the program for each platform is necessary, but in most cases the window system-specific calls are used in a small number of places. Of course there may be many other window system and user interface changes that must be made to the program, for example converting a Motif interface into the equivalent Win32 or MFC interface. These changes are independent of the graphics library.

The corresponding prefixes for Open Inventor are:

System

Open Inventor prefix

Unix/Linux X11

SoXt

Win32 (Windows)

SoWin

The correspondence between Open Inventor components in the Win32 environment and in the Unix environment is illustrated in Figure 18.9, “ Open Inventor for Win32 and Unix”.

Open Inventor for Win32 and Unix

Figure 18.9.  Open Inventor for Win32 and Unix



In porting an OpenGL program from Unix you find that the Win32 “wgl” functions have different names and different syntax but similar concepts. We used the same strategy in designing the Open Inventor SoWin SoWin classes. SoWin SoWin contains the same classes as SoXt and in most cases their methods differ only in the data types of the parameters. This allows experienced Unix Open Inventor programmers to benefit from their knowledge of the SoXt classes. It also allows all Open Inventor programmers to benefit from the extensive tutorial and example information in The Inventor Mentor (see Chapter 18, Inventor Component Library) and The Inventor Toolmaker. SoWin SoWin is fully decribed in the following section.

There is a very close correspondence between the Unix SoXt classes and the Win32 SoWin SoWin classes. This allowed us to define a set of SoXt “aliases” for the SoWin SoWin classes that facilitate porting Open Inventor programs from the Unix X11 environment. Using the SoXt aliases, simple Open Inventor programs can often be ported to the Win32 environment with only minor changes. The SoXt aliases are described in their own section.

A significant difference between the Win32 C++ environment and Unix is that much of the new C++ development under Win32 is being done using the Microsoft Foundation Classes (MFC) and the MFC AppWizard. MFC is both a class library that “wraps” the Win32 API and a powerful application framework based on the document/view paradigm. MFC provides a standard implementation of services common to many applications, such as printing, tool bars, and status bars. The MFC AppWizard is a tool that generates a skeleton MFC application that is ready to compile, link, and execute. The AppWizard can automatically add support for various features such as OLE, ODBC (database access), and so on.

For programmers using MFC, Open Inventor for Win32 provides the IVF classes and the IVF AppWizard. IVF is both a class library that “wraps” the SoWin SoWin API (similar to the way MFC wraps the Win32 API) and an extension of the MFC application framework specifically for 3D graphics applications. Using Open Inventor features, IVF also extends MFC to provide a standard implementation of 3D graphics services common to many applications, such as interactive viewers, object manipulators, and attribute editors. The IVF AppWizard is an extension of the MFC AppWizard that generates a skeleton MFC application with 3D graphics enabled. The IVF AppWizard can automatically add support for features such as interactive viewers, VRML “anchors,” and so on. IVF is described in its own section.

So there are actually three parts to the implementation of the window system-specific components of Open Inventor for Win32:

Open Inventor prefix

Feature

SoWin

Win32-specific classes

SoXt

Aliases for SoWin classes

IVF

MFC extension classes

Why three parts instead of just integrating Open Inventor into MFC? In short, because it provides the “best of both worlds.” IVF provides a good, clean extension to MFC without having to expose every method of every Open Inventor component class. Similar to the way MFC allows direct access to the Win32 API, the Open Inventor component methods are accessible to applications that need them. At the same time, the one-to-one correspondence between SoWin SoWin and SoXt classes allows Open Inventor programmers to carry over knowledge from one environment to the other and makes resources such as The Open Inventor Mentor directly applicable to the Win32 environment. The SoWin SoWin classes provide critical flexibility in two areas that will allow Open Inventor to be more quickly adopted and have lasting value in this environment. First, the SoWin SoWin classes can be used to build extension classes for application frameworks other than MFC. Second, the SoWin SoWin classes can be used to add 3D graphics functionality to existing applications, even in situations where development policy does not allow the class inheritance tree to be modified (as would be required to add IVF support to an MFC application).

SoXt Alias Classes

Here is a simple Open Inventor program from Chapter 2 of The Inventor Mentor (see Example 2.4, “ “Hello, Cone” Using the Examiner Viewer). The only difference between this program and the Unix/X11 version shown in The Inventor Mentor is the addition of one line (highlighted below). This header file uses the standard preprocessor symbol “WIN32,” which is defined in the Win32 environment, to change the name of function “main”. Most Win32 programs (the exception is console applications) do not have a function named main. Instead the first entry point in the program is named “WinMain” and has a very different set of parameters. Applications using Open Inventor for Win32 are not required to have a WinMain because the optional SoWin SoWin utility library provides one. Open Inventor’s WinMain function performs some basic bookkeeping operations and then calls the application’s “ivMain” function with the same parameters as the familiar Unix “main” function. SoWinApp.h also contains pragma statements to include the current version of INVUxxx.LIB or INVUxxxD.LIB. The code in this header file is under an #ifdef WIN32 for cross-platform portability.

Example 18.6. “Hello Cone” using the SoXt alias classes

#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>

#include <Inventor/SoWinApp.h>

int
main(int, char **argv)
  {
  Widget myWindow = SoXt::init(argv[0]);
  if (myWindow == NULL) exit(1);

  SoSeparator *root = new SoSeparator;
  root->ref();
  SoMaterial *myMaterial = new SoMaterial;
  myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
  root->addChild(myMaterial);
  root->addChild(new SoCone);

  SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow);
  myViewer->setSceneGraph(root);
  myViewer->setTitle("Examiner Viewer");
  myViewer->show();

  SoXt::show(myWindow);
  SoXt::mainLoop();
  return 0;
  }


Prior to Open Inventor 5.0, it was necessary to explicitly #define main to ivMain and also to explicitly add INVUxxx.LIB or INVUxxxD.LIB to the link string. If you include SoWinApp.h in your Win32 (not console) application, you do not need to put the definition of ivMain directly in your code, and the correct version of the INVU libraries will be used automatically.

Because the WIN32 symbol is not defined on Unix systems, this program is completely portable. It can be compiled, linked, and executed with no additional changes in either a Win32 or a Unix environment. With one or two exceptions, all the example programs from The Inventor Mentor and The Inventor Toolmaker were made portable with only minor changes similar to the ones shown above. This is highly desirable because these programs are, for the most part, intended to illustrate system-independent features of Open Inventor. Using the SoXt alias classes, the important information in the programs is not obscured by platform differences.

The SoXt alias classes are valuable for more than just simple example programs. Potentially any Open Inventor program that does not make explicit calls to Xlib, Xt, or Motif functions can be ported to Win32 with its SoXt calls intact. For example, the Open Inventor demo program “slotcar” (included with the Open Inventor for Win32 SDK) is a moderately complex program that was ported using the SoXt aliases. Even when there is Motif code to be converted, knowing that SoXt calls map directly to SoWin SoWin calls (and generally have the same behavior) means that the SoXt calls could be left in the code, which would reduce the number of “#ifdefs” in the final code. We do not recommend this approach for production code but it can make porting easier for some projects.

Strictly speaking, there are no SoXt classes or data types in Open Inventor for Win32. However, all the SoXt header files are provided. These header files redefine the SoXt classes as the corresponding SoWin SoWin classes and the necessary X11 (and Xt and Motif) data types as the corresponding Win32 data types. So, for example, class SoXtExaminerViewer is redefined as class SoWinExaminerViewer SoWinExaminerViewer . There is an SoWin SoWin class that corresponds to each of the SoXt classes. The Win32 SoXt (SoWin SoWin ) methods generally perform the same, or very similar, actions as their Unix counterparts.

In order to redefine the X11 data types, we take advantage of the general similarity between the X Window System and the Microsoft Windows System. Both refer to windows using opaque handles, both provide a window hierarchy in which child windows are clipped against their parent window, and so on. The X11 type Window corresponds well to the Win32 type HWND (which is the mapping Open Inventor uses). Win32 does not have a concept directly analogous to the Xt “widget.” However, in Xt there is always exactly one window that corresponds to a particular widget, so Open Inventor for Win32 also maps the Xt type Widget to the Win32 type HWND. Thus the SoXt::init method returns a “Widget” which is actually a window handle which can then be passed to the SoXtExaminerViewer constructor as its parent window.

The SoXt::show method has the same effect as its Unix equivalent in this situation. It uses the Win32 function ShowWindow to make the application’s top level window visible. The SoXt::mainLoop method also has essentially the same behavior as its Unix equivalent. It uses the SoXt::nextEvent and SoXt::dispatchEvent methods, which are in turn implemented using (mostly) the Win32 GetMessage and DispatchMessage functions. More details are given in the SoWin SoWin section. The Win32 MSG data type is roughly equivalent to the X11 Xevent data type. Most of the same basic user interface events exist in both window systems, for example keypress, mouse button, and mouse motion events.

SoWin Classes

Here is a simple Open Inventor program from Chapter 2 of The Inventor Mentor (Example 2.4, “ “Hello, Cone” Using the Examiner Viewer). This is the same program used as an example in the “ SoXt Aliases ” section. In this example, a minimal translation of the program from Unix to Windows has been done (“SoXt ” changed to “ SoWin SoWin ” and so on), but the program still has the structure of a Unix/X11 program. If this program was being ported from Unix to Win32, it would be easier (and work just as well) to use the SoXt alias classes described in the previous section. The SoWin SoWin version is shown here to illustrate the one-to-one relationship with the SoXt classes.

This version of the program works essentially the same as it would under Unix. Calling SoWin::init with a string creates a top level window for the application and returns (in this case) its window handle. The viewer’s window is created as a child of the top level window. The “show” calls make the various windows visible and SoWin::mainLoop provides the application’s event loop (message loop in Win32 terminology).

Example 18.7. “Hello Cone” using the SoWin classes without Win32

#include <Inventor/Win/SoWin.h>
#include <Inventor/Win/viewers/SoWinExaminerViewer.h>
#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>

#ifdef WIN32
#define main ivMain
#endif

int
main(int,  char **argv)
  {
  HWND myWindow = SoWin::init(argv[0]);
  if (myWindow == NULL) exit(1);

  SoSeparator *root = new SoSeparator;
  root->ref();
  SoMaterial *myMaterial = new SoMaterial;
  myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
  root->addChild(myMaterial);
  root->addChild(new SoCone);

  SoWinExaminerViewer *myViewer = new SoWinExaminerViewer(myWindow);
  myViewer->setSceneGraph(root);
  myViewer->setTitle("Examiner Viewer");
  myViewer->show();

  SoWin::show(myWindow;
    SoWin::mainLoop();
    return 0;
  }


Below is the same simple program again, but written as a native Win32 program. Notice that the code using core Open Inventor classes does not change. These classes are, with a few minor exceptions (see the section called “General Issues”), system independent. Notice that there are minor changes to the code using SoWin SoWin classes. Instead of a string, SoWin::init is called with the window handle of the application’s top level window (this window is created by the application in the WinMain function). The SoWin::show and SoWin::mainLoop calls are not used because the application controls the visibility of the top level window and has its own message processing (event) loop.

Also notice that SoWin::isInventorMessage is called at the top of the application’s WndProc. This method was added to Open Inventor specifically for the Win32 environment. Conceptually similar to the Win32 IsDialogMessage function for modeless dialog boxes, it allows Open Inventor to handle certain messages that are only sent to the application’s top level window. For example, when the video mode is 8-bit (256 colors), SoWin SoWin automatically creates and manages the palette (color map) that OpenGL needs for rendering. Unlike X11, a Win32 application is responsible for realizing (installing) its own palette when it receives the input focus. Win32 sends the application a special “palette changed” message, but palette change messages are only sent to the application’s top level window, not to the actual drawing window.

Example 18.8.  “Hello Cone” using the SoWin classes with Win32

#include <windows.h>
#include <Inventor/Win/SoWin.h>
#include <Inventor/Win/viewers/SoWinExaminerViewer.h>
#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>

LRESULT InitInventor(HWND);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

///////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
  {
  WNDCLASS wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = NULL;    //Inventor will take care of it
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = "Inventor Win32";
    RegisterClass(&wndclass);

    HWND hwnd = CreateWindow("Inventor Win32", "Inventor Win32",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT,
    500, 400, NULL, NULL, hInstance, NULL);

    InitInventor(hwnd);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  return msg.wParam;
  }


///////////////////////////////////////////////////////////////////////
LRESULT InitInventor(HWND mainWindow)
  {
  SoWin::init(mainWindow);

    SoSeparator *root = new SoSeparator;
    root->ref();
    SoMaterial *myMaterial = new SoMaterial;
    myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
    root->addChild(myMaterial);
    root->addChild(new SoCone);

    SoWinExaminerViewer *myViewer = new SoWinExaminerViewer(mainWindow);
    myViewer->setSceneGraph(root);
    myViewer->setTitle("Examiner Viewer");
    myViewer->show();

    return 0;
  }


///////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
  {
  if (SoWin::isInventorMessage(hwnd, message, wParam, lParam))
    return TRUE;

    switch (message) {  case WM_DESTROY:  PostQuitMessage(0);  return 0; }
  return DefWindowProc(hwnd, message, wParam, lParam);
  }


SoWin

The SoWin SoWin class initializes Inventor for use with the Win32 window system. All its methods are static convenience functions. Every SoWin SoWin class includes all the methods of its corresponding Unix class. Generally these methods have the same, or very similar, behavior. In a few cases the data types have been changed for the Win32 environment. In particular:

  • Type HWND is used in place of the X11 type Window and the Xt type Widget.

  • Type MSG is used in place of the X11 type XEvent.

Some of the SoXt methods have no meaning in the Win32 environment. They are retained for convenience, but do nothing. These include getAppContext(), getDisplay(), encodeString(), decodeString(), and getPopupArgs().

Some additional methods specific to the Win32 environment have been added. The isInventorMessage function should be called at the top of the application’s WndProc to ensure that Open Inventor receives messages that Win32 only sends to the application’s top level window. (The need for this function in color palette handling is discussed at the beginning of this section.) The doIdleTasks function processes any pending Open Inventor idle sensors (see SoIdleSensor SoIdleSensor SoIdleSensor ). By default, idle sensors will be processed even without using this function. However if the Open Inventor delay queue time-out is disabled (using SoDB::setDelaySensorTimeout ), the application may need to call doIdleTasks periodically. Note that if the application is using SoWin::mainLoop or SoWin::nextEvent, doIdleTasks is called automatically when the message queue is empty. Also note that isInventorMessage and doIdleTasks are called automatically in an IVF application (see the section called “ IVF AppWizard”).

SoWinClipboard

The SoWinClipboard SoWinClipboard class provides copy and paste for Open Inventor data using the Win32 clipboard. It is somewhat less important in the Win32 environment because interfacing to the clipboard is not as complicated as it is under X11. However it is still convenient. The default value of the format parameter in the constructor is the Win32 constant “CF_TEXT”. This is the most common Win32 clipboard format and causes Open Inventor to write data to the clipboard in ASCII format. From the clipboard, CF_TEXT data can be pasted into an Open Inventor application or any text-based application. For example it is often convenient to paste a portion of the scene graph into the Notepad utility to verify its contents.

SoWinComponent

SoWinComponent SoWinComponent is the abstract base class for all Open Inventor components. Some of the methods have no meaning in the Win32 environment, including getDisplay(), setIconTitle(), and getIconTitle(). New member variables helpFileName and helpContextId are provided to allow applications to override the default help topic displayed by a component. Editor components, such as SoWinMaterialEditor SoWinMaterialEditor , have a help item in their menu bar. Viewer components, such as SoWinExaminerViewer SoWinExaminerViewer , have a help button in their toolbar.

SoWinGLWidget

SoWinGLWidget SoWinGLWidget is the generic component for OpenGL rendering. In addition to the data type mapping discussed in the SoWin SoWin topic, there are the following changes:

  • Type HGLRC is used in place of the X11 type GLXContext.

  • Type PIXELFORMATDESCRIPTOR is used in place of the X11 type XVisualInfo.

Some behaviors specific to the Win32 environment have been added (usually to preserve a useful feature of the Open Inventor user interface). SoWinGLWidget SoWinGLWidget provides the equivalent of X11’s automatic pointer grab on a mouse button press (using SetCapture()). This ensures that the application will always get the mouse “button up” event corresponding to a “button down” event. This is not normally guaranteed in the Win32 environment. SoWinGLWidget SoWinGLWidget also (optionally) provides the equivalent of X11’s “focus follows pointer” policy. This ensures that Open Inventor viewer components receive the input focus when the cursor enters their window. Because this behavior is not always desirable, it can be disabled using a Win32-specific method (see next section).

Some additional methods specific to the Win32 environment have been added. The getNormalDC and getOverlayDC functions return the current Win32 Device Context (needed for wglMakeCurrent). The setStealFocus function enables or disables emulation of the X11 “focus follows pointer” policy (by default the Open Inventor viewer components enable this behavior).

Look and Feel

Open Inventor for Win32 has been adapted to the Win32 user interface guidelines, but at the same time preserves the system-independent “look and feel” of the viewer and editor components. Notice that the distinctive Open Inventor thumb wheel interface is preserved and the viewer toolbar uses the same button bitmaps as on other platforms:

Examiner Viewer for Win32

Figure 18.10. Examiner Viewer for Win32



Material editor for Win32

Figure 18.11. Material editor for Win32



Open Inventor custom color sliders are used rather than generic Win32 sliders.

SoWinExaminerViewer and Configuration of Key/Mouse Binding

With the Examiner Viewer, it is possible to define the keys and/or mouse presses associated with the various viewer functions, such as pan, dolly, zoom, etc.

A class derived from SoViewingFunction is defined for each possible viewing function, and must be used for making the association between a key press or mouse press and a viewing function.

  • SoViewingFunction

    • SoViewingDolly

    • SoViewingEmpty

    • SoViewingRotationX

    • SoViewingRotationXViewer

    • SoViewingRotationY

    • SoViewingRotationYViewer

    • SoViewingRotationZ

    • SoViewingRotationZViewer

    • SoViewingSeek

    • SoViewingSphericalRotation

    • SoViewingTranslation

Figure 18.12.  Viewing function classes



The methods addFunctionKeyBinding() and addViewingMouseBinding() of the SoWinExaminerViewer SoWinExaminerViewer class can be invoked to add a viewing function that is triggered with a specified mouse button or key press. These methods have as parameters:

  • the chosen viewing function which is an instance of a class derived from SoViewingFunction,

  • the associated key (i.e., SoKeyboardEvent) or mouse button(s) (i.e., SoMouseButtonEvent).

Mouse button events can be combined with one or several modifiers (CTRL, SHIFT, …). Thus CTRL + SHIFT + left mouse button (button 1) could trigger, for example, a dolly viewing function.

The methods removeFunctionKeyBinding() and removeViewingMouseBinding() of the SoWinExaminerViewer SoWinExaminerViewer class can be used to remove a key or mouse binding.

The following example (which can be found in the directory $OIVHOME/src/Inventor/examples/Features/SetKeyBinding) defines the key and mouse bindings as follows:

  • The F3 through F9 keys toggle between picking mode and a particular viewing function, as follows:

  • F3 key: dolly

  • F4 key: pan

  • F5 key: constrained x-axis rotation

  • F6 key: constrained y-axis rotation

  • F7 key: constrained z-axis rotation

  • F9 key: spherical rotation (as with a standard Examiner Viewer)

  • CTRL + SHIFT + left mouse button (button 1): dolly

  • SHIFT + left mouse button (button 1): pan

Example 18.9. Defines key and mouse button bindings for an Examiner Viewer


// SoWinExaminerViewerSetKeyBinding class derives from SoWinExaminerViewer
// and defines new key/mouse bindings within its constructor;

SoWinExaminerViewerSetKeyBinding::
SoWinExaminerViewerSetKeyBinding(Widget parent,const char *name,
                                SbBool buildInsideParent,
                                SoWinFullViewer::BuildFlag flag,
                                SoWinViewer::Type type)
                                : SoWinExaminerViewer(parent, name,
                                                    buildInsideParent,
                                                    flag, type)
  {
  // New key binding.

  // F3 = Dolly
  addFunctionKeyBinding(SoKeyboardEvent::F3, new SoViewingDolly());

  // F4 = Panning
    addFunctionKeyBinding(SoKeyboardEvent::F4, new SoViewingTranslation());

  // F5 = X Rotation
    addFunctionKeyBinding(SoKeyboardEvent::F5, new SoViewingRotationX());

  // F6 = Y Rotation
    addFunctionKeyBinding(SoKeyboardEvent::F6, new SoViewingRotationY());

  // F7 = Z Rotation
    addFunctionKeyBinding(SoKeyboardEvent::F7, new SoViewingRotationZ());

  // F9 = Spherical rotation
    addFunctionKeyBinding(SoKeyboardEvent::F9, newSoViewingSphericalRotation());

  // Assign new mouse binding.
  // Equivalent to F3 = Zoom.
    SoKeyboardEvent::Key *spinModifierKeys = new SoKeyboardEvent::Key[2];
    spinModifierKeys[0]=SoKeyboardEvent::LEFT_CONTROL;
    spinModifierKeys[1]=SoKeyboardEvent::LEFT_SHIFT;
    SoMouseButtonEvent::Button *newButton = new SoMouseButtonEvent::Button[1];
    newButton[0] = SoMouseButtonEvent::BUTTON1;
    addViewingMouseBinding(spinModifierKeys, 2,newButton, 1, new SoViewingDolly());

  // Equivalent to F4 = Translation.
    SoKeyboardEvent::Key *panModifierKeys = new SoKeyboardEvent::Key[1];
    panModifierKeys[0]=SoKeyboardEvent::LEFT_SHIFT;
    SoMouseButtonEvent::Button *panButton = new SoMouseButtonEvent::Button[1];
    panButton[0] = SoMouseButtonEvent::BUTTON1;
    addViewingMouseBinding(panModifierKeys, 1, panButton, 1,
    new SoViewingTranslation());

  // Pick Mode
    setViewing(FALSE);
  }


IVF Classes

Introduction

The purpose of the IVF class library is to simplify integration of functionality provided by the Open Inventor for Win32 classes with MFC-based applications. IVF builds upon the application framework provided by MFC to deliver the additional functionality of Open Inventor in a manner that is familiar to MFC developers. The following discussion assumes familiarity with MFC terminology and the structure of SDI, MDI, and dialog-based MFC applications. In addition, a basic understanding of Open Inventor and the Open Inventor User Interface Component Library (SoWin SoWin or SoXt ) is assumed.

Figure 18.13, “ Objects in an IVF SDI application ” illustrates the objects in an IVF SDI (Single Document Interface) application. Note there is no fundamental difference between the object relationships in an IVF application and an MFC application. An IVF application is an MFC application. This diagram intentionally follows architecture diagrams that Microsoft uses to explain MFC.

Objects in an IVF SDI application

(Arrows show direction of communication flow.)

Figure 18.13.  Objects in an IVF SDI application



In an IVF application, the application, document, mainframe window, and view objects have been endowed with additional functionality. The additional functionality enables the standard MFC objects to interact appropriately with the core Open Inventor Library and the Open Inventor User Interface Component Library. The addition of functionality to the standard MFC objects is achieved through the use of multiple inheritance.

Class hierarchy in an IVF SDI application

Figure 18.14.  Class hierarchy in an IVF SDI application



This example diagrams an Examiner Viewer application. The class hierarchy for all IVF viewer applications is similar, the difference being which IVF viewer class the user view is derived from.

The IVF classes are not derived from CObject. This is because the current implementation of MFC does not support the use of CObject as a virtual base class. It is important to note in Figure 18.14, “ Class hierarchy in an IVF SDI application” that all of the user classes have the MFC class as the first (left most) base class.

Example 18.10. Use of CIvfDocument

class CHelloConeDoc : public Cdocument, public CIvfDocument { ...}



This is necessary due to the implementation of the MFC run-time typing mechanism. Refer to MFC Technical Note 16 for information about using multiple inheritance with MFC. In IVF SDI and MDI applications, the Open Inventor viewer occupies the entire view window. The details of how the view’s window is transformed into an Open Inventor viewer are discussed later.

IVF also supports MFC dialog-based applications. In an IVF dialog-based application, any of the dialog’s child controls can be turned into an Open Inventor viewer or render area.

Figure 18.15, “ IVF class tree” shows the IVF class tree. Class CIvfComponent is an abstract base class which roughly corresponds to the SoWinComponent SoWinComponent class. Each of the CIvfComponent subclasses contains an instance of the corresponding SoWin SoWin viewer class.

Example 18.11. Use of CIvfComponent

class CIvfExaminerViewer : public CIvfComponent
  {
  ...
    SoWinExaminerViewer *m_pViewer;
    ...
  }


This is similar to the way MFC wraps some of the Win32 API objects and it provides a great deal of flexibility. The application can use high level methods exposed by CIvfComponent and its subclasses. But it can also use the full set of SoWin SoWin component methods when more detailed control is needed.

The actual SoWin SoWin component is created automatically when a view object is created whose class is derived from CIvfComponent . Components can also be created explicitly using the IvfCreateComponent method. This can be used to turn almost any window into an Open Inventor viewer or render area. For example, the IVF AppWizard has an option to add a “spinning text” control to your application’s About Box by creating a simple static control and creating an Open Inventor component inside its window.

SoInput SoInput SoInput and SoOutput SoOutput SoOutput are standard Open Inventor classes that have been extended to support reading and writing MFC CArchive objects. This is discussed in the section called “General Issues”.

IVF class tree

Figure 18.15.  IVF class tree



The following table summarizes the purpose of the primary IVF classes:

CIvfApp

Methods for initialization of Open Inventor and IVF.

CIvfComponent

Methods for messages sent to the IVF component object and methods to interact with the SoWin SoWin component.

CIvfDocument

Methods for manipulation of Open Inventor documents.

CIvfMainFrame

Methods to process messages sent to the frame window.

CIvfArchiveInput

Methods for Inventor input from MFC CArchive objects. (SoInput SoInput SoInput -derived class)

CIvfArchiveOutput

Methods for Inventor output to MFC CArchive objects. (SoOutput SoOutput SoOutput -derived class)

CIvfExaminerViewer

IVF wrapper for an SoWinExaminerViewer SoWinExaminerViewer object.

CIvfFlyViewer

IVF wrapper for an SoWinFlyViewer SoWinFlyViewer object

CIvfPlaneViewer

IVF wrapper for an SoWinPlaneViewer SoWinPlaneViewer object

CIvfRenderArea

IVF wrapper for an SoWinRenderArea SoWinRenderArea object

CIvfSceneViewer

IVF wrapper for the Scene Viewer object

CIvfWalkViewer

IVF wrapper for an SoWinWalkViewer SoWinWalkViewer object

Figure 18.16.  Summary of IVF classes



Summary of IVF Classes

Example 18.12, “ “Hello Cone” using the IVF classes with MFC”, shows Open Inventor in an MFC program using the IVF AppWizard and IVF classes. The code shown is an excerpt from the application’s document class implementation file (“hellodoc.cpp”). The complete source code for this example is included with the Open Inventor for Win32 SDK, but this excerpt shows all the code that had to be added by hand (highlighted lines). The line bracketed by BEGIN/END IVWGEN was inserted by the IVF AppWizard. The highlighted lines are the same ones we added to the Win32 example program with one exception. Notice that we do not need to explicitly create a viewer component. An Examiner Viewer will be automatically created when the document template creates an MFC view of this document. Instead of passing the scene graph directly to the Examiner Viewer as we did in previous examples, we specify the scene graph as the current contents of the MFC document, using the IvfSetSceneGraph() method. Just as in any MFC document/view application, the document will notify all its views to display the new contents (scene graph). Typically an Open Inventor MFC program will contain a similar mix of calls to IVF and “core” Inventor methods.

We also added a call to “unref” the root node of the scene graph. This ensures that the memory allocated for the nodes in the scene graph will be reclaimed when the scene graph is removed from the Examiner Viewer. See the section called “Reference Counting” in The Inventor Mentor for a discussion of reference counting and memory management in the Open Inventor class library. Strictly speaking, the other example programs should also have this line, but it does not appear in the original Unix version of the program and it is less of an issue because the scene graph creation code can only be executed once in those examples.

By placing the scene graph creation code in OnNewDocument, we get behavior similar to our other simple Example 18.12, “ “Hello Cone” using the IVF classes with MFC”. When the application is executed it will initially create a new document using this method, so the application window will initially contain a red cone and Examiner Viewer decorations. Just like any AppWizard-generated application, this simple program is actually much more functional than its Win32 equivalent. For example, IVF provides a default implementation for File/Open, so this application is already capable of loading Open Inventor and VRML geometry files!

Example 18.12.  “Hello Cone” using the IVF classes with MFC

#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>

BOOL CHelloConeDoc::OnNewDocument()
  {
  if (!CDocument::OnNewDocument())
    return FALSE;

  // BEGIN_IVWGEN
    IvfOnNewDocument();
  // END_IVWGEN
    IvfSetSceneGraph(root);
    SoSeparator *root = new SoSeparator;
    root->ref();

    SoMaterial *myMaterial = new SoMaterial;
    myMaterial->diffuseColor.setValue(1., 0., 0.); //Red
    root->addChild(myMaterial);
    root->addChild(new SoCone);
    root->unref();
    return TRUE;
  }


Document/View Architecture

MFC is most powerful when applications are constructed using its Document/View paradigm. (See, for example, Microsoft’s Introducing Visual C++ or Programming with MFC and Win32 for a general discussion of this architecture.) This is a complex topic, but roughly the Document is the application’s data interface. It is not necessarily a “document” in the common sense of the word. An application may have more than one Document (traditionally done with MDI child windows, but increasingly done with splitter windows or multiple windows). The View is a presentation of the application’s data. There may be multiple views for one document. Each view of the document may be a different type of view, for example a graphical view and a textual view.

IVF implements the Document/View architecture for Open Inventor, following the natural division already implied by the Open Inventor architecture. The Document in an IVF application is the application’s Open Inventor scene graph. A graphical View can be constructed using one of the CIvfComponent viewer classes. A textual View could be constructed using MFC’s CEditView (for an example of this, see the “DualC” program included with the Open Inventor for Win32 SDK).

The Document/View division in Open Inventor is easiest to see when the application’s scene graph does not contain a camera. In this case (as usual) the Inventor viewer automatically creates a camera which is not part of the (application’s) scene graph. However IVF makes the Document/View model work even when the scene graph already contains a camera or even multiple cameras. For example, IVF automatically handles the VRML 2.0 “ViewPoints” convention, extracting the cameras under the “Cameras” switch and making them available in the (optional) ViewPoints menu.

Multiple Inheritance

IVF uses multiple inheritance to add functionality to an MFC application’s classes. As Figure 18.14, “ Class hierarchy in an IVF SDI application” shows, the application classes are derived from both MFC and IVF classes. For example, the application’s Document class is derived from both CDocument and CIvfDocument . It is more common to see examples where the application classes are derived from new classes which are themselves derived from MFC classes. This works fine when the only class library used is MFC. But the single inheritance strategy can break down when the application needs to use other class libraries. If every class library used this strategy then an application would only be able to use one other class library.

Benefit of multiple inheritance with IVF

Figure 18.17. Benefit of multiple inheritance with IVF



Multiple inheritance allows 3D graphics capability to be added to any application, even if it is also using other class libraries to gain other capabilities.

VRML and Extensibility

IVF provides a convenient layer for implementing Win32-specific extensions to the core Open Inventor capabilities without having to modify Open Inventor itself (and thereby introduce portability problems). A good example is the standard VRML feature support defined for IVF. VRML stands for Virtual Reality Modelling Language. It is a standard file format, based on Open Inventor, for using 3D geometry and scenes on the World Wide Web. Open Inventor itself can read and write VRML geometry files (in addition to Open Inventor geometry files) and supports a superset of the nodes defined in VRML. However reading VRML nodes into the scene graph and actually providing browser-like behavior is two different things.

For example, the WWWAnchor node provides a way to associate a hyperlink with some geometry in the scene. Fields in the WWWAnchor node allow both a URL and some descriptive text to be specified for the link. The WWWAnchor class allows the application to specify a callback function to be called when the Anchor’s geometry is selected. So far, so good. But typically an application using WWWAnchor also wants to have some feedback provided to the user when the cursor is over the associated geometry, in other words when a click (or other action) would select the Anchor. IVF provides this behavior. By default the URL string is displayed in the mainframe window’s status bar when the cursor is over the geometry. An application can override this behavior in the usual MFC way, by overriding a virtual function.

As was mentioned earlier, IVF implements support for VRML “hints,” including ViewPoints. ViewPoints, for example, are specified as a set of predefined cameras under a Switch node named “Cameras”. IVF puts the viewpoints (cameras) into a convenient list from which the application can set them. The IVF AppWizard can optionally create a ViewPoints menu for the application. If this menu exists, IVF will automatically add the viewpoints from the scene to the menu.

IVF AppWizard

The MFC AppWizard is a standard part of the Microsoft Visual C++ product. It is a powerful (and often used) tool that generates a skeleton MFC application that can include support for OLE, printing, and many other features. The resulting application is ready to compile, link, and execute. The IVF AppWizard is a “Custom AppWizard” for the Visual C++ environment that extends the standard MFC AppWizard. In addition to the standard AppWizard dialog boxes, it displays the dialog box shown below. The IVF AppWizard generates an MFC application that includes support for interactive 3D graphics using IVF and the Open Inventor class library. Notice that the optional features include support for VRML nodes.

Create an IVF project with Visual C++

Figure 18.18. Create an IVF project with Visual C++



Custom viewers

Introduction

Since Open Inventor 8.0 SoGuiAlgoViewers API provides an easier way to implement custom viewers. Simple to use, its implementation follows the one used for the Open Inventor viewers. Platform independent, it regroups the common and specific algorithms for the different types of viewers.

Algorithms

SoGuiAlgoViewers is divided into two main sections: the common algorithms and the specific algorithms. Animations are also handled by the API in a transparent way for the application.

Common algorithms

The common algorithm methods are used by each type of viewer. These methods include scene graph management, camera management, draw styles, seek animation and methods to interact with the scene (thumbwheels, methods accessible from the buttons or the popup menu of the viewers). In addition to the algorithms which already existed in standard viewer classes, new ones have been introduced including the capability to do selection or zoom on a portion of the scene.

Specific algorithms

The specific algorithms are grouped by viewer type. They involve mainly animations and camera management.

Creating a viewer

Introduction

In order to create a new viewer only a render area is needed. This provides the application with the possibility to add a separate or embedded GUI, and create a new type of viewer.

Using SoGuiAlgoViewers

In order to use SoGuiAlgoViewers: create an instance of it, set the type of viewer, pass it to your render area and finally call the methods needed. The following code snippet illustrates these points:

SoGuiAlgoViewers* algo = new SoGuiAlgoViewers;    // Initialization
algo->setViewerType( SoGuiAlgoViewers::Examiner ); // Set the type
m_renderArea = new SoWinRenderArea( this,"RAName",true,true,true,algo );
[...]
                                                
m_guiAlgo->doBoxZoom( xPos1, yPos1, xPos2, yPos2 );  // Zoom on the
// specified area
Managing events

In order to handle events, an event callback must be registered with the render area. This callback will be used to redirect the events to the appropriate methods that the application may redefine to handle them. To have the event processed by the scene graph (especially when the viewer is in picking mode), it must be sent to the render area. The following code snippet illustrates these points:

// Register this callback to receive events from the render area
m_renderArea->setEventCallback( CustomViewer::renderAreaEventCB, this );
[...]

// Skeleton of the callback
SbBool
CustomViewer::renderAreaEventCB( void* userData, Event* anyevent )
  {
  CustomViewer* customViewer = (CustomViewer*)userData;
    switch (anyevent->type())
    {
    // Redirect the event
    }
  [...]
  // In the method handling the events when the viewer is in picking mode
  // Send the event to the scene graph if viewing mode is off
    m_renderArea->setEventCallback( NULL, NULL ); // This will prevent
  // the event to be sent again to CustomViewer
    m_renderArea->sendEvent( anEvent );      // Make the render area process the
  // event
    m_renderArea->setEventCallback( CustomViewer::renderAreaEventCB, this );

Simple example

The following example illustrates how to use SoGuiAlgoViewers to perform a seek action on the scene graph.

Example 18.13. Simple use of SoGuiAlgoViewers

SeekExample.h

  #ifndef SEEK_EXAMPLE_H
  #define SEEK_EXAMPLE_H
  #include <Inventor/Gui/viewers/SoGuiAlgoViewers.h>
  #include <Inventor/Xx/SoXxRenderArea.h>    //Xx is the platform(Qt, Xt, Win)

    class SeekExample
    {
    public:
      SeekExample();

      private:
      static SbBool eventCB( void* userData, Event* anyevent );

        SoGuiAlgoViewers* m_algo;
        SoXxRenderArea* m_renderArea;
    };
#endif


SeekExample.cpp

#include "SeekExample.h"

  SeekExample::SeekExample
    {
    m_algo = new SoGuiAlgoViewers;
      algo->setViewerType( SoGuiAlgoViewers::EXAMINER );
      m_renderArea = new SoXxRenderArea( this,"CVRenderArea",true,true,true,
      m_algo );
      m_renderArea->setEventCallback( SeekExample::eventCB, this );
    }

  SeekExample::eventCB( void* userData, Event* anyevent )
    {
    SeekExample* sE = (SeekExample*)userData;

      if ( anyEvent->type() == MouseButtonPress )
      {
                                              
      m_algo->seekToPoint( anyEvent->x(), anyEvent->y() );  // Performs
      // the seek animation and position the camera automatically
        return TRUE;          // Return TRUE because the event has been processed
      }
    return FALSE;             // The event may be processed by the render area
    }

A complete and detailed example can be found in $(OIVHOME)/src/Inventor/examples/Qt4/QtCustomViewer (figure 4-11 & 4-12).

Below are the screen captures of the QtCustomViewer example illustrating the use of the API.

Render Area of the custom viewer

Figure 18.19. Render Area of the custom viewer



Remote control of the custom viewer

Figure 18.20. Remote control of the custom viewer



General Issues

The core Open Inventor classes (nodes, actions, sensors, etc.) are almost entirely platform independent. However there are some cases where platform differences must be addressed, either by establishing a convention or by enhancing the classes. The most significant cases are:

These issues are addressed in the following sections.

File Portability

Open Inventor “ascii” format files are inherently portable because they are simply “text” files. The only issue is the minor difference in how a “line” is terminated. In the Unix environment, each line of a text file is terminated by a newline character, also known as the line feed character. In the Win32 environment, many programs still use the DOS convention that each line of a text file is terminated by a carriage return plus a line feed. Open Inventor for Win32 reads both kinds of text files, but always writes files with Unix style line termination (line feed only). Programs to do the (simple) conversion between line termination styles are widely available.

Open Inventor “binary” files face the usual portability issues of byte order and floating point format. Open Inventor for Win32 follows these conventions in reading and writing binary files:

Byte order

Same as SGI (“big endian”)

Floating point format

IEEE floating point

Numeric values

32 bits

Necessary format conversions are done automatically when reading or writing a file. Because Open Inventor uses the same conventions on every platform, Open Inventor binary data files are network transparent. The same file can be read successfully on any platform without performing any explicit conversion. Because the Open Inventor binary format is also a very compact representation, the portability conventions make it ideal for transferring and storing 3D graphics data.

Extended Input and Output

The standard Open Inventor classes, SoInput SoInput SoInput and SoOutput SoOutput SoOutput , can read and write Inventor data from a buffer in memory or a file represented by an stdio file pointer (“FILE *”). Both of these mechanisms are also available in the Win32 environment, but there are several additional requirements. Open Inventor for Win32 can also read and write Inventor data from a file represented by a Win32 file handle or an MFC CArchive object. Win32 file handles are specified using a new method. MFC CArchive objects are handled by new classes derived from SoInput SoInput SoInput and SoOutput SoOutput SoOutput (see the section called “ IVF Classes”). A CArchive object hides the underlying source or destination, which may be a file, a memory buffer, or an OLE structured storage. Support for OLE structured storage is a requirement for using an Open Inventor program as an OLE server, as the target of OLE drag-and-drop operations, and so on.

In order to allow derivation of new classes from SoInput SoInput SoInput and SoOutput SoOutput SoOutput , these classes were modified to make (essentially) all of their methods virtual. This change does not affect Open Inventor applications.

Offscreen Rendering

The Open Inventor SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer class is a very powerful tool for generating bitmap images of an Open Inventor scene. The bitmap image can be written out to a file or reused within the application as an image or texture map. Arguably, SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer belongs in the Open Inventor component library because it needs to use window system-specific calls to obtain a bitmap (pixmap in X11 terms) that OpenGL can render into. However the system-specific details are hidden from the application.

The standard SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer can write files in SGI’s “.rgb” format and Encapsulated PostScript. FEI has added the ability to write TIFF, JPEG, JPEG2000, PGX, PNM, Sun Raster, and PNG files, and on Windows platforms, BMP files as well.


  SbBool writeToBMP(FILE *fp) const;

This is much more useful in the Win32 environment because many programs can import and display “.bmp” files, including Microsoft’s Word, Excel, and PowerPoint.

The image produced by the SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer is sent to one of the SoRasterImageRW SoRasterImageRW SoRasterImageRW -derived classes, each one specialized to deal with a specific graphic file format.


    SbBool writeToRaster(FILE *fp, SoRasterImageRW* imageWriter)

A custom reader/writer class can also be used by the SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer using writeToRaster().

See also the section called “SoRasterImageRW” for information on writing your image to a custom format.

The standard SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer also provides a method (getBuffer() ) that returns a pointer to the bitmap image in OpenGL (glReadPixels ) format. Open Inventor for Win32 extends this concept with a new method that returns a pointer to the Win32 memory device context (DC) that contains the image:


    HDC getDC() const;

The memory DC is roughly equivalent to the glXPixmap used in the Unix/X11 implementation. Because of the generality of the Win32 graphics model, a Win32 program can “blt” the bitmap image directly from the memory DC into a display window context, a Windows Enhanced Metafile (“.emf”) context, or a printer device context. A printer device context hides the details of the actual printer, allowing Open Inventor for Win32 programs to support hardcopy onto any device (supported by Windows) with no additional effort. The getDC method is also used in MFC programs to implement support for the “print preview” feature.

If the image size is larger than the OpenGL rendering capabilities (typically 2048 by 2048), SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer renders the image using multi-tile rendering. This allows very large images to be produced.

Some graphic file formats are much more efficient than others at dealing with large images. Some formats can be written using bands of tiles, whereas others require the full image to be written all at once, which requires more memory. If you plan to generate very large images, it would be best to use one of the more efficient file formats. You can use SoRasterImageRW::getWriteCapability to query the write capability of the file format: WRITE_SCANLINES is more efficient, WRITE_FULL_IMAGE is less efficient. This is discussed in the section called “SoRasterImageRW”.

Individual tile buffers can be retrieved as a callback is called after each tile is rendered.

SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer will automatically try to use an OpenGL Pbuffer for rendering. If a Pbuffer is available, rendering will be accelerated by the 3D graphics hardware. If no Pbuffer is available, rendering will use a software rendered off-screen buffer as before. Pbuffers are a standard feature of OpenGL 1.2 and are often available as an extension on older versions of OpenGL. However it is not guaranteed that Pbuffers will be available or that a Pbuffer of the desired size can be created.

Rendering with a Pbuffer will generally produce the same image as would be rendered on the screen, but this is not guaranteed. In general, software rendering will produce the same image as on-screen if only OpenGL 1.1 features are used. Newer OpenGL features and extensions will generally not be available using software rendering.

The following method can be used to specifically request that a Pbuffer be used (or not used):


    void  setPbufferEnable  ( SbBool enable );

In order to tune the Pbuffer graphic configuration you can use the following methods to set a graphic configuration template or only set anti-aliasing preferences:


    void setGraphicConfigTemplate(SoGLGraphicConfigTemplate* gTemplate);

    void setFullSceneAntialiasing( SoGraphicConfigTemplate::Preference pref,
                                    int minFsaaBits = 0,
                                    int maxFsaaBits = INT_MAX );

The following method can be used to specify an OpenGL context to be shared by the SoOffscreenRenderer SoOffscreenRenderer SoOffscreenRenderer :


    void  setShareContext  (const SbGLShareContext shareCxt);

This avoids the necessity to re-generate textures and display lists if they are already available in another OpenGL context (the viewer context, for instance).

Using SoWin::doIdleTasks

If you’re using SoWinRenderArea SoWinRenderArea or any of its derived classes, e.g., SoWinExaminerViewer SoWinExaminerViewer , then Open Inventor automatically creates an SoSceneManager SoSceneManager SoSceneManager which creates a node sensor and attachs it to the root of the scene you specified with setSceneGraph(). When you change the scene graph (and auto-redraw is enabled, which is true by default), the sensor is triggered and the SoSceneManager SoSceneManager SoSceneManager “schedules” a redraw. This is the same as if your application called the scheduleRedraw() method. It schedules a sensor in the “idle queue”. Conceptually the sensors in the idle queue are triggered when Open Inventor detects that the application is “idle” and then a redraw occurs.

Now the question is: how/when does Open Inventor know that the application is “idle” and process the Open Inventor idle queue?

If you are using SoWin::mainLoop (or nextEvent / dispatchEvent ), it is handled for you. Otherwise, on Windows at least, it requires a little cooperation from the application. We have packaged the “things to be done when the application is idle” in the method SoWin::doIdleTasks. Calling this method will process the idle queue and so on. If this method is never called, then autoRedraw (automatically redrawing on a scene graph change) may not work.

If you are writing a straight Win32 program, you probably have an explicit Windows message loop in your application. An easy way to detect “idle” is when the message queue is empty. SoWin SoWin ’s nextEvent() method does this:


    DWORD qstatus = GetQueueStatus( QS_ALLINPUT );
    if (qstatus == 0)
    SoWin::doIdleTasks();

before calling GetMessage (because GetMessage blocks when the queue is empty!). This is about the same as calling PeekMessage.

If you are writing an MFC program, it’s easier because MFC has its own idea of what “idle” means. You just override the OnIdle handler in your app class, e.g., CMyApp::OnIdle() , and call SoWin::doIdleTasks in there. If you look at the simple MFC examples (.../src/Inventor/examples/mfc/...) you’ll see this. Of course if you used the IVF AppWizard we provide to create your MFC (+IVF) program, then you don’t need to do anything. The AppWizard automatically overrides OnIdle and inserts the call to doIdleTasks.