OpenGL Programming with Windows MFC


Building a Simple OpenGL View Class with MFC Framework

When Visual C++ has started, you will see a Start Screen similar to this one:

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. Your main window has changed. It now has three main regions.

If your window differs from the one pictured, you can still probably find the same elements. Most likely the Solution Explorer and Property manager will be on the left. The exact layout does not matter much.

In the Projct Management Panel, click on the "Class View" tab, and the class hierarchy will be displayed. There should be five classes (CAboutDlg, CMainFrame, COpenGLApp, COpenGLDoc, and COpenGLView) 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 does nothing yet) just to make sure there are no errors in your building process.

3. Add Necessary Member Functions to the COpenGLView Class

The MFC Project Wizard creates the minimum number of member functions for each class being generated. We may take a look at them by looking at the Class View:

To examine COpenGLView class: Now that COpenGLView is selected we can add functions for the following additional messages: To add functions for these messages, first ensure that you have selected "COpenGLView" in the "Class View", then press Alt + Enter to open the Properties window. It will open on the right of the screen. You will notice that in the Properties window, the properties of "COpenGLView" are listed. The goal is to list appropriate messages and select functions. Follow the below example for all four messages.

You may notice that, OnCreate is added into the "Class View" window.

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

Click on COpenGLView again to add the next function and repeat as needed.

4. 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 the project has compiled successfully, you will see a message like this:
---------------------- Done ----------------------
Build: 1 succeeded, 0 failed, 0 skipped

It is very exciting to run this little window's program if it is your first Windows program.

A new window should be displayed in a few seconds. There are three menus: "File", "Edit", and "Help". These are the minimum options automatically created by your VC++ system. Don't be surprised; the inside window is totally blank, because we haven't written any functions yet.

5. 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 OpenGLView.h header file:

// Include the OpenGL headers
#include "gl\gl.h" // standard OpenGL library
#include "gl\glu.h" // OpenGL utility library
#include "gl\glut.h" // replace this with "gl\freeglut.h" if you installed freeglut.

To add the above contents, you have to first 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 Editing window.

The following was necessary in VS2008 and earlier. If it causes problems in VS2010, then everything should work if you remove the Additional Dependencies.

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

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

D. Editing Member Functions

Now that the OpenGL libraries have been added to our project, we are ready add some real OpenGL code. For this lab, you are concerned with the COpenGLView class only. Therefore, you will work with two files:

The code you will add will:

  1. Create and set up a Device Context
  2. Establish a Rendering Context
  3. Handle window events
  4. Draw a scene
  5. Clean up after itself

For now here are the changes you need to make to the two files. The parts you will work with are highlighted in red.

File: OpenGLView.h
// OpenGLView.h : interface of the COpenGLView class


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

...... class COpenGLView : public CView { protected: // create from serialization only ..... // Attributes ...... // Operations public: ...... // Implementation public: ...... protected:
      // You will add the following stuff!!!

virtual BOOL SetupPixelFormat( void );


private:
//OpenGL Setup
BOOL GetRenderingContext();
      //Rendering Context and Device Context Pointers
HGLRC m_hRC;
CDC* m_pDC;

//Error Handling
void SetError( int e );
      static const char* const _ErrorStrings[];
const char* m_ErrorString;




// Generated message map functions protected: DECLARE_MESSAGE_MAP() public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnDestroy(); afx_msg void OnSize(UINT nType, int cx, int cy); }; ......
OpenGLView.cpp

// OpenGLView.cpp : implementation of the COpenGLView class

#include "stdafx.h"

// SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail
// and search filter handlers and allows sharing of document code with that project.
#ifndef SHARED_HANDLERS
#include "OpenGL.h"
#endif

#include "OpenGLDoc.h"
#include "OpenGLView.h"


// Include Event Helper prototypes and Scene Control Variables
// from lab 1 notes
...... // 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]) // Call constructors
{ ...... } ...... 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
    // Add OpenGL Drawing Code Here


    //Swap buffers to show result
    if ( FALSE == ::SwapBuffers( m_pDC->GetSafeHdc() ) )
    {
        SetError(7);
    }


} ...... // COpenGLView message handlers int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here
    // You will add stuff here!!!
GetRenderingContext();

//Add OpenGL Init code Here


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;
}
}
void COpenGLView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);

// TODO: Add your message handler code here

    //Add OpenGL Resize Code here
}
/////////////////////////////////////////////////////////////////////////////
// GL helper functions
// You are to add all new functions here !!!!!!!!!!
// They include:
// void COpenGLView::SetError( int e )
// BOOL COpenGLView::GetRenderingContext()
// BOOL COpenGLView::SetupPixelFormat()


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::GetRenderingContext()
{
// 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;
}

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;
}


// Be sure to add the init, resizing, and drawing functions from the lab notes!

Don't forget to add the OpenGL code from last lab to the OpenGLView.h and OpenGLView.cpp as well!

Once you have modified the above two files, you can compile and run the program.

E. Clean Your Project Before You Submit It

To clean up your project for submission delete all Debug folders in the project and delete all sdf files. They are rather large (in some cases > 30 MB) and the .exe file in the Debug folder will be rejected by many spam filtering systems. Besides, you will get them back the next time you compile your program. All the source files should be small enough to put on a floppy disk. Another option is to ftp the whole project folder (minus Debug folders) up to hercules.
Note: Opening Existing projects
To open an existing project, on the hard drive, you can change to that directory (For instance, My Documents\Visual Studio 2010\Projects\OpenGL) and double click on the solution file (OpenGL.sln), which looks like the following:
However, this approach will not give you the "Start Page" Tab.

Another way to open an existing project is:

The project should be up and ready to go (just as you left it).