Home » Things I've Made

Using OpenGL with MFC

14 October 2000 44 Comments

This tutorial is intended to describe how to setup MFC with OpenGL or how to setup OpenGL with MFC.

In this lesson we’ll create a simple MFC project, workspace and application. The project will create a window complete with menu, toolbar, status bar and rotate a nice torus in the window.

First off, Download the code for this lesson.

This tutorial is intended to describe how to setup MFC with OpenGL or how to setup OpenGL with MFC, depending on your point of view. Many people have asked for this and it’s a topic that comes up frequently on the message boards so here it is. It’s also surprisingly simple to do.
MFC really is easy to use once you get used to it and Visual C++ IDE does a tremendous job of automating the development process. So let’s get started.

In this lesson we’ll create a simple MFC project, workspace and application. The project will create a window complete with menu, toolbar, status bar and rotate a nice torus in the window.

The Classes you'll have after your application is generated.
First thing you want to do is create the project. Fire up VC++ and create a new MFC App Wizard project. Move through the wizard, entering the parameters as you see fit. It really doesn’t matter what you pick here so go nuts.
Here’s a shot of the classes you’ll end up with.

VC generates lots of neat code for you. It’s a good idea to have it insert “TODO” comments as this will help you figure out what most of the methods are for.
Now let’s prepare the window. First off you need to modify the window in the PreCreateWindow method of the View class so that the window can work with OpenGL. Do this by adding the following lines to your project:

BOOL CMfc_basicView::PreCreateWindow(CREATESTRUCT& cs)
{
// Add Window styles required for OpenGL before window is created
cs.style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CS_OWNDC);

return CView::PreCreateWindow(cs);
}

The PreCreateWindow Message handler in the Workspace window.
Make your PreCreateWindow look like mine ( with the exception of the CMFC_basicView:: piece, this is the class name and will differ based on what you named your project/class ).

Ok, let’s get the include files inserted into the project. But where do they go I hear you ask: Since we’ll be using openGL calls from potentially anywhere in the application, we’ll add them to a file called stdafx.h. You may need to go find this file under the “headers” section in the File view of the Workspace window.
Here’s a snippet from that file that includes the last line generated by VC++ and the include files I’ve added.

#include <afxcmn.h>
// MFC support for Windows Common Controls

// Inserted these files for openGL
#include <gl.h>
#include <glut.h>
#include <glu.h>
#include <glaux.h>

#endif // _AFX_NO_AFXCMN_SUPPORT

Now we’ll setup the PixelFormat etc. for OpenGL. Before we do, here are a couple of member variables I find it useful to add to the View class at this point. They are the following.
Just paste these into your View class .h file under Attributes, public section.

HGLRC m_hRC; // Permanent Rendering Context
HDC m_myhDC; // Private GDI Device Context
int m_height; // Stores the height of the View
int m_width; // Stores the width of the view

These variables store the rendering and Device context information.
I also like to store the width and height in case I need them. I could use a GetRect call to obtain these when I need them but I like to grab’em and store them when the window is re-sized. We’ll see that later.

While you’re in the View class header add the following method : BOOL SetupPixelFormat();
You can use VC++’s tool to do this by right-clicking on the View class and then selecting Add Member Function. I recommend using this method as it’s going to generate the .h statement and the body of the function for you. Here’s the code I enter into that method. It’s pretty much the same as every other OpenGL function and I’ve commented the code so it should make sense. Let me know if you need any further explanation.

BOOL COglm_demoView::SetupPixelFormat()
{
GLuint PixelFormat;
static PIXELFORMATDESCRIPTOR pfd= {
sizeof(PIXELFORMATDESCRIPTOR),
// Size Of This Pixel Format Descriptor
1,
// Version Number (?)
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
24, // Select A 24Bit 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, // Accumulation Bits Ignored (?)
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer (?)
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved (?)
0, 0, 0 // Layer Masks Ignored (?)
};

m_myhDC = ::GetDC(m_hWnd); // Gets A Device Context For The Window
PixelFormat = ChoosePixelFormat(m_myhDC, &pfd); // Finds The Closest Match To The Pixel Format We Set Above

if (!PixelFormat)
{
::MessageBox(0,”Can’t Find A Suitable PixelFormat.”,”Error”,MB_OK|MB_ICONERROR);
PostQuitMessage(0);
// This Sends A ‘Message’ Telling The Program To Quit
return false ; // Prevents The Rest Of The Code From Running
}

if(!SetPixelFormat(m_myhDC,PixelFormat,&pfd))
{
::MessageBox(0,”Can’t Set The PixelFormat.”,”Error”,MB_OK|MB_ICONERROR);
PostQuitMessage(0);
return false;
}

m_hRC = wglCreateContext(m_myhDC);
if(!m_hRC)
{
::MessageBox(0,”Can’t Create A GL Rendering Context.”,”Error”,MB_OK|MB_ICONERROR);
PostQuitMessage(0);
return false;
}

if(!wglMakeCurrent(m_myhDC, m_hRC))
{
::MessageBox(0,”Can’t activate GLRC.”,”Error”,MB_OK|MB_ICONERROR);
PostQuitMessage(0);
return false;
}

// Now that the screen is setup we can
// initialize OpenGL();
InitGL();
return true;
}

Next you’ll want to disable the WM_ERASEBACKGROUND message. Without disabling this you’ll see a nasty flicker in your applications. Do this by returning FALSE from the OnEraseBkgnd() method. Your view won’t have this by default so you’ll need to right-click on the view class in the workspace window. Next select “Add Windows Message Handler” from the pop-up menu.In the Dialog that opens up look in the list box for the WM_ERASEBKGND message. Finally hit the “Add and Edit” button to add this
message handler and edit it. This will point you at the newly created message handler for that message, aka
OnEraseBkgnd(CDC* pDC).

Getting back to disabling this method: in the message handler just return FALSE. Make itlook like this.

BOOL CMfc_basicView::OnEraseBkgnd(CDC* pDC)
{
return FALSE;
}

Alright, we’re almost half-way there. You’ll have noticed that in SetupPixelFormat I called a function InitGL(). Let’s add that function to your View class. This will initialize OpenGL and you can modify it as you see
fit.

void CMfc_basicView::InitGL()
{
// Enables Depth Testing
glEnable(GL_DEPTH_TEST);

// Enable the point size for selected points
glPointSize(5.0f);

// This Will Clear The Background Color To Black
glClearColor(.4, 0.2, 0.0, 0.0f);

// Reset the current projection matrix
SetProjection();

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//Enable back face culling, defaults to Clock wise vertices.
glEnable(GL_CULL_FACE);

glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glShadeModel(GL_SMOOTH);
glPolygonMode(GL_FRONT, GL_FILL);

}

Next we’ll want to write some code to handle the window resizing. In order to do this we add some code to another message handler. This time it’s the OnSize Message Handler. Right-click as before and follow the same steps to create the OnSize Message handler. You’ll notice the “UINT nType, int cx, int cy” paramaters that get passed into the method. These are the new width and height, cx and cy, of the window. We can use these to adjust our viewport and
projection matrices like accordingly.

void CMfc_basicView::OnSize(UINT nType, int cx, int cy)
{

CView::OnSize(nType, cx, cy);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Make the rendering context current
wglMakeCurrent(m_myhDC,m_hRC);

// Reset The Current Viewport And Perspective Transformation
glViewport(0, 0, cx, cy);

m_height= cy;
m_width = cx;

// Calculate The Aspect Ratio Of The Window
gluPerspective(60.0f,
(GLfloat)cx/(GLfloat)cy,
0.1f,
1000.0f);
}

Ok, we just have one more function to add and that’s this one.

void CMfc_basicView::SetProjection()
{

glViewport(0, 0, m_width, m_height);

// Reset The Projection Matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

// It’s a perspective projection
// Calculate The Aspect Ratio Of The Window
gluPerspective(60.0f,(GLfloat)m_width/(GLfloat)m_height, 0.1f,3000.0f);
}

This is just a simple function I use sometimes to reset the projection matrices. It’s up to you if you want to include this in your project.

So where do we put the Drawing code? Before we do that let’s link in the OpenGL libraries we need. Go the Project Menu and select Settings. Hit the Link tab and add the following statements to the end of the library entries.

opengl32.lib glu32.lib glut.lib glaux.lib

Next we want to add the call to SetupPixelFormat. The best time to do this is on window creation, just like you’d do in a normal win32 application. For this we’ll make use of the OnCreate message handler. Add this as you’ve added the other message handlers. Here’s the code to call the SetupPixelFormat function. Note I’ve added the call to wglMakeCurrent(NULL,NULL). This gives up the context to other processes.

int CMfc_basicView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

SetupPixelFormat();
wglMakeCurrent(NULL,NULL);

return 0;
}

Let’s compile, hit Ctrl-F5 and you should get a working application that just draws a window. It won’t even clear the screen for you. We need to add some drawing code. I’ve added a call to draw a few Toruses.

/////////////////////////////////////////////////////////////////////////////
// CMfc_basicView drawing
// This function draws three toruses on the screen and rotates them
// Replace this with your own drawing code.
//
void CMfc_basicView::OnDraw(CDC* pDC)
{
// Integer declared as static so the value is maintained
static int i=0;

// Increase i, this is the rotation value.
i += 1.11;

// Make the rendering context current as we’re about to
// draw into it
wglMakeCurrent(m_myhDC,m_hRC);

// Clear the screen and the depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Reset the model matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Translate to a suitable position
glTranslatef(0,0,-100);

// We’re going to draw using this color. ( an orange )
glColor3f( 0.89f, 0.36f, 0.0f);

// Rotate to some angle (i)
glRotatef(i, 1,0,0);
glRotatef(i ,0,1,0);
glRotatef(i ,0,0,1);

// Draw the first torus.
auxSolidTorus( 8,50);

// Rotate some more and draw the second.
glRotatef(i ,0,1,0);
glRotatef(i ,0,0,1);
auxSolidTorus( 8,30);

// Rotate some more and draw the third
glRotatef(-i ,0,1,0);
glRotatef(i ,0,0,1);
auxSolidTorus( 8,10);

// Swap the virtual screens
SwapBuffers(m_myhDC);

// Invalidate the window as we’re
// ready to draw another frame
Invalidate(FALSE);

}

And that’s it. Ths application is complete. Obviously this can be improved, the app only draws three toruses. It also uses a lot of CPU in idle time. I left these features as they are since I only wanted to illustrate the use of OpenGL with MFC. If you’d like to experiment, take this code and run with it. I’ll be happy to post anything you come up with. I hope found this TUT useful, if you did let me know.

44 Comments »

  • Francis (author) said:

    Amol: you dont include a lib, you link it. Make sure glut.lib is in your includes directory.

    Zaheer: why dont you just select the whole thing, copy it into notepad and print?

  • Amol said:

    I can,t include glut.lib. It gives me linking error.
    If I dont include glut.lib then I cant see anything on windpow except brown colour.

  • Zaheer (author) said:

    I want to read this tutorial. How can I can dowload it.

  • Adnan said:

    the link for downloading the tutorial is not working

  • Francis (author) said:

    Fixed the link. Click away.

  • Francis (author) said:

    NOTE: To run the source code, you need the GLUT available here:
    http://www.opengl.org/resources/libraries/glut.html

  • Kevin Wang said:

    Sir,
    I downloaded your sample code and compilered it, but got error message.
    The error message is like that: c: empformfc stdafx.h(22): fatal error C1083: Cannot open include file:
    glglut.h: No such file or directory. I dont know how to solve this problem. Could you give me some help in this way? Thanks.

  • Amol said:

    I want to select object in openGL.
    I have find no of things.But code on different sites are not easy to understand. Please help me.

  • Francis (author) said:

    For Selection, read these articles:

    http://www.francisshanahan.com/default.aspx?ctype=Articles&top=OpenGL%20Selection

  • hind said:

    how we program games

  • CHE said:

    Hi Francis,

    Ive just read your OpenGL-MFC turorial and executed successfully the program. But the problem is, that it is too CPU-cycles hungry. The CPU is always 100% busy, although OpenGL just clears the window. Why is it so?? Thanks a lot.

    Che

  • Gustavo said:

    Esto es una porqueria no funciona

  • Francis (author) said:

    CHE — Sorry, the CPU is fairly cycle hungry as the screen is constantly invalidated. You can regulate this with the WM_TIMER event. Enjoy.

  • Simone said:

    Great! Its do!

  • CcaptnB said:

    Hi, nice tutorial! Im just wondering how I could manage mouse events to zoom, pan or rotate my models in your example.

  • Francis (author) said:

    CcaptnB:
    Youd have to first capture where the mouse click happens (X,Y), then track an offset as teh mouse is moved. Depending on the delta between the current and clicked positions of the mouse, youd change the rotation or transformation of the model. I did this in the OGLM tool which is elsewhere on the site. Look for the code that renders the quadrants of the model. This exact functionality is done in the quadrants.

  • Ryan said:

    Francis, Im trying to write a demo very similar to what you show here… but when I call Invalidate() in my OnDraw() handler, the application stops rendering after the first pass. Any thoughts on what could be causing this?

  • Francis (author) said:

    Ryan:
    No sorry, you can email me the code if you want and if I get a spare min Ill take a look.

  • HUYOO (author) said:

    I have just modified this article(which is post in my favorites,not in my articles),and only remain the introduction and the link .
    i am getting out to work these days,so i am sorry i do not reply your comments immediatly~~~

  • Ernest said:

    Hi, very good tutorial! Thanks to people like you i have started to dive in this OpenGLs world. Thank you!

  • LApko Michael said:

    Thanks a lot!!!
    Very good tutorial!
    I saw many tut of Mfc and OpenGl but they were too much complicated. But you exactly what i need. Thanks.
    Just one request, may I?
    Would you like to write tut about making menu and control dialog toolbar as simply as this tutorial.

  • Nishant Mehta (author) said:

    Thanks a lot! Absolutely great….even better than whats given in the opengl superbible.

  • Andrew Senin said:

    Hello Francis! Its a really useful article. But there is something wrong with your project, I think. I downloaded it, started. Everything was fine but after I had changed configuration from debug to release all I saw was the black window. So, it did not show anything in release configuration. I tested it on 2 computers with similar results: debug works, release does not. Perhaps it is because I changed library files to opengl32.lib glu32.lib (I use standard realization of OpenGL from Microsoft). Do you have any ideas why it does not work in release? Thank you. –Andrew.

  • Francis (author) said:

    Hi Andrew. Ive no idea why Release config doesnt work for you but Id suspect its your libs. Microsoft vs OpenGL versions are not exactly alike as I recall.

  • pskumaran said:

    simply superb.

  • Hanns (author) said:

    Great Tutorial, but to start I had to change glut.lib to glut32.lib. Also the rotation was to fast. So I changed the integer i in OnDraw to a float and set it to 0.1 and then it was okay.

  • Priyadarshi said:

    Really cool stuff!!

  • Patrick Kellen said:

    Good tutorial for me. I struggled through some of the message handler stuff, but I had no previous MFC experience, just OpenGL.

  • Ceres629 (author) said:

    Great tutorial and it worked which is great but i dont really know how to draw my scenes, everything I try just gives me a black screen. Is there something I need to change with the view?

  • Ceres629 (author) said:

    Nevermind, I figured it out… great tutorial though good work ;)

  • Noob said:

    Really good TUT. I have a question besides the TUT.
    Is there any tools that can generate this code(somth like a 3d wizard ?)? and Is there a way to import somehow a 3dsMAX generated 3d object without drawing it myself(maybe with somekind of plug in that exports to smth compatible)?

  • ceinx (author) said:

    thanks a lot….it realy help me…..

  • santosh said:

    good effort. but some illustrative example
    with picture would serve the purpose more
    efficiently.

  • Francis (author) said:

    Lam: Check http://nehe.gamedev.net, they should have what you’re looking for.

  • lam said:

    i’m want to have some code to using mouse in Opengl with visual c++ 7.0

  • Bob (author) said:

    Nice work Francis, thanks a lot. There is just something that puzzles me however: If you click on the MAXIMIZE box button on the title bar, the application fills the whole screen but the toolbar of the mainframe seems to have an issue to redraw properly. If yo MINIMIZE the mainframe again and move the window a bit, it repaints fine. I tried on another computer with a powerful video card and got the same issue. Has anyone experienced this also? If yes, how to fix it…?

  • Godwin said:

    Excellent resource, just what I was looking for.

  • Aristotel (author) said:

    Thanks for this nice tutorial! :)

  • Nizar (author) said:

    Hi,
    Great tutorial.
    thanks.
    One suggestion.
    if you can override the OnSize function of the Mainframe, the resize, maximize and minimize issues will be solved.

    Example.

    void CMainFrame::OnSize(UINT nType, int cx, int cy)
    {
    CFrameWnd::OnSize(nType, cx, cy);

    RedrawWindow();
    // TODO: Add your message handler code here

    }

  • plum said:

    i exactly followed your tutorial but got a blank window with nothing displayed, would you please point out where the problem most possibly be? thx!

  • Francis said:

    Not sure I can help you, I haven’t tried that code in years.

  • H. Lin said:

    Dear Sir,
    Your code is very concise and informative. Thanks.
    Right now I have a problem getting instantaneous coordinates of vertices of transformed objects while they are performing either translation, rotation, etc. For example, I want to draw multiple linee from all the vertices of transforming object to a fix 3D point. The positions and lengths of these lines should be changed instantaneously. Would you mind giving an advise?
    Thanks a lot.
    By the way,
    Some user complained about the linking your code for not able to find the glut.lib. I got the similar error at first. It was solved after I changed the glut.lib to glut32.lib in project setup, since there is no glut.lib in visual studio.
    Thanks again!
    H.L.

  • min said:

    This gave me a good help, thank you very much for your knowledge and time!

  • saintpp said:

    Thank you very much for your nice tutorial. It really helps me a lot. Currently, I am trying to use part of the code to build GUI of a software. However,I found the program often crash when click the Menu>Help>About. Usually the program freeze here. It seems that the MFC dialogs have some kind of confliction with OpenGL code here. Have you even meet such things? Do you know how to prevent it? Thanks a lot.

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.