Fundamentals

Database - HDB

A single HDB object should be created and initialized within a startup function of your application.

Example:

void initialize_my_app_data()
{
        m_pHoopsDB = new HDB;
        m_pHoopsDB->Init();
}

When your application is ready to terminate, you should destroy the HDB object before the end of the application’s main function (or, if created in a DLL/share library, before the library is unloaded). If you try to destroy HDB after that point, you may experience undefined behavior.

Model - HBaseModel

The HBaseModel class encapsulates a logical grouping of graphical information known as a model. The model is accessed by a key that represents a segment found in the include library segment in the HOOPS database. When HBaseModel is instantiated, a new segment is created in the include library. To directly create and/or modify graphical information for the model, call HBaseModel::GetModelKey to access its root segment. You can also load information about a model by importing a file using the HBaseModel::Read method. HOOPS/MVO can import a wide variety of file formats. When you have finished constructing and enhancing your model, you can save this informtion to a file using HBaseModel::Write which can export to many commonly used file formats. The following code snippet shows how you can create an instance of HBaseModel and load information into it:

// Creating new instance of a model
HBaseModel *modelPlane = new HBaseModel();

// Loading in model information
modelPlane->Read("ModelPlane.hsf");

// Getting Model Key
HC_KEY model_key = modelPlane->GetModelKey();

HC_Open_Segment_By_Key (model_key);
        // Creating a wireframe effect
        HC_Set_Visibility("faces=off, edges=on");
HC_Close_Segment();

// Saving the model to a file
modelPlane->Write("WireframeModelPlane.hsf");

Creating a Custom Model Object

The following code is the interface for a sample model class called HMyModel, derived from HBaseModel. It adds a custom Read function to support reading of ‘FOO’ files, in addition to relying on the base classes’ ability to read HMF files:

#include "HBaseView.h"

class HMyModel : public HBaseModel
{

public:
        HMyModel();

        void Read(const char * FileName);
        void ReadFooFile(const char * FileName);
};

The following provides an example of the application-specific Read function for the custom Model object. Note that it calls the base classes’ Read function to handle reading of HMF files, since they can be read by HBaseModel::Read:

int MyModel::Read(const char * FileName)
{
        char extension[120];

        // read the file into the model object's model segment
        HC_Open_Segment_By_Key(m_ModelKey);

          extension[0]=0;
          HC_Parse_String(FileName,".",-1,extension);
          strlwr(extension);

          if  (streq(extension,"foo"))
                // bogus function for discussion purposes
                ReadFooFile(FileName);
          else if  (streq(extension,"hmf"))
                // call base class method to read an HMF file
                HBaseModel::Read(FileName);
          else
                return (1); // unknown file type

        HC_Close_Segment();

        return(0);
}

View - HBaseView

To display a model in HOOPS, you include it in a scene which can be viewed via an output device. The HBaseView class encapsulates all the information associated with the presentation of a scene. Under the HOOPS/MVO architecture, a model can have multiple views but a view can only be associated with one model. The most common approach is a one to one mapping between a GUI widget object and an instance of HBaseView. Each HBaseView object generally should be created and initialized in a startup function associated with each view. In this example, HMyBaseView (a simple class derived from HBaseView) is instantiated, associated with a GUI widget, and initialized:

void InitialViewUpdate()
{

        m_pHView = new HMyBaseView( hmodel, NULL, NULL, NULL, (long) m_hWnd, (long)(HPALETTE)*(GetPalette()));
        m_pHView->Init();
}

The HBaseView constructor requires a pointer to the HBaseModel object associated with the view. It also has a variety of other optional arguments, including the window handle of the native GUI window, and the native GUI palette. The window handle is internally passed to HOOPS, which will draw into the user-specified window rather than creating a new one. For a diagram of the underlying HOOPS segment structure, refer to the section on the HOOPS MVO classes.

When HBaseView is initialized, it creates a new segment under a driver instance segment. This segment contains all the information about the model and other elements in the scene such as lighting, rendering modes, camera settings and window attributes.

Rendering Modes

One element to consider when displaying a scene is how it should be rendered. For a photorealistic effect, you can choose from a variety of lighting interpolation methods like Gouraud or Phong. In some situations, it might make sense to show just the wireframe of your model. With HBaseView class, you can seamlessly move between different rendering modes using the HBaseView::SetRenderMode method. You also choose one of the methods that start with Render like HBaseView::RenderPhong or HBaseView::RenderShadedWireframe.

../../_images/0231agouraudurn.png ../../_images/0231aphongurn.png ../../_images/0231awireframeurn.png

Gouraud, Phong, and wireframe rendering of an urn.

Camera Settings

HBaseView has a set of camera methods that lets you control the viewpoint for a scene. When setting the initial parameters for HBaseView, you can use HBaseView::SetInitialCamera to specify the camera coordinates when the scene is first displayed. During the session, you can always return to this initial camera position via the HBaseView::ZoomToInitialCamera method. This method moves the camera from its current position to the coordinates you specifed in HBaseView::SetInitialCamera. By default, as the camera changes position, the lights follow it. If you want the lighting position to remain constant, call HBaseView::SetLightFollowsCamera and pass false. The viewpoint change can be rendered as an instanteous change or with a smooth transition where the user sees the camera pan and zoom to the new location. By default, all camera changes are instanteous. To choose a smooth transition, call HBaseView::SetSmoothTransition and pass true. Once you have turned on smooth transition, you can set the transition time by calling HBaseView::SetSmoothTransitionDuration.

General camera control can be accomplished via the HBaseView::SetCamera method. You can either pass an HCamera object which encapsulates information like position, field and target or pass attributes individually. HBaseView also has methods for the most common camera movements. For instance, HBaseView::ZoomToExtents resets the camera such that the view contains the extents of the scene. To prevent the camera from zooming in too closely, you can use the HBaseView::SetZoomLimit. If you want more control when zooming to a specific part of the scene, HBaseView offers a group of methods that begin with “Fit”. For instance, HBaseView::FitWorld is used by HBaseView::ZoomToExtents for calculating the exact camera position. Other fit methods include HBaseView::FitSegment and HBaseView::FitSelection.

Another useful camera control method is HBaseView::SetViewMode. This method lets you easily move between different axis planes like the XY plane or YZ plane. You can also specify custom view planes defined as front, back, left, right using the HBaseView::SetViewAxis method.

Axis Triad

The camera methods in HBaseView update the camera position programmatically. However, most applications also let the end-user control the camera interactively. HBaseView allows the user manipulate the camera with the mouse via a built-in axis triad. To enable the axis triad, call HBaseView::SetAxisMode() passing an AxisMode. You can simply pass AxisMode::AxisOn or one of the other variations available. Once the axis is enabled, HBaseView will render an axis triad at the bottom left corner of the view. The axis triad consists of three orthogonal arrows emanating from a center point and represent the X, Y and Z axes. When the user moves the axis triad with mouse, HBaseView responds by updating the view accordingly. HBaseView handles a number of mouse events related to the axis triad including mouse movement and double-clicking.

../../_images/0233atoycarfront.png ../../_images/0233atoycarside.png ../../_images/0233atoycartop.png

Three views of a toy car model with the axis triad used for navigation and orientation.

Interactivity and Selection

HBaseView offers a representation of a scene but it also has the capabilities to manage extensive interactivity through its relationship with operators. When the mouse, keyboard or any input device is employed, HBaseView captures these events and dispatches them to the current operator such as HOpSelectAperture which selects object based ona single mouse click. The following code sample shows how you can set the current operator for a view:

m_pHView->SetOperator(new HOpSelectAperture(m_pHView));

Once the selection operator is activated as the current operator, it interacts with the view updating information about which items are selected and deselected. HBaseView manages the selection process via HSelectionSet.

Creating a Custom View Object

The following code is the interface for a sample view class called HMyView, derived from HBaseView. It adds a custom Init function along with some sample members:

#include "HBaseView.h"

class HMyView : public HBaseView
{

        public:
                HMyView(HBaseModel *model, char *alias = null, char *driver_type = null,
                                        char *instance_name = null, long window_handle = 0, long colormap = 0);

                long m_parts_seg;
                int  m_parts_num;
                long m_construction_segment_key;

                void Init();
};

The following provides an example of the application-specific Init function for the custom View object. Note that it calls the base class’ Init, and creates a custom selection set object as well:

void HMyView::Init()
{
        // call base's init function first to setup the default HOOPS segment
        // hierarchy for the view
        HBaseView::Init();
        m_parts_seg = 0;
        m_parts_num = 0;
        m_construction_segment_key = 0;

        // create our app-specific Selection object and initialize
        m_pSelection = new HMySelectionSet();
        m_pSelection->Init();
}

Operators - HBaseOperator

The HBaseOperator class is the base for classes which handle user input and operate on the scene. HBaseOperator creates a mapping between user input and interaction logic by handling GUI events such as mouse movements and key presses. To construct a custom operator, create a class that derives from HBaseOperator and then implement the virtual methods that handle user events like mouse movements and key presses. For instance, if you want to create an operator that inserts a marker into the scene when the user presses the left mouse button, your operator would implement the OnLButtonDown method. The following code sample shows how this can be done:

class HOpCreateMarker : public HBaseOperator
{
public:

        HOpCreateMarker(HBaseView* view, int DoRepeat = 0, int DoCapture = 1);
        ...
        virtual int OnLButtonDown(HEventInfo &hevent);
        virtual HBaseOperator * Clone();
};

int HOpCreateMarker::OnLButtonDown(HEventInfo &event)
{
        HPoint pos = event.GetMouseWorldPos();
        HC_Open_Segment_By_Key(GetView()->GetModelKey());
                HC_Set_Visibility("markers=on");
                HC_Insert_Marker(pos.x,pos.y,pos.z);
                GetView()->Update();
        HC_Close_Segment();
        return(HOP_READY);
}

Creating and Setting an Operator

You can have any number of operators on hand. However, to use them in a scene, you must create an instance of the operator and then attach it to a view using HBaseView::SetOperator. This method registers the operator with the event manager which allows it to receive dispatched events. The following code sample shows how to set the current operator on a view:

m_pHView->SetOperator(new HOpCameraOrbit(m_pHView));

In addition to letting you create custom operators, HOOPS/MVO is also seeded with a number of prebuilt operators. They generally fall into the following categories:

  • Camera Manipulation operators for panning, zooming, or orbiting the camera. These include HOpCameraOrbit, HOpCameraZoom, and HOpCameraPan.

  • Object Creation operators for creating and inserting objects in the database. These include HOpCreateCircle, HOpCreateCylinder, HOpCreateSphere, and HOpCreateCuttingPlane.

  • Object Manipulation operators for translating and rotating objects. These include HOpObjectRotate, HOpObjectTranslate, and HOpObjectAnnotate.

  • Selection operators for picking objects. These include HOpSelectPolygon and HOpSelectPolyline.

  • Markup operators - for creating notes in the scene. These include HOpMarkupAnnotate, HOpMarkupCircle, and HOpMarkupMeasure.

Creating a Custom Operator Object

While the HOOPS/MVO prebuilt operators provide common methods of modifying the camera, manipulating objects, or selecting geometry, your application will likely require additional application-specific operators and/or UI specification. If the Model/View/Operator paradigm is used for developing a proof-of-concept, prototype, or final application, custom operators should be derived from HBaseOperator.

The following code is the interface for a sample operator class called HOpPanCamera, derived from HBaseOperator. It implements the methods that handle left mouse button events:

#include "HBaseOperator.h"

class HOpCameraPan : public HBaseOperator
{

        public:
                HOpCameraPan(HBaseView* view, int DoRepeat=0, int DoCapture=1);
                ~HOpCameraPan();
                void GetName(char *name);

                int OnLButtonDown (HEventInfo &event);
                int OnLButtonDownAndMove(HEventInfo &event);
                int OnLButtonUp (HEventInfo &event);
}

The following code provides an example of the application-specific event handling functions for the custom HOpCameraPan operator. Note that an HEventInfo object, discussed in detailed in this section, is passed into each function:

// left button went down
int HOpCameraPan::OnLButtonDown(HEventInfo &event)
{

        if (m_pView->GetModel()->GetBhvBehaviorManager()->IsPlaying() &&
                        m_pView->GetModel()->GetBhvBehaviorManager()->GetCameraUpdated())
                return (HOP_OK);

        if (!m_bOpStarted)
                m_bOpStarted = true;

        m_ptNew = event.GetMouseWorldPos();

        m_ptFirst.x = m_ptNew.x;
        m_ptFirst.y = m_ptNew.y;
        m_ptFirst.z = m_ptNew.z;



        return (HOP_OK);
}

// left button is down and has moved
int HOpCameraPan::OnLButtonDownAndMove(HEventInfo &event)
{
        HPoint delta, camera,target;

        if (!m_bOpStarted)
        return HBaseOperator::OnLButtonDownAndMove(event);

        m_ptNew = event.GetMouseWorldPos();

        delta.x = m_ptNew.x - m_ptFirst.x;
        delta.y = m_ptNew.y - m_ptFirst.y;
        delta.z = m_ptNew.z - m_ptFirst.z;

        HC_Open_Segment_By_Key (m_pView->GetSceneKey());

                HC_Show_Net_Camera_Target (&target.x, &target.y, &target.z);
                HC_Show_Net_Camera_Position (&camera.x, &camera.y, &camera.z);

                HC_Set_Camera_Target (target.x - delta.x, target.y - delta.y, target.z - delta.z);
                HC_Set_Camera_Position (camera.x - delta.x, camera.y - delta.y, camera.z - delta.z);

        HC_Close_Segment();

        m_pView->CameraPositionChanged();

        m_pView->Update();
        return (HOP_OK);
}


// left button has come up
int HOpCameraPan::OnLButtonUp(HEventInfo &event)
{
        if(!m_bOpStarted)
                return HBaseOperator::OnLButtonDownAndMove(event);

        m_bOpStarted = false;
        m_pView->CameraPositionChanged(true, true);

        return(HOP_READY);
}

Selection Sets - HSelectionSet

One of the most common operations in a 3D-based application is selection. HOOPS/MVO provides high-level selection capabilities in HBaseView via the HSelectionSet class. For a given view, thereis one HSelectionSet object used for maintaining selected items. This class manages a list of items which have been marked as selected. When an item is added to the selection set, HSelectionSet highlights the item based on the highlight mode. Prebuilt selection operators such as HOpSelectArea and HOpSelectAperture will utilize the HSelectionSet object associated with HBaseView.

Selection Level

There are various parameters that you can set on HSelectionSet to control selection behavior. One of the most important of these is selection level. When you set the selection level, you determine the level of selection granularity that can occur. The selection level is controlled via the method HSelectionSet::SetSelectionLevel, which accepts one of the enumerated HSelectLevel options as an argument. The default selection level is ‘segment’, which is the least specific level, and results in the HSelectionSet class to store selected HOOPS/3dgs segments as it’s selected items. (Note that if a ‘segment’ is highlighted, that implies that everytihng in that segment and it’s segment subtree will be highlighted.) When you set the selection level to HSelectLevel::HSelectEntity, selection items will include HOOPS/3dgs graphical primitives such as shells, spheres and meshes. There are two additional selection levels that allow you to select parts of shells. The first is HSelectLevel::HSelectRegion which will let you select predefined regions in a shell. The second is HSelectLevel::HSelectSubentity which allows you to select faces, vertices and edges. By default, when you set the selection level to subentity, selection includes faces, vertices and edges. However, you can limit the selectability for faces, vertices and edges via the HSelectionSet::SetSubentityFaceSelection, HSelectionSet::SetSubentityVertexSelection and HSelectionSet::SetSubentityEdgeSelection methods, respectively.

Selection Processing Control

Once you have set the selection level to the desired granularity, be sure to call the associated method HBaseView::SetViewSelectionLevel. This method sets a HOOPS/3dGS heuristic that determines how much selection processing HOOPS/3dGS will perform. If the HSelectionSet’s selection level is set to HSelectLevel::HSelectEntity, HSelectLevel::HSelectSubentity or HSelectLevel::HSelectRegion, you should call HBaseView::SetViewSelectionLevel and pass HViewSelectionLevel::HSelectionLevelEntity. If you have set HSelectionSet’s selection level to HSelectLevel::HSelectSegment, you should call HBaseView::SetViewSelectionLevel and pass HViewSelectionLevel::HSelectionLevelSegment. Setting the ViewSelectionLevel in conjunction with your HSelectionSet’s selection level is crucial for optimal performance and accuracy. If your application seems to be suffering from poor performance or inaccuracies during selection, please check that both the HSelectionSet’s selection level and the ViewSelectionLevel are in sync.

HSelectionSet Methods

To add items to the HSelectionSet class, you can use the set of Select methods. Primarily, selection at the application level is triggered by a user event like a mouse button being depressed. When the event is dispatched a selection operator like HOpSelectAperture or HOpSelectArea will perform a hit test based on event information. Using one of the compute selection methods available in HOOPS/3dGS, the operator determines the set of items eligible for selection. Based on the selection level criteria of HSelectionSet, eligible items are added to the HSelectionSet via the Select methods. If the item to be selected is segment or a piece of geometry, one of the variants of HSelectionSet::Select is called. If item is a shell and the selection level is region, HSelectionSet::SelectRegion is called. If the item is shell and the selection level is subentity, HSelectionSet::SelectSubentity is called. Multiple faces, vertices and edges can be selected in one call to HSelectionSet::SelectSubentity. The parameters for this method are very similar to the results of Show_Selection_Elements. Thus minimal processing needs to be done before calling the select method.

../../_images/025aEdgeSelection.png

A model with some edges highlighted. This effect was achieved via the subentity selection level setting in ``HSelectionSet``.

If you want to clear some of all of the selection list, call HSelectionSet::DeSelectAll or HSelectionSet::DeSelectSubentity. To remove an individual item from the selection list, you can use one of the HSelectionSet::DeSelect methods.

The selection list can also be directly managed via the HSelectionSet methods. The following code demonstrates how the various HSelectionSet methods might be used. It implements a selection policy based on the state of the Control and Shift keys:

// handle control, shift and no_key cases
if (event.Control())
{
        // if selected, deselect, else select
        if (selection->IsSelected(key, incl_count, incl_keys))
                selection->DeSelect(key, incl_count, incl_keys, true);
        else
                selection->Select(key, incl_count, incl_keys,true);
}
else if (!(selection->IsSelected(key, incl_count, incl_keys)))
        selection->Select(key, incl_count, incl_keys, true);

Highlighting

Currently, the supported highlight modes include conditional style highlighting, quick moves reference highlighting and inverse transparency. The first and default mode is HSelectionHighlightMode::HighlightDefault which provides basic highlighting for an object based on a set of predetermined conditions. The HSelectionHighlightMode::HighlightQuickmoves mode, on the other hand, provides a more efficient way to highlight by creating a reference object to stand in for the actual object. Thus any modifications do not require a full update to the segment structure. In both cases, the behavior that the end user sees in primarily the same. HSelectionSet can be extended to support other sophisticated highlighting modes and styles that may better suit your application.

Inverse Transparency Highlighting

When the highlight mode is set to HSelectionHighlightMode::InverseTransparency, highlighting behaves in a different way than in the previously discussed modes. The selected item remains the same while all other objects in the scene become transparent. You can control the level of transparency by using HSelectionSet::SetHighlightTransparency and passing a value from 0 to 1 where 0 is opaque. The default transparency is set to 0.9. For the best visual results, we suggest that you set the highlighting to render in gray scale by using HSelectionSet::SetGrayScale.

../../_images/0271InverseTransparencyHighlighting.png

A model with inverse transparency highlighting mode and grayscale in effect.

Dynamic Highlighting

When dynamic highlighting is enabled via the HBaseView::SetDynamicHighlighting method, HOOPS/MVO automatically highlights any geometric entities that lie underneath the cursor. This behavior is similar to the “focus follows mouse” found on UNIX versus the “focus follows click” on Microsoft or Apple operating systems.

In addition to this highly interactive behavior mode, dynamic highlighting offers the ability to highlight only specific regions within a shell. To enable this capability, you need to get the highlight selection set from HBaseView with a call to HBaseView::GetHighlightSelection. Then call HSelectionSet::SetSelectionLevel and pass HSelectLevel::HSelectRegion as seen in the following code sample:

//enabling dynamic highlighting
m_pHView->SetDynamicHighlighting(true);

//set the correct heuristic for all hit test computations
m_pHView->SetViewSelectionLevel(HViewSelectionLevel::HSelectionLevelEntity);

//enable highlighting of individual regions in a shell
HSelectionSet *highlightSelectionSet = m_pHView->GetHighlightSelection();
highlightSelectionSet->SetSelectionLevel(HSelectLevel::HSelectRegion);

Dynamic highlighting is off by default.