18.3. Microsoft Windows

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( C++ | .NET )classes.

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”.


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( C++ | .NET ) classes. SoWin( C++ | .NET ) contains the same classes as SoXt( C++ )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( C++ ) classes. It also allows all Open Inventor programmers to benefit from the extensive tutorial and example information in The Inventor Mentor (see Chapter 18, Open Inventor Component Library) and The Inventor Toolmaker. SoWin( C++ | .NET ) is fully decribed in the following section.

There is a very close correspondence between the Unix SoXt( C++ ) classes and the Win32 SoWin( C++ | .NET ) classes. This allowed us to define a set of SoXt( C++ ) “aliases” for the SoWin( C++ | .NET ) classes that facilitate porting Open Inventor programs from the Unix X11 environment. Using the SoXt( C++ ) aliases, simple Open Inventor programs can often be ported to the Win32 environment with only minor changes. The SoXt( C++ ) 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.

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

Why two parts instead of just integrating Open Inventor into MFC? In short, because it provides the “best of both worlds.” 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( C++ | .NET ) and SoXt( C++ ) 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( C++ | .NET ) 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( C++ | .NET ) classes can be used to build extension classes for application frameworks other than MFC. Second, the SoWin( C++ | .NET ) 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.

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( C++ | .NET )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.


[Important]

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( C++ ) alias classes, the important information in the programs is not obscured by platform differences.

The SoXt( C++ ) 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( C++ ) 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( C++ ) aliases. Even when there is Motif code to be converted, knowing that SoXt( C++ ) calls map directly to SoWin( C++ | .NET ) calls (and generally have the same behavior) means that the SoXt( C++ ) 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( C++ ) classes or data types in Open Inventor for Win32. However, all the SoXt( C++ ) header files are provided. These header files redefine the SoXt( C++ ) classes as the corresponding SoWin( C++ | .NET ) classes and the necessary X11 (and Xt and Motif) data types as the corresponding Win32 data types. So, for example, class SoXtExaminerViewer( C++ )is redefined as class SoWinExaminerViewer( C++ | .NET ) . There is an SoWin( C++ | .NET ) class that corresponds to each of the SoXt( C++ ) classes. The Win32 SoXt( C++ ) (SoWin( C++ | .NET )) 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::initmethod returns a “Widget” which is actually a window handle which can then be passed to the SoXtExaminerViewer( C++ )constructor as its parent window.

[Important]

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( C++ | .NET ) 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.

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( C++ ) ” changed to “ SoWin( C++ | .NET ) ” 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( C++ ) alias classes described in the previous section. The SoWin( C++ | .NET ) version is shown here to illustrate the one-to-one relationship with the SoXt( C++ ) 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).


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( C++ | .NET ) 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( C++ | .NET ) 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


C++
#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);
  }

The SoWin( C++ | .NET ) class initializes Inventor for use with the Win32 window system. All its methods are static convenience functions. Every SoWin( C++ | .NET ) 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( C++ ) methods have no meaning in the Win32 environment. They are retained for convenience, but do nothing. These include getAppContext(), getDisplay(), encodeString(), decodeString(), and getPopupArgs().

[Important]

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( C++ | Java | .NET )). 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.

The SoWinClipboard( C++ | .NET ) 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( C++ | .NET ) 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( C++ | .NET ) , have a help item in their menu bar. Viewer components, such as SoWinExaminerViewer( C++ | .NET ) , have a help button in their toolbar.

SoWinGLWidget( C++ | .NET ) is the generic component for OpenGL rendering. In addition to the data type mapping discussed in the SoWin( C++ | .NET ) 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( C++ | .NET ) 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( C++ | .NET ) 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).

[Important]

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).

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.


The methods addFunctionKeyBinding() and addViewingMouseBinding() of the SoWinExaminerViewer( C++ | .NET ) 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).

[Important]

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( C++ | .NET ) 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


C++

// 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);
  }

Since Open Inventor 8.0 SoGuiAlgoViewers( C++ ) 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.

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


SeekExample.cpp


C++
#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.



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.

The Open Inventor SoOffscreenRenderArea( C++ | Java | .NET )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, SoOffscreenRenderArea( C++ | Java | .NET ) 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 SoOffscreenRenderArea( C++ | Java | .NET ) can write files in SGI’s “.rgb” format and Encapsulated PostScript. We also added the ability to write TIFF, JPEG, JPEG2000, PGX, PNM, Sun Raster, and PNG files, and on Windows platforms, BMP files as well using


C++

  bool renderToFile (const SbString &filename) const

The format of the generated image is then chosen automatically according to the file extension.

A custom reader/writer class can also be used by the SoOffscreenRenderArea( C++ | Java | .NET ) using renderToBuffer(). If your application needs more control over creation of an output file, the SoBufferObject can be converted into an SbRasterImage using the appropriate constructor. Once instantiated, the SbRasterImage can be passed as a parameter to the write function of any class inheriting from SoRasterImageRW

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

[Important]

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::getWriteCapabilityto 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.

If you’re using SoWinRenderArea( C++ | .NET )or any of its derived classes, e.g., SoWinExaminerViewer( C++ | .NET ), then Open Inventor automatically creates an SoSceneManager( C++ | Java | .NET )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( C++ | Java | .NET ) “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( C++ | .NET )’s nextEvent() method does this:


C++

    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.