Wednesday, March 5, 2014

How to Build an simple Opencascade Program using Visual C++

What is Opencascade?

Opencascade is an open source software development platform which provides easy interface to build CAD, CAM Software. It contains the optimized data structures and algorithms to perform the required geometric operations efficiently. You might wonder what I meant by geometric operations?
For example in CAD software you might want to find the intersection points of two 3D curves or you need to cut some shape off from a solid shape etc.
For that you don't need to write code from the scratch. You can use the generic geometric operations provided in the Opencascade library.
For further details you can visit the Opencascade official website: http://www.opencascade.org/

How to Install Opencascade in your computer?

I hope now you have some picture about the Opencascade. Best way to learn Opencascade is to try it out and play with it. In Order to work with Opencascade first you have to install Opencascde in your computer. You can download the latest revision of the Opencascade from http://www.opencascade.org/getocc/download/occarchives/. (Note: At the time I’m writing this blog Opencascade 6.7.0 is released. But in this tutorial all will use Opencascade 6.6.0. There is nothing to worry, Opencascade 6.6.0 and the Opencascade 6.7.0 are pretty much the same.)
Go to the link specified above and select Opencascade 6.6.0 version to download. Select the Opencascade 6.6.0.exe to start downloading Opencascade 6.6.0.
After downloading Opencascade 6.6.0.exe run the setup file with administrative privilages in order to install it correctly.
After you have successfully installed the Opencascade you can go to the installation location and see the installed files and folders.




How to Configure visual studio to use Opencascade ?

Configuring Opencascade in Visual Studio is extremely easy. Before configuring Opencascade in Visual Studio we need to configure some environment variables in the windows.
For that Right Click of My Computer =>Properties => Advanced System Settings=> Environment Variables. Then Select Environment variable named “Path” and add Openascade bin folder path to it. In Opencascde installation location you have a folder named “3rdParty”. In this folder it contains all the 3rd party dll files needed by Opencascade. In 3rdparty folder go to each and every folder and find the path to bin and add that path to the environment variable “Path”. Ok. Now we have finished configuring environment variables in windows for Opencascade.
Now it is time to configure Visual Studio to use Opencascade. In Visual Studio Will need to Configure the following project properties for any created project in order to use Opencascade in that project. To change these properties, Right Click on your project => Properties

·         Include Files Path    (C++ =>General => Additional Include Directories) (Include folder path of the Opencascde)
·         
      Preprocessor Definitions (C++ => Preprocessor)  (Add WNT, WNT_WINDOW)
·        
      Library Files Path (Linker => General => Additional Library Directories) (Add Lib folder path of the Opencascade)
·        
      Additional Dependencies (Linker => Input => Additional Dependencies) ( Add the Following Lib Names
TKOpenGl.lib;TKernel.lib;TKGeomBase.lib;TKTopAlgo.lib;TKOffset.lib;TKBool.lib;TKPrim.lib;TKFillet.lib;TKMath.lib;TKService.lib;TKV3d.lib;TKBrep.lib;TKIGES.lib;PTKernel.lib;TKSTL.lib;TKVRML.lib;TKSTEP.lib;TKShapeSchema.lib;TKG3d.lib;TKG2d.lib;TKXSBase.lib;TKPShape.lib;TKShHealing.lib;TKBO.lib


Congratulations!!! Now you have configured Visual Studio to use Opencascade library.

Our First Application

Ok. Let’s Start Coding. What would be our first application? I have decided to create a simple MFC application. I use Opencascade to draw some 3d object and rotate that object according to mouse movement.
If you don’t know anything about MFC applications don’t worry. I will explain briefly about the MFC application. If you are familiar with the MFC applications skip reading the below passage on MFC. People who are new to MFC I highly recommend reading the below passage or do your own research on MFC and get little bit familiar with the MFC applications in order to continue this tutorial.
What is MFC? MFC stands for Microsoft Foundation Class. In a MFC application there are 3 objects that are essential. MFC application can’t survive without those 3 objects. Those are

  • Application Class
  • Document Class
  • Viewer Class
Application Class: This is the class with contains all the initializations of the application. Windows start to run our application from this class. There can be only one application class for an MFC application.
Document Class: This Class contains the data which we need to show and interact with the user. Note that this class contains only the data. There is no way to show these data without using the viewer class.
Viewer Class: To Show a data in the document Class we need the Viewer Class. Without the Viewer Class we can’t show the data in the document.
For Application there can be multiple instances of documents. For each document there is a bound viewer object.
For More information http://msdn.microsoft.com/en-us/library/9es9d1k4.aspx

Ok Back to Opencascade,
I hope now you have created the MFC Application. In stdafx.h file copy the following includes. These are the mainly used includes in Opencascade. (You can include the specific header file when you need that file. But following approach is easy for beginners. So they can directly write code without wondering what files to include.)
 #include <BRepTools.hxx>  
 #include <Standard_DefineHandle.hxx>  
 #include <DsgPrs_LengthPresentation.hxx>  
 #include <GCPnts_TangentialDeflection.hxx>  
 #include <Geom_Axis2Placement.hxx>  
 #include <Geom_CartesianPoint.hxx>  
 #include <Geom_Line.hxx>  
 #include <Geom_Surface.hxx>  
 #include <BRepAdaptor_Surface.hxx>  
 #include <GeomAbs_CurveType.hxx>  
 #include <GeomAdaptor_Curve.hxx>  
 #include <GeomTools_Curve2dSet.hxx>  
 #include <gp_Vec.hxx>  
 #include <Graphic3d_NameOfMaterial.hxx>  
 #include <MMgt_TShared.hxx>  
 #include <OSD_Environment.hxx>  
 #include <Precision.hxx>  
 #include <Prs3d_IsoAspect.hxx>  
 #include <Prs3d_LineAspect.hxx>  
 #include <Prs3d_Projector.hxx>  
 #include <Prs3d_Text.hxx>  
 #include <Quantity_Factor.hxx>  
 #include <Quantity_Length.hxx>  
 #include <Quantity_NameOfColor.hxx>  
 #include <Quantity_PhysicalQuantity.hxx>  
 #include <Quantity_PlaneAngle.hxx>  
 #include <Quantity_TypeOfColor.hxx>  
 #include <SelectBasics_BasicTool.hxx>  
 #include <SelectBasics_ListOfBox2d.hxx>  
 #include <SelectMgr_EntityOwner.hxx>  
 #include <SelectMgr_SelectableObject.hxx>  
 #include <SelectMgr_Selection.hxx>  
 #include <SelectMgr_SelectionManager.hxx>  
 #include <SelectMgr_ListOfFilter.hxx>  
 #include <Handle_SelectMgr_Filter.hxx>  
 #include <SelectMgr_Filter.hxx>  
 #include <StdSelect_EdgeFilter.hxx>  
 #include <StdSelect_ShapeTypeFilter.hxx>  
 #include <ShapeSchema.hxx>  
 #include <Standard_Boolean.hxx>  
 #include <Standard_CString.hxx>  
 #include <Standard_ErrorHandler.hxx>  
 #include <Standard_Integer.hxx>  
 #include <Standard_IStream.hxx>  
 #include <Standard_Macro.hxx>  
 #include <Standard_NotImplemented.hxx>  
 #include <Standard_OStream.hxx>  
 #include <Standard_Real.hxx>  
 #include <StdPrs_Curve.hxx>  
 #include <StdPrs_Point.hxx>  
 #include <StdPrs_PoleCurve.hxx>  
 #include <TCollection_AsciiString.hxx>  
 #include <TColgp_Array1OfPnt2d.hxx>  
 #include <TColgp_HArray1OfPnt2d.hxx>  
 #include <TCollection_AsciiString.hxx>  
 #include <TColStd_HSequenceOfTransient.hxx>  
 #include <TColStd_MapIteratorOfMapOfTransient.hxx>  
 #include <TColStd_MapOfTransient.hxx>  
 #include <TopExp_Explorer.hxx>  
 #include <TopoDS.hxx>  
 #include <TopoDS_Compound.hxx>  
 #include <TopoDS_Shape.hxx>  
 #include <TopoDS_Solid.hxx>  
 #include <TopoDS_Vertex.hxx>  
 #include <TopExp.hxx>  
 #include <TopTools_HSequenceOfShape.hxx>  
 #include <UnitsAPI.hxx>  
 #include <V3d_View.hxx>  
 #include <V3d_Viewer.hxx>  
 #include <WNT_Window.hxx>  
 #include <Handle_AIS_Drawer.hxx>  
 #include <AIS_Drawer.hxx>  
 #include <Prs3d_PointAspect.hxx>  
 #include <AIS_Point.hxx>  
 #include <BRep_Tool.hxx>  
 #include <BRepAlgoAPI_Fuse.hxx>  
 #include <BRepBuilderAPI_MakeEdge.hxx>  
 #include <BRepBuilderAPI_MakeFace.hxx>  
 #include <BRepBuilderAPI_MakeWire.hxx>  
 #include <BRepBuilderAPI_MakeVertex.hxx>  
 #include <BRepBuilderAPI_Transform.hxx>  
 #include <BRepPrimAPI_MakeCone.hxx>  
 #include <BRepPrimAPI_MakeRevol.hxx>  
 #include <BRepFilletAPI_MakeFillet.hxx>  
 #include <BRepBuilderAPI_Copy.hxx>  
 #include <BRepBuilderAPI_MakePolygon.hxx>  
 #include <BRepLib.hxx>  
 #include <BRepOffsetAPI_MakeThickSolid.hxx>  
 #include <BRepOffsetAPI_ThruSections.hxx>  
 #include <BRepPrimAPI_MakeCylinder.hxx>  
 #include <BRepPrimAPI_MakePrism.hxx>  
 #include <BRepPrimAPI_MakeTorus.hxx>  
 #include <BRepAlgoAPI_Section.hxx>  
 #include <BRepPrimAPI_MakeSphere.hxx>  
 #include <BRepFeat_SplitShape.hxx>  
 #include <TColgp_HArray1OfPnt.hxx>  
 #include <GeomAPI_Interpolate.hxx>  
 #include <GC_MakeArcOfCircle.hxx>  
 #include <GC_MakeSegment.hxx>  
 #include <GC_MakeCircle.hxx>  
 #include <GCE2d_MakeSegment.hxx>  
 #include <gp.hxx>  
 #include <gp_Ax1.hxx>  
 #include <gp_Ax2.hxx>  
 #include <gp_Ax2d.hxx>  
 #include <gp_Dir.hxx>  
 #include <gp_Dir2d.hxx>  
 #include <gp_Pnt.hxx>  
 #include <gp_Pnt2d.hxx>  
 #include <gp_Trsf.hxx>  
 #include <gp_Vec.hxx>  
 #include <Geom_CylindricalSurface.hxx>  
 #include <Geom_Plane.hxx>  
 #include <Geom_Surface.hxx>  
 #include <Geom_TrimmedCurve.hxx>  
 #include <Geom2d_Ellipse.hxx>  
 #include <Geom2d_TrimmedCurve.hxx>  
 #include <TopExp_Explorer.hxx>  
 #include <TopoDS.hxx>  
 #include <TopoDS_Edge.hxx>  
 #include <TopoDS_Face.hxx>  
 #include <TopoDS_Wire.hxx>  
 #include <TopoDS_Shape.hxx>  
 #include <TopoDS_Compound.hxx>  
 #include <GCPnts_AbscissaPoint.hxx>  
 #include <BRepAdaptor_Curve.hxx>  
 #include <GeomLib.hxx>  
 #include <GeomConvert_CompCurveToBSplineCurve.hxx>  
 #include <TopTools_ListOfShape.hxx>  
 #include <TopTools_ListIteratorOfListOfShape.hxx>  
 #include <TopTools_DataMapOfShapeInteger.hxx>  
 #include <TopTools_DataMapOfShapeReal.hxx>  
 #include <TopTools_IndexedDataMapOfShapeAddress.hxx>  
 #include <V3d_PositionalLight.hxx>  
 #include <V3d_DirectionalLight.hxx>  
 #include <V3d_AmbientLight.hxx>  
 #include <IGESControl_Controller.hxx>  
 #include <IGESControl_Writer.hxx>  
 #include <Interface_Static.hxx>  
 #include <OpenGl_GraphicDriver.hxx>  
 #include <Graphic3d_GraphicDriver.hxx>  

Opencascade is a CAD geometry library. To show 3d objects on the screen Opencascade needs some link to the computers graphic card. So the First thing that we have to is the making a link between the Opencascade and the computer graphic card.

I use the Application class to keep that link. In application Class header file declare the variable

 Handle(Graphic3d_GraphicDriver) m_GraphicDriver;                 // To Keep the Initialized graphic driver...  

What is the best time to create the link between Opencascade and the Graphic driver. I think it is good and recommended if we can create that link at the start of the application. So I initialize the header file declared variable in the Application Constructor Class.

      
 try{ 
        m_GraphicDriver=new OpenGl_GraphicDriver("TKOpenGl");  
     }catch(Standard_Failure){  
          AfxMessageBox("_T(Error Ocured in Initializing the Opencascade graphic variable.)");     
     }  

So now we have created the link between Opencascade and the graphic card. Add a getter method to App class header file in order to return this variable.

 Handle(Graphic3d_GraphicDriver) GetGraphicDriver(){return m_GraphicDriver;}  

Ok. Now let’s move on to the Document Class. In the document class header file we need to declare two variables. Those are
 Handle(AIS_InteractiveContext) myAISContext;  
 Handle(V3d_Viewer) myViewer;  

AIS_InteractiveContext is the one which manages the user interaction with the displayed objects. Those interactions may be rotating, panning, zooming, selecting shapes etc.
V3d_Viewer Contains all the required variables about the Openacsacade viewer.  You really don’t want to worry about that now.  We need the Viewer to create a Opencascade View. (Read Carefully.)
To Initialize these variables we add code to the Document Class Constructor.
 Handle(Graphic3d_GraphicDriver) theGraphicDriver=((CCADViewerApp*)AfxGetApp())->GetGraphicDriver();  
     theGraphicDriver->Begin(new Aspect_DisplayConnection());  
     TCollection_ExtendedString aNameOfViewer("Visu3d");  
     //Initializing V3d_Viewer  
     myViewer = new V3d_Viewer (theGraphicDriver, aNameOfViewer.ToExtString());  
     myViewer->Init();  
     myViewer->SetLightOn();  
     myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);  
     // create a new window or a wrapper over the existing window,  
     myAISContext=new AIS_InteractiveContext(myViewer);  
 myAISContext->SetDisplayMode(AIS_Shaded);  
     myAISContext->SetAutomaticHilight(Standard_False);  

Ok Now Let’s Move on to the Viewer Class in MFC.  Viewer Class Contains the Opencascade View Variable. (View at particular point of the Opencascade viewer.)
 Handle(V3d_View) myView;  

Viewer class we need to override method On InitialUpdate. This is run at the first update of the window viewer. To override the method add following to the header file.
 virtual void OnInitialUpdate();  

Add this Overridden code to the Viewer OnInitialUpdate Method.
 void CCADViewerView::OnInitialUpdate(){  
  myView=GetDocument()->GetViewer()->CreateView();  
  myView->SetShadingModel(V3d_GOURAUD);  
  Handle(Graphic3d_GraphicDriver) theGraphicDriver = ((CCADViewerApp*)AfxGetApp())->GetGraphicDriver();  
  Aspect_Handle aWindowHandle = (Aspect_Handle)GetSafeHwnd();  
  Handle(WNT_Window) aWntWindow=new WNT_Window(GetSafeHwnd());  
  myView->SetWindow(aWntWindow);  
  if(!aWntWindow->IsMapped()){  
  aWntWindow->Map();  
  }  
  Standard_Integer w=100;  
  Standard_Integer h=100;  
  aWntWindow->Size(w,h);  
  ::PostMessage(GetSafeHwnd(),WM_SIZE,SIZE_RESTORED,w+h*65536);  
  myView->FitAll();  
 myView->ZBufferTriedronSetup(Quantity_NOC_RED,Quantity_NOC_GREEN,Quantity_NOC_BLUE1,0.8,0.05,12);  
   myView->TriedronDisplay(Aspect_TOTP_LEFT_LOWER,Quantity_NOC_WHITE,0.2,V3d_ZBUFFER);  
 }  

On Initial Update of the MFC Viewer Class we initially update the Opencascade View , Setting up the window sizes of the view etc.

In Addition to that we need to draw the 3D axis System on the screen We Should add the Following Code to the bottom of the On Initial Update Method.

 myView->ZBufferTriedronSetup(Quantity_NOC_RED,Quantity_NOC_GREEN,Quantity_NOC_BLUE1,0.8,0.05,12);  
 myView->TriedronDisplay(Aspect_TOTP_LEFT_LOWER,Quantity_NOC_WHITE,0.2,V3d_ZBUFFER);  

Now it time to update the OnDraw  method in MFC Viewer Class. This method is called by MFC automatically when window viewer needs to update. (Like Resizing the window, Moving the window etc). When the window viewer needs to be updated the Opencascade View also needs to be updated.
So Add the Following code to MFC Viewer Class OnDraw method.
 myView->MustBeResized();  
 myView->Update();  

Ok. Congratulations!!! Now you have completed building your first app using Opencascade. If it compiled without an error then when you run your application you should see a view with the 3D Axis on the left bottom of the Screen.


Creating a Geometric Object using Opencascade?

Ok. Now let’s draw some object on the Opencascade viewer. Opencascade is a very rich robust geometric library. You can perform very complicated geometric operations using Opencascade.
For now we will just create a Sphere. (I will write my next post on how to create complicated shapes using Opencascade.)
Let’s add a function to draw a sphere in the Document Class.

 void CCADViewerDoc::DrawSphere(double Radius){  
     BRepPrimAPI_MakeSphere mkSphere(Radius);  
     TopoDS_Shape Sphere=mkSphere.Shape();  
     myAISContext->Display(new AIS_Shape(Sphere));  // Draw the Sphere on the Screen  
     myViewer->ActiveView()->FitAll();           // Focus to the View to the Drawn Shape.  
 }  

Now we needs to call this function in order to draw the shape. For now I will call this function in OnDraw method.



How to interact with the Opencascade viewer?

We can interact with the drawn object very easily. Let’s add functionality to rotate any object drawn in the viewer using move movement plus left mouse button down. To do that you need to add mouse movement hander to our application. We will add mouse movement handler function to the Viewer Class of Our application. (you can add mouse event automatically by visual studio.)

 void CCADViewerView::OnMouseMove(UINT nFlags, CPoint point)  
 {  
     // TODO: Add your message handler code here and/or call default  
     CView::OnMouseMove(nFlags, point);  
     if(nFlags && MK_LBUTTON){  
            myView->Rotate(point.x,point.y);  
            myView->Rotation(point.x,point.y);  
     }  
 }  


So we have reached to the end of this tutorial. I hope you took some knowledge from this. If You have any questions regarding this tutorial or Opencascade please comment below or mail me (milindasf@gmail.com) and I will try my best to answer those questions.

6 comments:

  1. #include AIS_InteractiveContext.hxx
    #include AIS_Shape.hxx
    I thinnk you missed these includes
    and getViewer function also not there
    and last statement in draw returns a error? any idea..

    ReplyDelete
  2. PTKernel.lib; is not a valid name. I think it was a typo
    Valid name:
    TKernel.lib;

    ReplyDelete
  3. can you please share the project source code

    ReplyDelete
  4. post the source code on github

    ReplyDelete
  5. the on mouse move makes the 3d model move way too fast. it is extremely sensitive to movement. how can i slow it down/reduce the sensitivity?

    ReplyDelete