CS405 Lab 1: Introduction to OpenGL Programming
                                for Windows


Highlights of this lab:

    This lab is an introduction to OpenGL programming for Windows

Assignment:

    After the lab lecture, you have approximately two weeks of time to:

Online glut Manual

A. OpenGL Architecture on Windows 98 and NT

                    APPLICATION
                                                         |
                         V
           ---------------------------------
           |             |                 |
           V             V                 V
     OpenGL DLL <----- GLU32 DLL         GDI DLL
     |        |     <------------------- [wgl]
     V        V                             |
  OpenGL     Generic                        |
Installable  OpenGL                         |
  Client     Driver                         |
  Driver       |                            |
    |          |                            |
    V          V                            V
-------------------------------------------------
|                 DISPLAY DRIVER                |
| [OpenGL Driver]                 [GDI Driver]  |
-------------------------------------------------
 


B. Overview of Visual C++ Environment

Architecture of the Model-View-Controller

    This is the basic concept in object-oriented programming.

    Microsoft Foundation Classes (MFC) is an example of this architecture. MFC programs consist of:


How does an MFC Application Program Work?

       After a MFC application is started, it enters its main loop, called Windows Message Loop. This main loop will wait for a message. When a message arrives, appropriate member functions in appropriate classes will be called to perform computation. This is briefly illustrated by the following diagram:

                               --------------------
                               | Get DC           |
                  WM_CREATE    | Set Pixel Format |
                 ------------> | Create RC from DC|
                 |             | Make RC Current  |
                 |             | using DC         |
                 |             --------------------
                 |
    -----------  |WM_PAINT     ---------------------
    | Windows |  |-----------> | Make OpenGL Calls |
    | Message |->|             --------------------
    | Loop    |  |
    -----------  |             ----------------------
                 |WM_DESTROY   | Make RC Noncurrent |
                 |-----------> | Delete RC          |
                               | Release DC         |
                               ----------------------

    For example, when an application starts running, a WM_CREATE message will be automatically generated. When Windows Message Loop receives this message, it will call the handling functions associated with message, names Get Dc, etc.

    A message is usually triggered by a window event. For example, when you use mouse to drag the window to a different location, a WM_PAINT message will be generated by this event, and Window Message Loop receives it and call the handling function to redraw the scene.

    User's responsibility is to develop these handling functions corresponding concerned messages.

C. Building the OpenGL View Class Framework

1. Start Visual C++ Environment

    You will see that the Visual C++ window has three main regions:

2. Create a New Project

    The following show you how to create a framework for the OpenGL viewing class. You will need to perform the following steps.

At this point, we have created a framework for the OpenGL program we are to develop. You may look at the Workspace and there should be three tabs at the bottom: Cclick on the "Class" tab, and the class hierarchy will be displayed. There should be four classes automatically created for the OpenGL project. One of them, called "COpenGLView", will be particularly interesting to you because the main task of this lab is to add functions into this class.

At this point, you might want to compile the application (which of course dose nothing yet) just to make sure there are no errors in your building process.

3. Compile Your Application

    It will take a little while to compile and link the program. You may look at the output from the compiling process being displayed in the Output region at the bottom. If compiling is successful, 0 errors will be displayed at the end of compiling.

4. Run Your Application

    It is quite exciting to run this little window program if it is your first Windows program.

    A new window should be displayed in a few seconds. There are three buttons on the Menu bar: "File", "Edit" and "Help". They are the minimum things automatically created by your VC++ system. Inside of the window is totally blank. This is not surprising to you because we haven’t written any functions yet.

5. Add Member Functions to the COpenGLView Class

    The build process creates a minimum number of member functions for each class being generated. We may take a look at what are there by opening up the ClassWizard:

    There are a number of fields in the ClassWizard. To examine COpenGLView class:     Now, look at the "Member Functions" window, you should see two member functions already  there, one is OnDraw() and the other is PreCreateWindow(). We need to add functions for the following additional messages:     You add functions by selecting the message and then click on the "Add Function" button. For example:     You may notice that, OnCreate is added into the Member Function window.

    Remember that the system can only create a skeleton for the member function. It is your responsibility to insert computations into the relavent member functions.

6. Linking OpenGL Libraries

    So far what we have created is all standard MFC stuff. It has nothing to do with OpenGL yet. To connect to OpenGL, we have do a few things first, then we may use functions from OpenGL library.

    First, we need to insert the following lines into COpenGLView.h header file:

// Include the OpenGL headers
#include "gl\gl.h"      // standard OpenGL library
#include "gl\glu.h"     // OpenGL utility library
#include "gl\glaux.h"   // OpenGL auxiliary library
    To add the above contents, you have first to be able to find the desired header file. This is a good time to learn how to navigate around the files of the project.     Now, you may go ahead to insert the above contents at the very beginning of this header file. Remember to save the document before you close this Editor window

    The next step is to add the OpenGL libraries to the link step:

    At this point, you might want compile your application again to make sure that everything is right so far.

D. Backup Your Project before You Leave

    You must backup your project before you leave, because you may not use the same PC next time. Even you use the same PC, the contents may be destroyed by others. You must exit Visual C++ system before you do backup. Remember to save all the documents before quitting.

    You project should be in the OpenGL folder under C:\Workarea\. I suggest that you copy the entire OpenGL folder to a floppy disk. But, you have to delete all files under OpenGL\Debug folder, because they are too large (>= 6 Mb) to be stored on a floppy disk. Beside, you will get them back next time when you compile your program again. All the source files should be small enough to put on a floppy disk.

    Next time when you want to continue the project, you may load the project from the backup floppy disk back to the harddisk, preferably at C:\Workarea\.

    Note that, you are to continue with an existing project now. Do the following to open an existing project.

Then, the project is loaded in the Workspace and back to where you have left.

E. Editing Member Functions

    Now, we are ready to modify or add some functions to do real computation. For this lab, you are concerned with COpenGLView class only. Therefore, you will work with two files:

    Here are the list of the two files. The parts you will work with are highlighted in red color.

    COpenGLView.h


    // COpenGLView.h : interface of the COpenGLView class 

    // You should have already added these lines. 
    #include "gl\gl.h" 
    #include "gl\glu.h" 
    #include "gl\glaux.h" 

    ...... 

    class COpenGLView : public CView 
    { 
    protected: // create from serialization only 
    ..... 

    // Attributes 
    ...... 

    // Operations 
    public: 

    ...... 

    // Implementation 
    public: 
    ...... 

    protected: 

    // Generated message map functions 
    protected: 
    ...... 

          // You will add the following stuff!!! 

           virtual BOOL SetupPixelFormat( void );
           virtual BOOL SetupViewport( int cx, int cy );
           virtual BOOL SetupViewingFrustum( GLdouble aspect_ratio );
           virtual BOOL SetupViewingTransform( void );
           virtual BOOL PreRenderScene( void ) { return TRUE; }
           virtual void RenderStockScene( void );
           virtual BOOL RenderScene( void );

    private:

           BOOL InitializeOpenGL();
           void SetError( int e );
       
           HGLRC     m_hRC;
           CDC*      m_pDC;
           
           static const char* const _ErrorStrings[];
           const char* m_ErrorString; 
      }

     ...... 



   

COpenGLView.cpp


    // OpenGLView.cpp : implementation of the COpenGLView class 

    #include "stdafx.h" 
    ...... 

    ///////////////////////////////////////////////////////////////////////////// 
    // COpenGLView 

    IMPLEMENT_DYNCREATE(COpenGLView, CView) 

    BEGIN_MESSAGE_MAP(COpenGLView, CView) 
    ...... 
    END_MESSAGE_MAP() 

    // You will add stuff here!!!! 
   const char* const COpenGLView::_ErrorStrings[]= {
                   {"No Error"},                     // 0
                   {"Unable to get a DC"},           // 1
                   {"ChoosePixelFormat failed"},     // 2
                   {"SelectPixelFormat failed"},     // 3
                   {"wglCreateContext failed"},      // 4
                   {"wglMakeCurrent failed"},        // 5
                   {"wglDeleteContext failed"},      // 6
                   {"SwapBuffers failed"},           // 7
 
          };
  
  

    ///////////////////////////////////////////////////////////////////////////// 
    // COpenGLView construction/destruction 

     COpenGLView::COpenGLView() : 
          // You will add the following line !!! 
          m_hRC(0), m_pDC(0), m_ErrorString(_ErrorStrings[0])

     { 
     ...... 
     } 

    ...... 

    BOOL COpenGLView::PreCreateWindow(CREATESTRUCT& cs) 
    { 
     // TODO: Modify the Window class or styles here by modifying 
     //  the CREATESTRUCT cs 

       // You will add stuff here !!! 
       // An OpenGL window must be created with the following flags and must not
       // include CS_PARENTDC for the class style. 
       cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

      return CView::PreCreateWindow(cs); 
    } 

    ///////////////////////////////////////////////////////////////////////////// 
    // COpenGLView drawing 

    void COpenGLView::OnDraw(CDC* pDC) 
    { 
      COpenGLDoc* pDoc = GetDocument(); 
      ASSERT_VALID(pDoc); 
      // TODO: add draw code for native data here 
  
      // You are to add a lot stuff here !!!!!! 
     
      ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 
      PreRenderScene();
 
      ::glPushMatrix();
      RenderStockScene();
      ::glPopMatrix();
 
      ::glPushMatrix();
      RenderScene();
      ::glPopMatrix();
 
      ::glFinish();
 
       if ( FALSE == ::SwapBuffers( m_pDC->GetSafeHdc() ) ) 
      { 
          SetError(7); 
      } 
            } 

    ...... 

    ///////////////////////////////////////////////////////////////////////////// 
    // COpenGLView message handlers 

    int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    { 
         if (CView::OnCreate(lpCreateStruct) == -1) 
          return -1; 
  
          // You will add stuff here!!! 
            InitializeOpenGL();

          return 0; 
    } 

    void COpenGLView::OnDestroy() 
    { 
        CView::OnDestroy(); 
  
        // TODO: Add your message handler code here 
  
        // You will add some stuff here!!!! 
           if ( FALSE == ::wglDeleteContext( m_hRC ) )
           {
                SetError(6);
           }
       
           if ( m_pDC )
           {
              delete m_pDC;
           }

      } 

    BOOL COpenGLView::OnEraseBkgnd(CDC* pDC) 
    { 
       // TODO: Add your message handler code here and/or call default 
  
       // You are to modify the line follows !!!! 
       // return CView::OnEraseBkgnd(pDC);

       return TRUE; // tell Windows not to erase the background
    } 

    void COpenGLView::OnSize(UINT nType, int cx, int cy) 
    { 
      CView::OnSize(nType, cx, cy); 
  
      // TODO: Add your message handler code here 

      // You are to add a lot of stuff here !!!! 
     GLdouble aspect_ratio; // width/height ratio
     
      if ( 0 >= cx || 0 >= cy )
          {
          return;
          }
 
 
      SetupViewport( cx, cy );
 
      // select the projection matrix and clear it
      ::glMatrixMode( GL_PROJECTION );
      ::glLoadIdentity();
 
      // compute the aspect ratio
      // this will keep all dimension scales equal
      aspect_ratio = (GLdouble)cx/(GLdouble)cy;
 
      // select the viewing volumn
      SetupViewingFrustum( aspect_ratio );
      
      // switch back to the modelview matrix
      ::glMatrixMode( GL_MODELVIEW );
      ::glLoadIdentity();
 
      // now perform any viewing transformations
      SetupViewingTransform();
       
      } 
  

    ///////////////////////////////////////////////////////////////////////////// 
    // GL helper functions 
    // You are to add all new functions here !!!!!!!!!! 
    // They include: 
    //    void COpenGLView::SetError( int e ) 
    //    BOOL COpenGLView::InitializeOpenGL() 
    //    BOOL COpenGLView::SetupPixelFormat() 
    //    BOOL COpenGLView::SetupViewport( int cx, int cy ) 
    //    BOOL COpenGLView::SetupViewingFrustum( GLdouble aspect_ratio ) 
    //    BOOL COpenGLView::SetupViewingTransform() 
    //    BOOL COpenGLView::RenderScene() 
    //    void COpenGLView::RenderStockScene() 

      void COpenGLView::SetError( int e )
      {
           // if there was no previous error,
           // then save this one
           if ( _ErrorStrings[0] == m_ErrorString ) 
           {
                m_ErrorString = _ErrorStrings[e];
           }
      }
       
      BOOL COpenGLView::InitializeOpenGL()
      {
           // Can we put this in the constructor?
          m_pDC = new CClientDC(this);
       
          if ( NULL == m_pDC ) // failure to get DC
                {
                SetError(1);
                return FALSE;
                }
       
           if (!SetupPixelFormat())
                {
              return FALSE;
                }
       
          //n = ::GetPixelFormat(m_pDC->GetSafeHdc());
          //::DescribePixelFormat(m_pDC->GetSafeHdc(), n, sizeof(pfd), &pfd);
       
          //  CreateRGBPalette();
       
          if ( 0 == (m_hRC = ::wglCreateContext( m_pDC->GetSafeHdc() ) ) )
                {
                SetError(4);
                return FALSE;
                }
       
          if ( FALSE == ::wglMakeCurrent( m_pDC->GetSafeHdc(), m_hRC ) )
                {
                SetError(5);
                return FALSE;
                }    
       
           // specify black as clear color
          ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
           // specify the back of the buffer as clear depth
          ::glClearDepth( 1.0f );
           // enable depth testing
          ::glEnable( GL_DEPTH_TEST );
       
           return TRUE;
      }
 
      BOOL COpenGLView::SetupPixelFormat()
      {
        static PIXELFORMATDESCRIPTOR pfd = 
           {
              sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
              1,                              // version number
              PFD_DRAW_TO_WINDOW |            // support window
                PFD_SUPPORT_OPENGL |          // support OpenGL
                PFD_DOUBLEBUFFER,             // double buffered
              PFD_TYPE_RGBA,                  // RGBA type
              24,                             // 24-bit color depth
              0, 0, 0, 0, 0, 0,               // color bits ignored
              0,                              // no alpha buffer
              0,                              // shift bit ignored
              0,                              // no accumulation buffer
              0, 0, 0, 0,                     // accum bits ignored
              // 32,                          // 32-bit z-buffer
              16,    // NOTE: better performance with 16-bit z-buffer
              0,                              // no stencil buffer
              0,                              // no auxiliary buffer
              PFD_MAIN_PLANE,                 // main layer
              0,                              // reserved
              0, 0, 0                         // layer masks ignored
          };
          int pixelformat;
       
          if ( 0 == (pixelformat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) )
               {
                SetError(2);
              return FALSE;
                }
       
          if ( FALSE == ::SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, &pfd) )
               {
                SetError(3);
              return FALSE;
                }
       
          return TRUE;
      }
       
      BOOL COpenGLView::SetupViewport( int cx, int cy )
      {
           // select the full client area
          ::glViewport(0, 0, cx, cy);
       
           return TRUE;
      }
       
      BOOL COpenGLView::SetupViewingFrustum( GLdouble aspect_ratio )
      {
           // select a default viewing volumn
          ::gluPerspective( 40.0f, aspect_ratio, .1f, 20.0f );
           return TRUE;
      }
       
       
      BOOL COpenGLView::SetupViewingTransform()
      {
           // select a default viewing transformation
           // of a 20 degree rotation about the X axis
           // then a -5 unit transformation along Z
           ::glTranslatef( 0.0f, 0.0f, -5.0f );
           ::glRotatef( 20.0f, 1.0f, 0.0f, 0.0f );
          return TRUE;
      }
       
       
      BOOL COpenGLView::RenderScene()
      {
           // draw a red wire sphere inside a
           // light blue cube
       
           // rotate the wire sphere so it's vertically
           // oriented
           ::glRotatef( 90.0f, 1.0f, 0.0f, 0.0f );
           ::glColor3f( 1.0f, 0.0f, 0.0f );
           ::auxWireSphere( .5 );
           ::glColor3f( 0.5f, 0.5f, 1.0f );
           ::auxWireCube( 1.0 );
          return TRUE;
      }    
       
      // Draw a square surface that looks like a
      // black and white checkerboard
      void COpenGLView::RenderStockScene()
      {
           // define all vertices   X     Y     Z
           GLfloat v0[3], v1[3], v2[3], v3[3], delta;
           int color = 0;
       
           delta = 0.5f;
       
           // define the two colors
           GLfloat color1[3] = { 0.9f, 0.9f, 0.9f };
           GLfloat color2[3] = { 0.05f, 0.05f, 0.05f };
       
           v0[1] = v1[1] = v2[1] = v3[1] = 0.0f;
       
           ::glBegin( GL_QUADS );
       
           for ( int x = -5 ; x <= 5 ; x++ )
                {
                for ( int z = -5 ; z <= 5 ; z++ )
                    {
                    ::glColor3fv( (color++)%2 ? color1 : color2 );
                
                    v0[0] = 0.0f+delta*z;
                    v0[2] = 0.0f+delta*x;
       
                    v1[0] = v0[0]+delta;
                    v1[2] = v0[2];
       
                    v2[0] = v0[0]+delta;
                    v2[2] = v0[2]+delta;
       
                    v3[0] = v0[0];
                    v3[2] = v0[2]+delta;
       
                    ::glVertex3fv( v0 );
                    ::glVertex3fv( v1 );
                    ::glVertex3fv( v2 );
                    ::glVertex3fv( v3 );
                    }
                }
           ::glEnd();    
           
      

            Once you modified the above two files. You can compile and run the program. See what is displayed.

Click HERE to see the result of this program.

            Good luck!

            If your program runs correctly, you might be wondering how exactly the picture is drawn. That is, you want to understand how each function works. Well, you do not have to worry about this in the first lab. The next four labs will go through these subjects one-by-one in detail. Of course, we will learn a lot of more advanced functions and features.