OpenGL Programming With Windows MFC
OpenGL Programming With Windows MFC
Before you begin this exercise, be sure you have installed freeGLUT and GLEW.
The following show you how to create a framework for the OpenGL viewing class. You will need to perform the following steps.
You will notice that on the left hand side are seven project setting options. The three that you will modify are circled in red. For thoroughness, we
will go through all seven options.
In "Application Type"
Select "Single document"
Select "Document/View architecture support"
Un-select "Security Development Lifecycle (SDL) checks" - the security checks deprecate some insecure C routines that Dr. Angel's library
uses.
Un-select "Use Unicode libraries
Select "MFC standard" - this will adjust some settings for you.
Select "Use MFC in a shared DLL"
In "Compound Document Support"
Make sure "None" is selected
In "Document Template Properties"
You do not have to worry about any of these settings
In "Database Support"
Make sure "None" is selected
In "User Interface Features", the following setting are needed
"Thick frame"
"Minimize box"
"Maximize box"
Under "Command bars (menu/toolbar/ribbon):", select "Use a classic menu" and uncheck the toolbar options
You can ignore all other settings
In "Advanced Features"
Clear all the options (keep things simple for now)
In "Generated Classes"
Should see four: COpenGLView, COpenGLApp, COpenGLDoc, and CMainFrame
Click the "Finish" button
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.
Right Top: Code Editor where you can write and edit your programs. When you first start Visual Studio, this is the "Start Page".
Right Bottom: Feedback Area. Depending on your current activity this area might show code definitions, compiler output, or debug information.
Left: Project Management Panel: contains Solution Explorer (default), Class View, Property Manager, and Team Explorer. Choose the one you
want with tabs at the bottom.
Solution Explorer shows you the files in the project or solution that you are working on.
Class View enables you to see the class hierarchies in your project.
Property Manager can be ignored for now.
Team Explorer can be ignored for now.
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 next section we will use the Class View tab in the Project Management Panel show the project's class hierarchy. 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 message handling functions into this class to allow
OpenGL to set up and render the view at the appropriate times.
At this point, you might want to build and run the application (which of course does nothing yet) just to make sure your the wizard gave you a working
project.
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:
Click on the "Class View" tab underneath the "Solution Explorer" on the right-hand side.
OR
Choose "View | Class View" from the menu
Expand the "OpenGL" project by clicking on the arrow to the left of "OpenGL".
You will now see the five classes that have been automatically created.
Click on "COpenGLView". There is no need to expand it.
If you examine the bottom panel of the Class view, you will see some member functions that have been automatically created including OnDraw
and PreCreateWindow.
Now, with COpenGLView 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, some properties of "COpenGLView" are listed.
The icons along the top allow you to select other property types. We want to see and edit the message mapping list. Follow the example below to see
how this is done, then add message handler functions for all the messages listed above.
Click on the messages icon (circled in red in the diagram to the left)
You will see a listing of messages.
You may notice that the OnCreate is added into the "Class View" window. Visual Studio has created
the member function for you. 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.
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.
Choose "Debug" from the menu options
Select "Start Without Debugging"
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.
NOTE: On Alex Clarke's VS2012 Professional install the OnCreate has several lines of garbage code that did not appear in previous versions. If your
code does not compile try using this version of OnCreate instead:
return 0;
}
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:
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.
Click the "Solution Explorer" tab (in the lower left), the "root" folder should be displayed.
Click the "+" node, and subfolders should be displayed. Two of them are of interest to you at the moment. They are "Source Files" and "Header
Files".
Click the "+" node to the left of the "Header Files" folder, you will see a number of header files are listed. One of them is "OpenGLView.h"
Double click on this filename. The contents of the "Editing Window" change to the contents in this file.
Now, you may add the above contents at the very beginning of this header file. Remember to save the document before you close this Editing window.
The next step is to add the OpenGL libraries to the link line:
Last, but not least, add support code from Lab 1 to your project by:
Downloading Angel.zip, uofrGraphics.zip
Extracting the files into your project folder next to the .cpp files that were generated by the Visual Studio wizard.
Downloading the shaders vshader.glsl and fshader.glsl to the project folder.
Adding all the new files (Angel.h, CheckErrors.h, fshader.glsl, InitShaders.cpp, mat.h, uofrGraphics.cpp uofrGraphics.h, vec.h,
and vshader.glsli) to your project with the "Project | Add Existing Item..." menu item.
Adding the line #include "stdafx.h" as the very first line in InitShader.cpp and uofrGraphics.h
Adding the line #include <wglew.h> immediately after the line #include <glew.h>.
At this point, you might want compile your application again to make sure that everything is right so far.
OpenGLView.h
OpenGLView.cpp
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
......
// Attributes
public:
......
// Operations
public:
// Overrides
public:
......
// Implementation
public:
......
protected:
// You will add the following stuff!!!
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;
......
OpenGLView.cpp
// OpenGLView.cpp : implementation of the COpenGLView class
#include "stdafx.h"
#include "OpenGLDoc.h"
#include "OpenGLView.h"
......
// COpenGLView
IMPLEMENT_DYNCREATE(COpenGLView, CView)
BEGIN_MESSAGE_MAP(COpenGLView, CView)
......
END_MESSAGE_MAP()
// COpenGLView construction/destruction
COpenGLView::COpenGLView()
// You will add the following line !!!
: m_hRC(0), m_pDC(0), m_ErrorString(_ErrorStrings[0]) // Call constructors
{
......
}
......
return CView::PreCreateWindow(cs);
}
// COpenGLView drawing
......
// We now have a rendering context, so we can set the initial drawing state.
// Find the initialize OpenGL function provided in the Lab 1 notes and call it here
return 0;
}
void COpenGLView::OnDestroy()
{
CView::OnDestroy();
if ( m_pDC )
{
delete m_pDC;
}
/////////////////////////////////////////////////////////////////////////////
// GL Rendering Context Creation Functions
//
// Since we are using Windows native windowing, we need to set up our own
// OpenGL rendering context. These functions do it to the main view area.
// It is possible to do it to a smaller sub view. If you are curious, you can
// find tutorials on how to do that on the net.
//
BOOL COpenGLView::GetRenderingContext()
{
// Can we put this in the constructor?
m_pDC = new CClientDC(this);
if (!GetOldStyleRenderingContext())
{
return TRUE;
}
//Get access to modern OpenGL functionality from this old style context.
glewExperimental = GL_TRUE;
if (GLEW_OK != glewInit())
{
AfxMessageBox(_T("GLEW could not be initialized!"));
return FALSE;
}
if(wglewIsSupported("WGL_ARB_create_context") == 1)
{
//If this driver supports new style rendering contexts, create one
HGLRC oldContext = m_hRC;
if ( 0 == (m_hRC = m_hRC = wglCreateContextAttribsARB(m_pDC->GetSafeHdc(),0, attribs) ) )
{
SetError(4);
return FALSE;
}
if(!wglMakeCurrent(NULL,NULL) )
wglDeleteContext(oldContext);
if ( FALSE == wglMakeCurrent( m_pDC->GetSafeHdc(), m_hRC ) )
{
SetError(5);
return FALSE;
}
}
else
{
//Otherwise use the old style rendering context we created earlier.
AfxMessageBox(_T("GL 3.2 Context not possible. Using old style context. (GL 2.1 and before)"));
}
return TRUE;
}
BOOL COpenGLView::GetOldStyleRenderingContext()
{
//A generic pixel format descriptor. This will be replaced with a more
//specific and modern one later, so don't worry about it too much.
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
32, // 32-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
24, // 24-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
// Get the id number for the best match supported by the hardware device context
// to what is described in pfd
int pixelFormat = ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);
BOOL COpenGLView::SetupPixelFormat()
{
//This is a modern pixel format attribute list.
//It has an extensible structure. Just add in more argument pairs
//befroe the null to request more features.
const int attribList[] =
{
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0, 0 //End
};
return TRUE;
}
// Add the definitions for the OpenGL initialize, resize, and draw functions from the lab notes!
Once you have modified the above two files, you can compile and run the program.
If you got errors, you might have forgotten to add the OpenGL code from the lab notes to the OpenGLView.h and OpenGLView.cpp! You will also need
the correct shaders, or the program will appear not to start. If you get a warning that says "GL 3.2 Context not possible", you have a graphics card that
doesn't support OpenGL 3.2 Core Profile and you will have to either update your drivers, or try again with a newer card. I suggest any nVidia or AMD
card produced in the last couple years.