HOOPS Visualize Documentation

< Table of Contents

PROGRAMMING GUIDE

3.1 Object Hierarchy

3.1 View hierarchy

HOOPS Visualize includes a basic object hierarchy which allows you allow you to more easily manage views within a window, scene updates, and user interactions. Four classes work together as nested containers to provide this functionality:

Each container nests inside the one above it. All the parent-child relationships are one-to-one, except for the layout - a Layout may have multiple views. Here's an example of the relationships:


[figure 3.1.a] An example of a single scene with multiple views

NOTE: Use of the canvas-layout-view-model hierarchy is not required for pure Visualize applications. However, it is required for any tookit integrations covered in Section 9.0. Each part of the view hierarchy is backed by a normal SegmentKey, and there is nothing that can be done with the view hierarchy that cannot be done using the corresponding segment keys alone. However, there is some pre-built functionality that is provided to make your job easier. For example, using the view hierarchy allows you to quickly set rendering modes, fit the camera to the scene, or provide an axis triad. See the following sections for more details.

If you do decide to use the view hierarchy, you should no longer update the scene using WindowKey::Update. Instead, call Update on any of the four view objects. For example, to update the whole window, you would call:

myCanvas.Update();
// or
myCanvas.UpdateWithNotifer();

To update only a single view, you would call:

myView.Update();
// or
myView.UpdateWithNotifer();

Refer to section 7.1 for more details on updating the scene.

In the following subsection, a simple window is built which includes a layout with two views, each one viewing a model using its own camera.

Step 1: Build the four core objects

Each of the classes is instantiated by the static Factory class. To start off the process, you need the WindowKey for the window you're planning to use. Typically, this would be returned from Database::CreateApplicationWindow, as mentioned in section 1.1.4.

[snippet 3.1.a]
Canvas canvas = Factory.CreateCanvas(windowKey);
Layout layout = Factory.CreateLayout();
View view1 = Factory.CreateView();
View view2 = Factory.CreateView();
Model model = Factory.CreateModel();

Step 2: Bind the objects into a hierarchy

After the objects are instantiated, the next step is to attach them together. When attaching views to a layout, you must specify the view rectangle, which is provided in window coordinates (see section 3.1.2 for details). After the view is attached to the layout, HOOPS Visualize refers to it as a layer. Layers and views can be thought of as analogous in most cases. However, when the same view is attached multiple times to one layout, each instance of that view is referred to as a layer.

[snippet 3.1.b]
canvas.AttachLayout(layout);
layout.AttachViewFront(view1, Rectangle(-1, 0, -1, 1)); // left view
layout.AttachViewFront(view2, Rectangle( 0, 1, -1, 1)); // right view
view1.AttachModel(model);
view2.AttachModel(model);
canvas.AttachLayout(layout);
layout.AttachViewFront(view1, new HPS.Rectangle(-1, 0, -1, 1)); // left view
layout.AttachViewFront(view2, new HPS.Rectangle( 0, 1, -1, 1)); // right view
view1.AttachModel(model);
view2.AttachModel(model);

In this particular example, the same model will be referenced by both views. However, it is perfectly fine to have each view refer to a different model.

Step 3: Insert the geometry into the Model

The layers and views are set up, but the Model is still empty. Get its associated SegmentKey and insert or load geometry as you normally would.

[snippet 3.1.c]
SegmentKey modelSegmentKey = model.GetSegmentKey();
ShellKey shellKey = modelSegmentKey.InsertShell(myShellKit);
SegmentKey modelSegmentKey = model.GetSegmentKey();
ShellKey shellKey = modelSegmentKey.InsertShell(myShellKit);

Step 4: Set attributes for each view

If you have multiple views of a model, each view would typically have different viewing attributes. After the views are set up, set attributes specific to each one by using the associated SegmentKey. Some settings, such as the camera, can be set directly:

[snippet 3.1.d]
SegmentKey viewSegmentKey = view.GetSegmentKey();
viewSegmentKey.GetVisibilityControl().SetText(false);
viewSegmentKey.SetCamera(cameraKit);
modelSegmentKey.GetMaterialMappingControl().SetFaceColor(RGBAColor(0.81f, 0.72f, 0.08f));
modelSegmentKey.GetMaterialMappingControl().SetEdgeColor(RGBAColor(0, 0, 0));
modelSegmentKey.GetVisibilityControl().SetEdges(true);
SegmentKey viewSegmentKey = view.GetSegmentKey();
viewSegmentKey.GetVisibilityControl().SetText(false);
viewSegmentKey.SetCamera(cameraKit);
modelSegmentKey.GetMaterialMappingControl().SetFaceColor(new RGBAColor(0.81f, 0.72f, 0.08f));
modelSegmentKey.GetMaterialMappingControl().SetEdgeColor(new RGBAColor(0, 0, 0));
modelSegmentKey.GetVisibilityControl().SetEdges(true);

After completing steps 1-4, the view hierarchy is established. The result is two views in the same windows. Each view has its own camera but is viewing the same object:

[figure 3.1.b] Viewing the same model from different places using the view hierarchy classes

3.1.1 Canvas

As mentioned previously, the Canvas is analogous to the client area of a native GUI window. The Canvas encapsulates a WindowKey which can be accessed by using Canvas::GetWindowKey().

The Canvas has one main purpose: to act as the root of the view hierarchy. However, it does have a few auxiliary functions. For example, you can control updates via the Canvas methods. If you want to update the entire window, you would use Canvas::Update(). Note that if you have multiple attached objects of type View, each of those views will also be updated (to update only one View, you would call Update() on that particular View).

The standard approach is for each application GUI window to have a single 3D view, which means you will only have one View per canvas. In this case you do not need to use the Layout object, and can directly ask HPS to create an implicit Layout from an View:

[snippet 3.1.1.a]
canvas.AttachViewAsLayout(view);
canvas.AttachViewAsLayout(view);

If you are building a scene hierarchy that involves multiple View objects, you would attach each View to a Layout, and then attach the Layout to the Canvas.

Fixed Framerate

The Canvas provides the ability to maintain a fixed framerate and is covered in Section 7.3.5.

IMPORTANT: When you are finished using the canvas and want to destroy the window, be sure to call Canvas::Delete. Doing so ensures the driver gets a chance to shutdown cleanly before the application window is freed from memory.

3.1.2 Layout

As mentioned above, an Layout is used to position views on a Canvas. If you are using the standard approach where there is a single View per Canvas, you don't need to bother with this section - simply use the code from snippet 3.1.1.a.

If you do wish to have more than one view per canvas, the placement of each view is specified by a Rectangle using window coordinates.

[figure 3.3.b] The "window" coordinate system

Using this coordinate system, a subwindow can be specified anywhere in window space, even in areas outside the [-1, 1] bounding space (although it would be clipped in this instance). For example, if you want to position a View in the lower-right quadrant of the window above, you would do the following:

[snippet 3.1.2.a]
// Rectangle constructor parameters are left, right, bottom, top
myLayout.AttachViewFront(view, Rectangle(0, 1, -1, 0));
// Rectangle constructor parameters are left, right, bottom, top
myLayout.AttachViewFront(view, new HPS.Rectangle(0, 1, -1, 0));

It is possible to have overlapping views. If you had an second subwindow positioned in the center of the screen with bounds of [-0.5, 0.5, -0.5, 0.5], one View would overlap with the other. To ensure they are layered in the order of insertion, use AttachViewFront. Similarly, AttachViewBack will add the view to the back of the list. The list is always drawn last-to-first, so that the view at the front of the list is drawn atop the others.

The layer concept

While using the API, you may notice the use of the word 'layer'. A layer can be thought of as a View and its position in a layout. If any views overlap, the position is used to determine which views are drawn first. To reorder views after insertion, use one of the Layout methods, such as:

It is possible to have multiple instances of the same view attached to one layout. In this case, detaching a view using Layout::DetachView will remove both instances of the view from the layout. However, detaching a layer using Layout::DetachLayer will remove only the layer specified.

3.1.3 View

Among other things, a View allows you to set the rendering mode, fit the camera to the extents of the model, get an array of all attached operators, and update itself.

Rendering mode

The rendering mode sets the general appearance of the geometry in the View. The rendering mode refers to a collection of common settings used to achieve a high-level effect. These settings can be enabled individually. However, setting the rendering mode on the View is provided as a convenience. For example, outside of the view hierarchy, there is no "wireframe" attribute. Instead, you would have to manually disable faces and enable lines to get this effect. With View, this is done for you by simply setting the render mode to Wireframe.

Other render modes enable hidden line rendering or a particular lighting interpolation algorithm. All options are listed in the reference manual page for Rendering::Mode.

[snippet 3.1.3.a]
// sets geometry to wireframe
view.SetRenderingMode(Rendering::Mode::Wireframe);
// geometry rendered with Phong lighting
view.SetRenderingMode(Rendering::Mode::Phong);
// geometry rendered with hidden lines
view.SetRenderingMode(Rendering::Mode::HiddenLine);
// sets geometry to wireframe
view.SetRenderingMode(Rendering.Mode.Wireframe);
// geometry rendered with Phong lighting
view.SetRenderingMode(Rendering.Mode.Phong);
// geometry rendered with hidden lines
view.SetRenderingMode(Rendering.Mode.HiddenLine);

FitWorld

View::FitWorld is used to automatically adjust the camera to the extents of the scene. FitWorld automatically calls View::Update, so be sure to add logic to avoid a double-redraw.

[snippet 3.1.3.b]
// adjusts the camera to the scene extents and redraws the scene
view.FitWorld();
// adjusts the camera to the scene extents and redraws the scene
view.FitWorld();

Enabling the axis triad and navigation cube

The View object is able to display an axis triad and navigation cube to assist with orientation of the user. Either object may be placed in any of the corners of the window. To enable them, use the following code:

[snippet 3.1.3.c]
// enable axis triad
myView.GetAxisTriadControl().SetVisibility(true).SetLocation(AxisTriadControl::Location::BottomRight);
// enable navigation cube
myView.GetNavigationCubeControl().SetVisibility(true).SetLocation(NavigationCubeControl::Location::BottomLeft);
// enable axis triad
myView.GetAxisTriadControl().SetVisibility(true).SetLocation(HPS.AxisTriadControl.Location.BottomRight);
// enable navigation cube
myView.GetNavigationCubeControl().SetVisibility(true).SetLocation(HPS.NavigationCubeControl.Location.BottomLeft);
[figure 3.1.c] The navigation cube and the axis triad