3D Tutorial: Manipulating 3D Geometry

This step reviews how to select/highlight, and manipulate 3D objects.

manipulating.jpg

Manipulation Operators

HOOPS/MVO contains several operators that provide support for manipulating 3D geometry. They include HOpObjectRotate and HOpObjectTranslate, which rotate and translate objects in the selection list. Before hooking up and expermenting with these operators, we first need to review some of their limitations:

  • They only operate on an object that is currently selected. (The default use-model in MVO is that you first select on an object, and then you operate on it.)
  • They only operate on the selection list object of the 'key' is of type 'segment. If they key is of type 'geometry', then nothing will occur. The pre-supplied Rotate/Translate operators were written this way since the modelling matrix in a segment affects everything in that segment (just like any other attribute). Therefore, the intention was that if you wish to apply a rotation or translation, it needs to be done to a segment. (The rotation/transation operators would be extended to also work on individual geometries, but they would have to first be moved out of their parent segment and into a temporary segment, since we wouldn't want to blindly apply the transform to the entire segment in this case. The HOpObject<type> operators might be extend to support entity-level manipulation in the future.)
  • They only operate on the selection list if it only has1 item in it. This follows from the 2nd point above; if we wanted a translation or rotation to be applied to multiple segments, the results would be non-intuitive since the rotate/translate operators take into consideration the mouse-movements relative to a single segment's object-space. For example, if the user were clicking on a cube at its centroid and then dragged to it's edge, (and the current operator was 'rotate') they would expect the cube to rotate by a corresponding amount, but rotating other objects in an entirely different part of the screen would be a bit strange.

So, before we can rotate or translate anything, we need to first select on an object, it needs to be of type 'segment', and there can only be 1 object in the list. However, as reviewed in the first '2D' tutorial, the default selection mode for the HOpSelect<selection_type> operators is such that they will select on individual geometry entities (not segments). Therefore, we first need to customize the selection support in our 'Spheres' application to provide 'segment-level' selection.

 

Adding Segment-Level Selection

Similar to the 2D Tutorial, we'll abstract our Spheres application's selection functionality to provide 2 levels of selection: 'entity' and 'segment'. The code, while similar to the 2D tutorial, is a bit different since our Spheres application doesn't have the concept of selecting on 'layers'.

The HOOPS/MVO HSelectionSet class already defines an enumerated type called HSelectLevel which we can utilize. We'll extend the existing HSpheresSelectionSet class to add a new member called m_SelectLevel along with methods to Get/Set the selection level.

(Contents of spheres/HSpheresSelectionSet.h)

 
class HSpheresSelectionSet :  public HSelectionSet 
{
private:
    HSelectLevel m_SelectLevel;

public:
    HSpheresSelectionSet(HBaseView* view);
    ~HSpheresSelectionSet();

    virtual void Select(HC_KEY key, int num_include_keys, HC_KEY * include_keys, bool emit_message = false);
    virtual void DeSelect(HC_KEY key, int num_include_keys, HC_KEY * include_keys, bool emit_message = false);
    virtual void DeSelect(HC_KEY key, bool emit_message = false);

    virtual void SelectFromMessage(HC_KEY key, int num_include_keys, HC_KEY * include_keys, bool emit_message = false);
    virtual void DeSelectFromMessage(HC_KEY key, int num_include_keys, HC_KEY * include_keys, bool emit_message = false);

    void SetSelectLevel(HSelectLevel level) { m_SelectLevel = level; }
    const HSelectLevel GetSelectLevel() { return (m_SelectLevel);}

};

The Constructor

First, we set the starting segment level to 'Segment':

 
HSpheresSelectionSet::HSpheresSelectionSet(HBaseView* view) : HSelectionSet(view)
{
    m_SelectLevel = Segment;
}
 

Customing Selection and DeSelection to select on Segments

Next, we overload the HSpheresSelectionSet::Select and HSoccerSelectionSet::DeSelect, and HSoccerSelectionSet::IsSelected methods to first check whether we are in 'segment' selection mode. We check for this because perhaps our application will have a regular 'entity' selection mode to allow the user to select on individual pieces of geometry, in which case we don't want to filter for 'segment'.

If we are in segment selection mode, we check if the key is of type 'segment'. If it isn't, we get its owner. The Select method code is as follows:

 
void HSpheresSelectionSet::Select(HC_KEY key, int num_include_keys, HC_KEY * include_keys, bool emit_message)
{
    char    keyType[MVO_BUFFER_SIZE];

    // if we are in 'Segment' selection mode, do some checking
    if (m_SelectLevel == Segment) 
    {
        // Get the key type 
        HC_Show_Key_Type(key, keyType);

        // if they key isn't to a segment (which means it's to an entity)
        // then we get its parent
        if (!streq("segment", keyType))
            key = HC_KShow_Owner_By_Key(key);   
    }
    
    HSelectionSet::Select(key, num_include_keys, include_keys, emit_message);
}

The DeSelect methods will contain similar code as above.

 

Let's Manipulate!

Now that the application can select/deselect segments, we can manipulate them using the HOpObject<manipulation_type> operators. Let's hook up the rotate operator by adding a new GUI button that is mapped to the following method:

 
 

Test out the rotate operator by first selecting an object (remember that the rotate operator will only work if a single entity is selected).