5,276,406 members and growing! (15,548 online)
Email Password   helpLost your password?
Multimedia » OpenGL » General     Intermediate

A small VRML viewer using OpenGL and MFC

By Pierre Alliez

VC6, C++Windows, NT4, OpenGL, VS6, VS, Dev

Posted: 30 Nov 1999
Updated: 30 Nov 1999
Views: 206,212
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
120 votes for this Article.
Popularity: 10.02 Rating: 4.82 out of 5
0 votes, 0.0%
1
1 vote, 2.3%
2
3 votes, 6.8%
3
0 votes, 0.0%
4
40 votes, 90.9%
5
  • Download files - 669 Kb
  • FIG. 1. You can easily display a wrl-based terrain using OpenGL and MFC.
     

    This contribution is a small MFC sample to learn how to :

    • display a VRML file
    • use OpenGL display lists
    • superimpose wireframe on a flat or smoothly shaded mesh
    • smoothly subdivide a 3D triangular mesh (from Charles Loop)
    • implement mouse interaction (rotation and translation)
    • build a scene graph from a vrml 2.0 file (hand-made and not lex-based)

    DISPLAY LIST

    Using display lists is a nice way to accelerate your rendering application. A display list compiles a sequence of gl drawings using standard OpenGL calls, then can be recalled later using a simple list id number. The resulting list is thus resident in the main memory in a precompilated mode, the which greatly accelerates rendering loops. A good command sequence to build a display list may be :
    int list = ::glGenLists(1); // ask for a free id number 
    
    ::glNewList(list,GL_COMPILE_AND_EXECUTE); 
      ::glBegin(GL_TRIANGLES); 
      // std gl calls here... fill vertices, normals, colors 
    
      ::glEnd(); 
    ::glEndList();
    A good command sequence to use a display list may be :
    if(::glIsList(list) == GL_TRUE)
      ::glCallList(m_ListOpenGL);
    The sample builds a scene graph from a vrml 2.0 file (exported via 3D Studio Max only), then uses display lists. Each 3D mesh contains a list number, and use a glCallList command instead of standards glBegin(GL_TRIANGLES) commands when its list is built. A flag m_Modified permits to rebuild the list when the mesh is modified.
    //******************************************** 
    
    // The 3D mesh class definition 
    
    //******************************************** 
    
    class CMesh2d : public CObject3d 
    { 
    private : 
    
     // Std datas 
    
     CArray<CVertex3d> m_ArrayVertex; 
     CArray<CFace3d>   m_ArrayFace; 
    
     // OpenGL-specific 
    
     unsigned int m_ListOpenGL; 
     BOOL m_ListDone; 
     BOOL m_Modified; 
     .../... 
    
    public : 
     BOOL glDraw(); 
     .../... 
    } 
    
    //******************************************** 
    
    // Mesh drawing 
    
    //******************************************** 
    
    BOOL CMesh2d::glDraw() 
    { 
     // Build list at first 
    
     if(!m_ListDone || m_Modified) 
      glBuildList(); 
    
     // Is the list valid ? 
    
     if(::glIsList(m_ListOpenGL)==GL_TRUE) 
     { 
       ::glCallList(m_ListOpenGL); 
      return TRUE; 
     } 
     return FALSE; 
    }

    SUPERIMPOSING WIREFRAME

    Sometime you would like to view the wireframe superimposing the flat or smooth shaded mesh. A good way to do this is to use the glPolygonOffset command, which creates a z-buffer offset. The following code shows the RenderScene function of the document, if one resumes two rendering passes are necessary, the first render the mesh using lighted flat mode, the second cut off the light, set the line mode, set a z-buffer offset, then draw the mesh again.
    //*********************************************** 
    
    // RenderScene 
    
    //*********************************************** 
    
    void CMeshDoc::RenderScene() 
    { 
     // Main drawing 
    
     m_SceneGraph.glDraw(); 
    
     // Add wireframe (no light, and line mode) 
    
     if(m_AddWireframe) 
     { 
      // Set state 
    
      ::glDisable(GL_LIGHTING); 
      ::glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 
      ::glEnable(GL_POLYGON_OFFSET_LINE); 
      ::glPolygonOffset(m_PolygonOffset,-1.0f); 
    
      // Draw again... 
    
      m_SceneGraph.glDraw(TYPE_MESh2D); 
    
      // Restore light and mode 
    
      ::glDisable(GL_POLYGON_OFFSET_LINE); 
      ::glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); 
      ::glEnable(GL_LIGHTING); 
     } 
    
     ::glFlush(); 
    }

    FIG. 2. You can superimpose a wireframe on a flat-rendered mesh, using a second rendering loop with the line option.

    SMOOTH SUBDIVISION

    From a given 3D mesh, how can we improve the geometric appearence on smooth models ? The Charles Loop smooth subdivision comes here to help us. Each triangle is divided in four triangles (see figure 3), and a filtering function permits the mesh to be smoothed. The command is easy-to-use in the document, and I let you discover the details in the mesh's code.
    "wrl_viewer/wrl_viewer3.jpg" height=191 width=600>
    FIG.3. The one-to-four triangle subdivision scheme used by method.
    //*********************************************** 
    
    // Smooth subdivision 
    
    //*********************************************** 
    
    void CMeshDoc::OnMeshLoop() 
    { 
     BeginWaitCursor(); 
     int NbObject = m_SceneGraph.NbObject(); 
     for(int i=0;i<NbObject;i++) 
     { 
       CObject3d *pObject3d = m_SceneGraph[i]; 
       if(pObject3d->GetType() == TYPE_MESh2D) 
      { 
       CMesh2d *pMesh  = (CMesh2d *)pObject3d; 
       pMesh->SubdivisionLoop(); 
      } 
     } 
     UpdateAllViews(NULL); 
     EndWaitCursor(); 
    }

    FIG.4. Two successives iterations of one-to-four subdivision scheme.

    FIG.5. See the visual enhancement obtained by a smooth subdivsion scheme..
     

    MOUSE INTERACTION

    A few variables and commands inserted in the view permit mouse interaction.
    //*********************************************** 
    
    // Left button -> x/y translation 
    
    //*********************************************** 
    
    void CMeshView::OnLButtonDown(UINT nFlags, CPoint point) 
    { 
     m_LeftButtonDown = TRUE; 
     m_LeftDownPos = point; 
     SetCapture(); 
     CView::OnLButtonDown(nFlags, point); 
    } 
    void CMeshView::OnLButtonUp(UINT nFlags, CPoint point) 
    { 
     m_RightButtonDown = FALSE; 
     m_LeftButtonDown = FALSE; 
     ReleaseCapture(); 
     CView::OnLButtonUp(nFlags, point); 
    } 
    
    //*********************************************** 
    
    // Right button : z translation 
    
    //*********************************************** 
    
    void CMeshView::OnRButtonDown(UINT nFlags, CPoint point) 
    { 
     m_RightButtonDown = TRUE; 
     m_RightDownPos = point; 
     SetCapture(); 
     CView::OnRButtonDown(nFlags, point); 
    } 
    void CMeshView::OnRButtonUp(UINT nFlags, CPoint point) 
    { 
     m_RightButtonDown = FALSE; 
     m_LeftButtonDown = FALSE; 
     ReleaseCapture(); 
     CView::OnRButtonUp(nFlags, point); 
    } 
    
    //*********************************************** 
    
    // Mouse move 
    
    // Both : rotation 
    
    // Left : x / y translation 
    
    // Right : z translation 
    
    //*********************************************** 
    
    void CMeshView::OnMouseMove(UINT nFlags, CPoint point) 
    { 
     // Both : rotation 
    
     if(m_LeftButtonDown && m_RightButtonDown) 
     { 
      if(m_xyRotation) 
      { 
       m_yRotation -= (float)(m_LeftDownPos.x - point.x) * m_SpeedRotation; 
       m_xRotation -= (float)(m_LeftDownPos.y - point.y) * m_SpeedRotation; 
      } 
      else 
      { 
       m_zRotation -= (float)(m_LeftDownPos.x - point.x) * m_SpeedRotation; 
       m_xRotation -= (float)(m_LeftDownPos.y - point.y) * m_SpeedRotation; 
      } 
      m_LeftDownPos = point; 
      m_RightDownPos = point; 
      InvalidateRect(NULL,FALSE); 
     } 
    
     else 
    
     // Left : x / y translation 
    
     if(m_LeftButtonDown) 
     { 
      m_xTranslation -= (float)(m_LeftDownPos.x - point.x) * m_SpeedTranslation; 
      m_yTranslation += (float)(m_LeftDownPos.y - point.y) * m_SpeedTranslation; 
      m_LeftDownPos = point; 
      InvalidateRect(NULL,FALSE); 
     } 
    
     else 
    
     // Right : z translation 
    
     if(m_RightButtonDown) 
     { 
      m_zTranslation += (float)(m_RightDownPos.y - point.y) * m_SpeedTranslation; 
      m_RightDownPos = point; 
      InvalidateRect(NULL,FALSE); 
     } 
    
     CView::OnMouseMove(nFlags, point); 
    }

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    About the Author

    Pierre Alliez



    Location: France France

    Other popular OpenGL articles:

    • 3D Graph ActiveX Control
      An ATL/STL ActiveX control based on OpenGL library for 3D data visualization
    • Achieving PostScript and Wmf outputs for OpenGL
      This article explains how to generate resolution independent versions of 3D meshes rendered by OpenGL/MFC programs, i.e. how to export the rendering results to vectorial formats such as encapsulated postscript (EPS) and Windows enhanced metafile (EMF) formats. The main goal consists of being able to
    • OpenGL Win32 AppWizard
      This Custom AppWizard for VC++ 6.0 or VC++.NET creates an OpenGL enabled Win32 application suitable for demos and simple games.
    • Interactive water effect
      A short OpenGL demo showing multitexturing effects and clever math optimizations.
    • Circuit Engine
      A System for Simulation and Analysis of Logic Circuits

    Article Top
    Sign Up to vote for this article
    You must Sign In to use this message board.
    FAQ FAQ Noise ToleranceSearch Search Messages 
     Layout  Per page   
     Msgs 1 to 25 of 41 (Total in Forum: 41) (Refresh)FirstPrevNext
    Subject  Author Date 
    Generalmouse interaction.. how to make each obkect "clickable" (or selectable)?membermimosa10:20 24 Jun '08  
    Generalunable to render other .wrl filesmemberRajani Kamath0:13 29 Apr '08  
    GeneralWont compile in VisStudio 2005memberMember 460001415:31 13 Mar '08  
    AnswerRe: Wont compile in VisStudio 2005memberCDO23:35 18 Mar '08  
    Generalupdating surfacememberOlaf Petersen4:43 25 Oct '07  
    Questionhow's that supposed to work?memberQuarkette9:19 20 Aug '07  
    AnswerRe: how's that supposed to work?memberDucThanh21:49 8 Oct '07  
    GeneralChange Alpha ValuememberDigvijay Gupta0:16 26 May '07  
    Generalthank you!memberkru_pl2:13 9 Jan '07  
    Generalcan not drawmemberdapson.dps23:27 7 Sep '06  
    GeneralSome suggestmemberPapyna21:09 11 Mar '06  
    Generalnothing happens when i upload .wrl in openGLmembervijethas14:09 20 Nov '05  
    GeneralThanksmembercoder@coder.gr10:42 6 Apr '05  
    GeneralSpecifying a texture image onto the mesh surface?memberTBiker12:33 24 Feb '05  
    GeneralwowmemberYves18:38 5 Oct '04  
    GeneralHow to save GDI+ bmp file as PDF File Format.susspubba21:03 13 Jul '04  
    GeneralRe: How to save GDI+ bmp file as PDF File Format.memberazonenberg5:09 4 Mar '08  
    Generalhow to display following files using opengl and mfc?memberbuaa200316:39 10 May '04  
    GeneralHow to run 2 different(or same) OpenGL objects in one DialogBox?memberwerter122:09 3 May '04  
    Generalproblem of runing the programmemberfreeman1818:22 25 Mar '04  
    GeneralReverse sub-division?sussGernotFrisch11:01 4 Nov '03  
    General4x4 Matrixmembermacmac3811:13 8 Oct '03  
    GeneralRe: 4x4 Matrixmemberpxp1:28 13 Feb '05  
    GeneralMemory allocation errormemberFrontman6:44 2 Oct '03  
    GeneralOpengl Color and selectmembermcalves@coc.ufrj.br7:07 7 Apr '03  

    General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    PermaLink | Privacy | Terms of Use
    Last Updated: 30 Nov 1999
    Editor: Chris Maunder
    Copyright 1999 by Pierre Alliez
    Everything else Copyright © CodeProject, 1999-2008
    Web18 | Advertise on the Code Project