This step reviews the basics of creating 3D geometry.
The HOOPS/MVO Toolkit includes several basic geometry-creation operators. Their naming convention is HOpCreate<geometry_type>, and they generally behave by creating temporary 'construction' geometry during the mouse down/drag input, and then inserting the resulting geometry into the scene-graph during the mouse-up input. The GUI of any AppWizard-generated application provides access to a few of them by default, including HOpCreateSphere, which is need by our 'Spheres' application. In this step, we'll take a closer look at exactly what is happening in the HOpCreateSphere source, and do a little bit of customization.
You may recall that the interface for an operator is quite telling, since it indicates which mouse/keyboard methods the operator is going to handle. Taking, a look at HOpCreateSphere.h, we see that it implements the mouse-down, mouse-down-and-motion, and mouse-up methods to provide a fairly straightforward way of creating a sphere (just click and drag):
{public:....};
This method sets the member variable indicating that the operator has started, records the first mouse location, and returns a value indicating that the this method succeeded. When creating 3d geometry, we generally want to obtain the geometry definition points in 'viewpoint' space as that is independent of how the camera may currently be oriented. Then, before inserting the final geometry into the scene-graph, we convert from 'viewpoint' space to 'object' space. For more information about these coordinate systems and how they relate to one another, refer to the Section 3 of the HOOPS/3dGS Programming Guide.
int HOpCreateSphere::OnLButtonDown(HEventInfo &event){if (!m_bOpStarted)m_bOpStarted = true;m_ptFirst = event.GetMouseViewpointPos();m_ptNew = m_ptFirst;return (HOP_OK);}
This method is a bit more involved. First, it obtains the new mouse location and stores it in the m_ptNew member variable:
int HOpCreateSphere::OnLButtonDownAndMove(HEventInfo &event){// Mouse is down and has movedHVector radius_vector;m_ptNew = event.GetMouseViewpointPos();
Next, it calculates the length of the radius vector:
radius_vector.Set (m_ptNew.x - m_ptFirst.x,m_ptNew.y - m_ptFirst.y,m_ptNew.z - m_ptFirst.z);m_radius = (float)HC_Compute_Vector_Length(&radius_vector);
If the radius is greater than zero, it opens the construction key (which holds 'temporary', overlayed or XOR geometry), flushes it to remove any wireframe sphere geometry that might have been in there from the previous update, creates a wireframe sphere, and generates some construction lines for visual feedback:
if (m_radius > 0.0){HC_Open_Segment_By_Key(m_pView->GetConstructionKey());HC_Flush_Geometry(".");CreateWireframeSphere (m_ptFirst, m_radius);GenerateLinesThroughSphere();}
Finally, it calls Update() method to redraw the scene, and returns a value indicating that this method succeeded:
m_pView->Update();return (HOP_OK);}
This method finalizes the sphere and inserts geometry into the HOOPS/MVO 'model' object. It begins by storing the latest (and final) point in m_ptNew, flushes the construction segment (since we're all done with viewing the temporary construction geometry), and does makes sure the first point doesn't equal the last point:
{if(!m_bOpStarted)return HBaseOperator::OnLButtonDownAndMove(event);m_bOpStarted = false;m_ptNew = event.GetMouseViewpointPos();HC_Open_Segment_By_Key(m_pView->GetConstructionKey());if (m_ptNew.x == m_ptFirst.x && m_ptNew.y == m_ptFirst.y && m_ptNew.z == m_ptFirst.z){m_bOpStarted = false;return(HOP_CANCEL);}
The next code block sets the z-component of the first/last point to be equal to the length of the view vector, and stores away the normalized camera up/view vectors:
HC_Open_Segment_By_Key(m_pView->GetSceneKey());HVector pos, tar, up;HC_Show_Net_Camera_Position(&pos.x, &pos.y, &pos.z);HC_Show_Net_Camera_Target(&tar.x, &tar.y, &tar.z);HPoint view;view.Set(pos.x-tar.x, pos.y-tar.y, pos.z-tar.z);float length = (float)HC_Compute_Vector_Length(&view);m_ptFirst.z = length;m_ptNew.z = length;HC_Compute_Normalized_Vector(&view, &view);HC_Show_Net_Camera_Up_Vector(&up.x, &up.y, &up.z);HC_Compute_Normalized_Vector(&up, &up);
The next block does some work to make sure that the object is drawn in the view plane correctly. It converts the first/last points from viewpoint coordinates (the coordinate system they were obtained in), to object space, and then calculates/computes the length of the sphere's radius.
HC_KEY keys[2];keys[0] = m_pView->GetModel()->GetModelKey();keys[1] = m_pView->GetSceneKey();HVector radius_vector;radius_vector.Set (m_ptNew.x - m_ptFirst.x,m_ptNew.y - m_ptFirst.y,m_ptNew.z - m_ptFirst.z);m_radius = (float)HC_Compute_Vector_Length(&radius_vector);
It then calls the HBaseView::CreateSphere method (which results in a HOOPS/3dGS shell getting inserted into the scene-graph), calls Update() to redraw the scene, and returns a value indicating that this operator is ready for a fresh round of activity:
m_pView->CreateSphere("spheres", m_ptFirst, m_radius, 20, view, up, true);m_pView->SetGeometryChanged();m_pView->Update();return(HOP_READY);}
Let's make a modification to create the sphere with a variable tesselation value. When the operator calls HBaseView::CreateSphere, it currently passes in a hardcoded value of 20 for the 'num_faces' tesselation value. We'll have to overload the OnLButtonUp method so that it calls CreateSphere with a variable that we set in the operator's constructor. Our overloaded method will need to copy the existing OnLButtonUp methods code, and just pass in this new variable.
Our custom interface adds a new member called n_NumSides, and also overloads the constructor (giving it an additional 'NumSides' argument with a default value of 20), Clone, GetName, and OnLButtonUp methods:
Our new constructor accepts the NumSides argument, and then calls the base class's constructor with the standard initializer list:
HMyCreateSphere::HMyCreateSphere(HBaseView* view, int DoRepeat, int DoCapture, int NumSides) : HOpCreateSphere(view, DoRepeat, DoCapture){m_NumSides = NumSides;}
This contains the same source as the base class, except it calls the CreateSphere method with the m_NumSides argument:
{....}
Now we can use our custom sphere-creation operator and pass in a specific tesselation value. The operator is created in CSpheresView::OnCreateSphere, so we'll modify the code to use our operator and creates sphere's with 'numsides' equal to 50:
Note how the sphere is more finely tesselated. This is evident when looking at the silhouette of the sphere (when in shaded mode), or switching to the wireframe render mode.