When HOOPS/3DGS was first developed, window systems were not common - neither X-Windows nor Microsoft Windows existed yet. In those days (for example, on DOS or UNIX), when a 3D graphics application was started, it normally took control of the entire display screen. If you wanted multiple windows, it was up to HOOPS to create them, as there was no window system to provide them. Partly as a legacy from those days, HOOPS/3DGS has its own concept of a window.
The window attribute
In HOOPS/3DGS, a window is an attribute set on a segment with the ::Set_Window command. This command takes four arguments, which define the left, right, bottom, and top coordinates of the window, specified with respect to the parent window. Setting the window attribute on a segment causes all geometry in that segment to be drawn inside the specified window area. The window attribute inherits like any regular attribute, so any geometry in this segment's subsegments also is drawn inside this window (unless a child segment has a window attribute set on it explicitly).
As we discussed in the Cameras page, a camera maps a 3D scene into a 2D window. In particular, the camera field defines the coordinates of the output window. By default, the camera field sets the coordinates of the output window to range between -1.0 and 1.0 in both x and y. With a stretched camera projection, the camera output field is mapped exactly onto the output window. With a non-stretched projection, either the width or height of the camera field is padded to preserve the aspect ratio of the camera field.
The net window attribute defines the window on the display into which the camera field is mapped. Thus, when the window attribute is set on a segment, it causes any geometry that inherits the window attribute to be mapped into that window.
This program divides the output window into four equal-sized subwindows (Recall that the segment "?Picture" corresponds to the HOOPS output window or to the whole screen, if there is no window system):
Four subwindows in HOOPS.
The code above creates a new segment for each window, and sets the window attribute on that segment with the coordinates of each quadrant. For example, the segment "upper left window" defines a window that goes from -1.0 to 0.0 in x and 0.0 to 1.0 in y. When we add geometry to the scene, the window in which it appears depends on into which of the four subsegments the geometry is placed. For example, if we want to draw an object that will appear in only the upper-left window, we place it in the segment named "upper left window". If we want the same object to appear in all four windows, then we use the ::Include_Segment command to include the same object in all four subsegments, and set the appropriate camera on each one.
When we divide a window into subwindows, each new window is given its own coordinate system. The camera field is mapped into this coordinate system, as though the subwindow were a top-level window. So each subwindow has x and y coordinates that range from -1.0 to 1.0 (unless its camera field has been changed). The center of each subwindow corresponds to the net camera target for that segment, and the dimensions of the subwindow correspond to the net camera field for the segment.
Note that the segment "?Picture" is implicitly a window - the overall window corresponding to the HOOPS output window. You cannot set the window attribute (using ::Set_Window) on "?Picture", because "?Picture" already has an implicit window, and you cannot unset the window attribute (using ::UnSet_Window). In general, you cannot modify the window attribute on any driver instance (including the values of the aliases "?Picture" or "?Hardcopy"). One window attribute that can be modified at the driver level is fullscreen mode. If you turn on the fullscreen mode in ::Set_Driver_Options, HOOPS will draw the "?Picture" window as a borderless window that covers the entire desktop. Note that this option only works when HOOPS in stand-alone mode and is not using an external windowing system such as MFC or Qt.
It is legal for the arguments passed to ::Set_Window to be larger than the coordinates of the parent window's camera field. For example, if the camera field is the default -1.0 to 1.0, a subwindow can be set with coordinates from -2.0 to 2.0. Only that part of the subwindow that actually fits in the parent window will be visible, however; the rest will be clipped. You can use this ability to scroll a scene inside of a window.
HOOPS windows can themselves have subwindows, and so on, recursively. Each subwindow's coordinates are defined in terms of the coordinates of its immediate parent.
You can use the ::Set_Window command to create overlapping windows simply by creating windows whose coordinates overlap:
Overlapping windows in HOOPS.
Children subwindows always appear in front of their parents, but when overlapping windows are siblings of one another (have the same parent segment), one window will partially obscure other windows. In HOOPS, each window is created in front of any existing sibling windows, so the most recently created window will appear in front of all its siblings. For multiple overlapping windows, the ordering (from back to front) is the same as the order in which the windows were created.
You can change the ordering of sibling windows using the ::Bring_To_Front command. ::Bring_To_Front makes its argument subwindow act as though that subwindow's segment was created most recently. If you want to specify the ordering of a set of sibling subwindows, you should call ::Bring_To_Front on each one in order, from back to front.
You can use ::Bring_To_Front on non-windowed segments and ::Bring_To_Front_By_Key on any piece of geometry, to order geometry and segments with respect to one another and to windows. Note, however, that ::Bring_To_Front and ::Bring_To_Front_By_Key work on geometry and non-windowed segments only if hidden surfaces are off for the current segment (see ::Set_Heuristics). ::Bring_To_Front works on subwindows whether or not hidden surfaces are on. You can also use priority to control what appears in front. See Section 6.1 for more information on hidden surface removal and bringing objects to the front.
By default, windows are opaque - they obscure any window behind them. However, you can make a window transparent by setting the window pattern to be "clear":
To make a window transparent but also allow the geometry in subwindows to interact with the geometry in the parent window, pass the "invisible" option when calling ::Set_Window_Pattern.
In addition, you can use ::Set_Window_Pattern to define a background pattern for a window. Except for the "clear" and "invisible" patterns, the possible window patterns are the same as polygon-face patterns - for example, "##" for a crosshatch, "::" for dotted, and "" for checkerboard. See the ::Set_Window_Pattern command for more information.
Two options of the ::Set_Color command apply to windows. For example,
sets the window background color to light gray. When a window has a pattern (other than "solid" or "clear") applied to it, then the pattern is drawn with the window contrast color. For example,
draws a window background consisting of yellow dots on a purple background (this example should not be taken as an endorsement of this particular color scheme).
Gradient Window Background
Setting the "window coloring" option for ::Set_Window_Pattern tells the system to draw a gradient window background. The option is set to a direction such as 'N', 'NE', etc... The window color is the 'from' color and the window contrast color is the 'to' color. For example, "N" would mean that the bottom of the window will reflect the window color while the top of the window will be the window contrast color. See the ::Set_Window_Pattern reference for a full list of options.
The "Blend" Style
::Set_Window_Pattern accepts an option called "blend". This style creates the window background by sampling from the window background of the window it lies on top of. The sample is taken from the exact position of the window within the background window. This is particularly useful when you want an opaque subwindow to blend in with a window which has a gradient background.
Consider the case where there is a subwindow on top of another and it (the subwindow) is non-clear. Prior to this new option, one would probably set the color of the subwindow to be the color of the background window. The effect is that it seems clear, but the geometry in the background window is clipped about the window. The option allows a similar effect for gradient window backgrounds.
You can also use an image as a window backdrop as described in section 5.2.
If you have multiple images depicting the background of your scene, you can create a skybox.
Finally, when HOOPS creates a window, it draws a two-pixel-wide border line around the edge of the window as a window frame. The image inside the window is shrunk slightly to take the frame into account. You can control whether HOOPS draws this window frame with the ::Set_Window_Frame command. The options are "on", "single" and "off." By default, the window frames options is set to "on." To draw a single pixel wide frame, choose the "single" option. To turn frames off, use
In addition to these options, "single" and "on" have two modifiers "decorative" and "inset." The "decorative" modifier tells HOOPS not to shrink the image. Instead, it will draw the image as specified in the database and then draw the frame on top of what was rendered. If the "inset" modifier is set, then HOOPS will always draw the window frame entirely inside the parent window frame.
The window frame is drawn using the window contrast color.
When HOOPS/3DGS is used with a window system, normally the overall window (the window that corresponds to "?Picture") will have a border drawn around it by the window system, so HOOPS/3DGS does not draw an additional frame around that window. The border drawn by the window system normally also contains a control area containing a close box, controls for resizing the window, scroll bars, and so on. You can control the frame of the overall window with the "border" and "control area" options of ::Set_Driver_Options. For example, the following command instructs HOOPS/3DGS to tell the window system not to draw a border or a control area around the overall window:
Polygonal Clip Regions
HOOPS/3DGS windows are essentially rectangular clip regions which represent the software abstraction of a viewport. HOOPS/3DGS also supports polygonal clip regions. These can be useful to provide special 'detail' views. For example, the application might display a circle around a particular area of the screen which is 'blown up' to depict a view that zooms into that region. The routine ::Set_Polygonal_Clip_Region allows you to specify a convex polygonal region in the segment tree. All geometry in/beneath that segment will be drawn clipped to that region. The region acts as the 'containing' window for all geometry in the segments beneath it.
We previously discussed how text size can be specified in sru (screen relative units), oru (object relative units), or points. If your scene contains HOOPS/3DGS subwindows, then, in some cases, it is convenient to specify text size in wru (window relative units). When you use wru, the text size is set in relation to the smallest enclosing window, rather than in relation to the outermost window (as it is in sru). Setting text size in wru is useful for text inside a button or other user-interface gadget that is normally enclosed inside its own HOOPS window, so that the text will size automatically to the size of the button.
Generally, HOOPS/3DGS windows should only be used to represent 'subparts' of a single view, and each separate view should be associated with a unique HOOPS/3DGS driver instance (which has it's own implicit 'top-level' HOOPS/3DGS window). For example, if a view contains an axis or a legend, a HOOPS/3DGS window could be used to view an axis or a legend that lays 'on top' of the view. Each main view in your application should have it's own unique windowing-system (GUI) window, and that would have it's own driver instance. This is discussed in more detail in the following section.
The previous section discussed how to create subwindows of the overall application window using HOOPS/3DGS. In this section, we shall discuss how to create new window-system (overall application) windows from inside HOOPS/3DGS.
In review, a driver-instance segment (a segment such as "?Picture" that is a child of a driver segment) corresponds to a window-system window, so you can create a new window on the screen simply by creating a new driver instance. This window is not a HOOPS/3DGS subwindow; it is a full application window created by the window system, with a border, control area, and the user-interface gadgets that any normal application window has.
For example, this code opens up a driver instance that is a sibling window of "?Picture":`
Opening of a new driver instance.
If you run this program on an X11 system, then the "?Picture" alias will have the value "/driver/x11/unix:0.0", so this program will create a new segment with the name "/driver/x11/unix:0.0+A". So this program results in two segments that are children of the driver segment "/driver/x11". Each such segment is used by the driver to create a window on the screen.
The second window will be created with the same size and position as "?Picture", so we may have to move one window to see the other, but we can modify the window's position using the "subscreen" driver option. Note that, because we now have two driver instances, we can set separate driver options (such as "subscreen") on each one.
Why did we use the suffix "+A" to create the new window in the sample program? In HOOPS/3DGS, the name of the driver instance can be used by the driver. For example, for the PostScript driver, the name of the driver instance is the file name used to write the PostScript output. For the X11 driver, the name of the driver instance is the X11 name of the display to which to send the output.
It turns out that the X11 driver is the only current HOOPS/3DGS display driver that actually cares about the format of the driver-instance name. The syntax "0.0+A" tells X11 to create a new window. For more information, see the X11 documentation for your system.
What happens if we run the program on a non-X11 system? On Microsoft Windows, the default value of "?Picture" is "?driver/open/window0", so the program will create a new driver instance named "?driver/opengl/window0+A". The OpenGL driver uses the name "window0+A" to create the new window, but it does not care about the syntax of the name, so no harm is done. Thus, the "+A" makes X11 happy, and does not bother the other HOOPS/3DGS drivers. For more information about what each driver does with the driver instance names, see the HOOPS/3DGS Platform and Device Guide.
The code below displays a cube in four window-system windows. Note that, even though HOOPS/3DGS is using the window system to create the windows, this program does not contain any calls to the window system, so it is portable to any system that runs HOOPS. In the next section will see how HOOPS/3DGS can be used to draw into pre-existing window-system windows that are controlled by your native GUI code.
Lines 1 through 29 create the cube, and set up a style segment for the text captions.
Next we open up "?Picture" itself (line 30) and three additional siblings (lines 46, 61, and 75). We use "?Picture" for the front view. The ::Set_Driver_Options command (on line 32) sets the subscreen of "?Picture" to be the upper-left quadrant of the screen.
To open up siblings of "?Picture", we save the value of the "?Picture" alias in the string "segname", and concatenate "+A" onto it (lines 41 to 43). We also save the position of the letter "A", so that we can change it later for the other two segments (line 45). We use segname for the other two views, changing the suffix to +B and +C (lines 60 and 74).
The output consists of four separate window-system windows, as shown here:
For a typical graphics application, you will write the user interface using direct calls to the native window system, while HOOPS/3DGS will strictly be used to store and display the 2D/3D vector information. However, HOOPS/3DGS itself makes calls to the window system. For example, if you draw a 3D line using a HOOPS/3DGS command, HOOPS may need to transform the line into 2D screen space, remove any parts of the line that are hidden by objects in front of the line, and then draw one or more 2D line segments using native window-system calls. Thus, both HOOPS/3DGS and your application will be making calls to the native window system to draw objects into the same area of the screen. Consequently, HOOPS/3DGS and the native window system need to coordinate their actions, such as deciding when to clear the screen, what order to draw objects, what to do when a window is minimized or destroyed, and so on. Luckily, this coordination is remarkably easy to do with HOOPS/3DGS.
Connecting HOOPS/3DGS to a window system involves two tasks: connecting the window system's user input loop to HOOPS/3DGS so that HOOPS/3DGS can handle selection events, and connecting HOOPS/3DGS to a window-system window so that HOOPS/3DGS knows where to draw its output.
When you write your user interface using a window system, all low-level user input (such as mouse and keyboard events) will be handled by the window system.
The only help with user input that you need from HOOPS/3DGS is help handling selection events. To select a 3D object, the user clicks the mouse in a window containing HOOPS output. The window system handles the mouse event and passes it to your application. Your application's event handler then uses the mouse position from this event, and calculates what 3D object has been selected using the HOOPS command ::Compute_Selection. Selection events are covered in Section 4.2.
Normally, HOOPS/3DGS creates its own output window for each driver instance (such as "?Picture"). When you write your user interface using a window system, the window system will be in charge of creating the output window. All you need to do is to tell HOOPS/3DGS what this output window is, with the "use window id" driver option, so that HOOPS/3DGS can draw into it.
To connect HOOPS/3DGS to a window-system window, your application needs to perform the following six steps:
1. Delete the driver-instance segment associated with "?Picture", so that HOOPS/3DGS will not create an output window or try to send output to it.
2. Create a window. You create the window using the native window system; HOOPS/3DGS is not involved.
3. Create a new HOOPS/3DGS driver-instance segment for the window.
4. Set the "use window id" driver option on the new driver-instance segment; this option connects the driver instance to the window-system window.
5. Disable HOOPS/3DGS input, because user input will be handled by the window system. Use the "disable input" or "disable input = all" driver options (discussed in more detail below)
6. When the window system destroys the window, delete the driver-instance segment.
Note that it is possible to change the relative order of some of the steps. For example, we could create the window-system window (step 2) before we delete the "?Picture" driver instance (step 1).
Different window systems use different techniques to create windows, and because this book is not about how to use window systems, we shall not go into detail on the subject (we shall simply present several examples). For more information about creating window-system windows, see the documentation for your window system. Regardless of which window system you use, it will return an id or handle that identifies the window to your application. This id is the one that is passed to the "use window id" driver option.
The driver option "disable input" in step 5 tells HOOPS/3DGS not to process any input events, such as mouse clicks or key presses. It is up to your application to receive these events from the window system, and to process them appropriately. Even with the "disable input" driver option set, however, HOOPS/3DGS will process certain non-input events from the window system, such as "expose" events that are generated when the HOOPS/3DGS window is brought out from behind another window. If you want HOOPS/3DGS to disable event processing completely, you should specify "disable input = all" as the driver option. In this case, you will need to tell HOOPS/3DGS explicitly to redraw the scene (using ::Update_Display) when your application receives an expose event or other similar event. The above steps are encapsulated in the HOOPS/GUI modules for various GUI environments and toolkits, including MFC, Motif, ActiveX and QT)