2D Tutorial: Creating Geometry

This step reviews how to enhance the application to provide support for creating geometry.

soccer_field_geometry.gif

Let's add a simple operator which allows the user to add 'wireframe' rectangles to the scene. First, we add a new GUI button on the toolbar which is mapped to the following new method on the MFC view:


We then define our the HOpCreateRectangle interface. HOpCreateRectangle.h looks like the following:

class HOpCreateRectangle : public HOpConstructRectangle
{
public:
    HOpCreateRectangle(HBaseView* view, int DoRepeat=0, int DoCapture=1);
    ~HOpCreateRectangle();

    virtual const char * GetName();  

    virtual int OnLButtonUp(HEventInfo &event);  
    
    HBaseOperator * Clone();  
};
#endif

Our new class is derived from the HOOPS/MVO HOpConstructRectangle class which already does almost everything we need. It handles the Left mouse button down, Motion and Up methods to rubberband a 'construction' rectangle in the view. However, HOpConstructRectangle::OnLButtonUp flushes the final rectangle from the scene since it only creates temporary construction geometry (intended for purposes like our example here). We'll want to overload OnLButtonUp to take the rectangle point information and actually insert a rectangle into our model. First we'll quickly review the constructor, GetName and Clone methods that we generally always implement when deriving from an existing operator.

Constructor

We just call through to the base class:

HOpCreateRectangle::HOpCreateRectangle(HBaseView* view, int DoRepeat, int DoCapture) : HOpConstructRectangle(view, DoRepeat, DoCapture, false)
{
}

 

Clone

Return a pointer to a copy of our new operator:


GetName

We call our new operator HOpCreateRectangle:

const char * HOpCreateRectangle::GetName() { return "HOpCreateRectangle"; }

 

OnLButtonUp

Here's where the real work gets done, but it's quite straightforward. By the time OnLButtonUp is called, the operator has initialized the m_ptFirst and m_ptNew points which define two corners of the rectangle.

We first call the base class' OnLButtonUp method, which flushes the construction rectangle from the scene. We then check that the current point is not equal to the first point. (If it is, we exit.)

int HOpCreateRectangle::OnLButtonUp(HEventInfo &event)
{
    if(!m_bOpStarted) 
        return HOpConstructRectangle::OnLButtonDownAndMove(event);

    HOpConstructRectangle::OnLButtonUp(event);

    if (m_ptNew.x == m_ptFirst.x &&
        m_ptNew.y == m_ptFirst.y)
        return(HOP_CANCEL);

We can't directly use the 'first' and 'new' points to insert a rectangle into the model, because those points are for a screen-space 'construction' rectangle that essentially lies on top of the scene. More specifically, the points are in HOOPS/3dGS 'window space' whose extents are -1,-1 for the bottom left of the window, and 1,1 for the top right. We want to insert our rectangle into the 'world' coordinates system which is where our other model contents exist. Therefore, we take the First/New points and convert them from window to world space.

    HC_Open_Segment_By_Key (m_pView->GetSceneKey());
      HC_Compute_Coordinates(".", "local window", &m_ptFirst, "world", &m_ptFirst);
      HC_Compute_Coordinates(".", "local window", &m_ptNew, "world", &m_ptNew);
    HC_Close_Segment();

We then open up a new segment underneath the model-key segment which will hold the rectangles, and insert the new rectangle into it. We update the scene-graph, and reset the operator by setting m_bOpStarted to false and returning the HOperatorReturn type that indicates 'ready for processing'.

    HC_Open_Segment_By_Key(GetView()->GetModelKey());
      HC_Open_Segment("sample_rectangles"); 
        HUtility::InsertRectangle (".", m_ptFirst.x, m_ptFirst.y, m_ptNew.x, m_ptNew.y);
      HC_Close_Segment();
    HC_Close_Segment();

    m_pView->Update(); 
    m_bOpStarted = false;
    
    return(HOP_READY);  
}

Exercise: Enhance this previous code so that it places the new rectangles into a new 'layer' segment. You'll need to modify the code that opens up the rectangle segment so that it opens it by key and stores the key in the m_pLayers list associated with the HSoccerModel object. You'll also need to decide what order this rectangle segment should be in the layer list. It's probably easiest to precreate the 'rectangles' segment inside of HSoccerModel::Init, and then call Bring_To_Front on that segment to order it as you desire (we suggest you place it on top of all the other layers!)