Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

OpenGL MFC AppWizard

4.79/5 (10 votes)
15 Jan 2009GPL36 min read 157K   5.5K  
An article showing how to make a Custom AppWizard for OpenGL applications in Visual Studio .NET 2008

OpenGLWizard

Introduction

In this article, I will show you how to create a simple Visual Studio 2008 AppWizard. The AppWizard I will create will set up a basic OpenGL Application. This AppWizard has only one configurable property, which decides whether the axies and grid are rendered. However, once you've gone though this code, you should be able to add properties of your own!

My most basic OpenGL Application, called 'OpenGLApplication', which you can download from the link at the top of the page, was the source for the AppWizard. The purpose of the AppWizard is to spit out projects that look very similar to this application.

Background

I have been coding OpenGL for years but have never bothered with an AppWizard - there just seems to be too little information on the web. I finally bit the bullet and decided to dive in. This is the first version of the code, so please leave plenty of comments if there are any problems - I will update the code regularly.

The application itself is very simple - it is an MFC application WITHOUT the document / view architecture. In reality, this means there simply isn't a document - there is still a view. The OpenGL part of the code is quite a few years old now. There are three classes which enable OpenGL:

  • CDIBSurface - This is a class that enables rendering to a DIB surface.
  • COpenGLSurface - Derived from the CDIBSurface, this simply controls a render context.
  • COpenGLView - This is a CView derived class. It contains a COpenGLSurface. This is rendered to by OpenGL calls and blitted to the screen in the OnDraw function.

By overriding the DoOpenGLDraw and DoOpenGLResize functions, you can render using OpenGL and set up the viewport. This original OpenGL Application is available to download if you'd like to look at it.

Creating an AppWizard

Create a new Visual C++ AppWizard project. In my original project, I chose to create five property pages - this was rather optimistic and I soon reduced it to one!

Now the first thing to do is to make a copy of all of the files that you are going to include in your new project. Everything from my OpenGLApplication project in my case. Copy all of these files into the Template Files folder. Then add them to the project. The template files are the files that will be added to any project based on this AppWizard. Now open up the 'Templates.inf' file and make sure that all of the files are listed, like this:

readme.txt ChildView.cpp ChildView.h DIBSurface.cpp DIBSurface.h MainFrm.cpp MainFrm.h 
OpenGLApplication.cpp OpenGLApplication.h OpenGLApplication.rc OpenGLSurface.cpp 
OpenGLSurface.h OpenGLView.cpp OpenGLView.h Resource.h stdafx.cpp stdafx.h 
targetver.h res\OpenGLApplication.ico res\OpenGLApplication.rc2 res\Toolbar.bmp

This is a fairly standard set of files for an MFC project. By the time you have done this, you are ready for the first test.

The First Test

Compile the project and open up a new instance of Visual Studio. Create a new project based on the AppWizard. It should successfully create the project and copy all of the template files to it. Immediate problem number one - we don't have header files or resource files folder. Let's sort this now - first of all, we're going to open up the default.htm file and find the 'SOURCE_FILTER' string. Let's add some lines:

XML
<SYMBOL NAME='SOURCE_FILTER' TYPE=text
         VALUE='cpp;c;cxx;def;odl;idl;hpj;bat;asm'></SYMBOL>
<SYMBOL NAME='HEADER_FILTER' TYPE=text VALUE='h;hpp;hxx;hm;inl;inc'></SYMBOL>
    <SYMBOL NAME='RESOURCE_FILTER'
               TYPE=text VALUE='rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe'>
              </SYMBOL>

Here, we're defining what folders we'll have in the new project - and what types of files will go into them. We're not quite done yet though, open up the default.js file and find the AddFilters function - make it look like this:

C++
function AddFilters(proj)
{
	try
	{
		//  Add the Source Files group and set the filter.
		var group = proj.Object.AddFilter('Source Files');
		group.Filter = wizard.FindSymbol('SOURCE_FILTER');
		
		//  Add the Header Files group and set the filter.
		var group = proj.Object.AddFilter('Header Files');
		group.Filter = wizard.FindSymbol('HEADER_FILTER');
		
		//  Add the Source Files group and set the filter.
		var group = proj.Object.AddFilter('Resource Files');
		group.Filter = wizard.FindSymbol('RESOURCE_FILTER');
	}
	catch(e)
	{
		throw e;
	}
}

This is all we need to create the correct folders and automatically put the correct files into them. Build the project and create a new project based on this AppWizard - you should now see that the files are in folders just like in the original app.

Customising File Names

In the set of files shown above, we're going to want a few of them to be named after the project. For example, the View class will want file names like MyNewProjectView.h and MyNewProjectView.cpp rather than simply OpenGLApplicationView. We're going to delve into the default.js file to handle this. Let's look at the GetTargetName function.

C++
function GetTargetName(strName, strProjectName)
{
	try
	{
		//  Here we can create custom filenames.
		var strTarget = strName;

		if (strName == 'readme.txt')
			strTarget = 'ReadMe.txt';

        //  The ChildView class becomes the ProjectNameView class.
        if (strName == 'ChildView.cpp')
            strTarget = strProjectName + 'View.cpp';
        if (strName == 'ChildView.h')
            strTarget = strProjectName + 'View.h';
            
        //  The COpenGLApplication class becomes the ProjectNameApp
        // class (but no 'App' in the headers!).
        if (strName == 'OpenGLApplication.cpp')
            strTarget = strProjectName + '.cpp';
        if (strName == 'OpenGLApplication.h')
            strTarget = strProjectName + '.h';
        if (strName == 'OpenGLApplication.rc')
            strTarget = strProjectName + '.rc';
        if (strName == 'res\\OpenGLApplication.rc2')
            strTarget = 'res\\' + strProjectName + '.rc2';
        if (strName == 'res\\OpenGLApplication.ico')
            strTarget = 'res\\' + strProjectName + '.ico';

		return strTarget;
	}
	catch(e)
	{
		throw e;
	}
}

So what's going on here? Well, for every file that gets put into the newly created project, we can use this function to modify the name. By checking for the OpenGLApplication.cpp file for example, we can make the filename MyNewProject.cpp or whatever we want. But now, we have a new problem - what about everywhere that we've used '#include "OpenGLApplication.h"'? We need to find every instance in the source files where we have replaced part of a filename with the project name and update it like so, here's before:

C++
#include "stdafx.h"
#include "OpenGLApplication.h"
#include "MainFrm.h"

And here is the code after:

C++
#include "stdafx.h"
#include "[!output PROJECT_NAME].h"
#include "MainFrm.h"

The [!output PROJECT_NAME] is replaced with the project name that the user has chosen. This is where things start to get tricky - my AppWizard for example changes the name of six files. It also changes the name of two classes - like this:

C++
class C[!output PROJECT_NAME]View : public COpenGLView
{

public:

	//	Constructor / Destructor.
	C[!output PROJECT_NAME]View();
	virtual ~C[!output PROJECT_NAME]View();

So you have to go through your code in the Template Files folder with a fine-toothed comb and make sure you've updated everything correctly! If you're changing resource files like icons as well, you'll need to look through the rc and maybe even rc2 file! A good thing to do here is have another instance of Visual Studio open at the same time and keep on trying to build the project. Don't worry too much about odd linker errors just yet - just make sure there's nothing blazingly obviously wrong in the generated code.

Setting Up the Configurations

Now we come to the next problem - the project won't build. For one thing, it doesn't know to link to OpenGL32.lib and glu32.lib - but further investigation shows that the build parameters are all at their default - no MFC, no debug info, no nothing! Look at the AddConfig function in default.js. This is where you can change the build configurations. Let's make it look like this:

C++
function AddConfig(proj, strProjectName)
{
	try
	{
	    //  Create the debug settings.
		var config = proj.Object.Configurations('Debug');
		config.IntermediateDirectory = '$(ConfigurationName)';
		config.OutputDirectory = '$(SolutionDir)$(ConfigurationName)';
		config.CharacterSet = charSet.charSetUnicode;
		config.useOfMfc = useOfMfc.useMfcDynamic;

        //  Compiler.
		var CLTool = config.Tools('VCCLCompilerTool');      
		CLTool.UsePrecompiledHeader = pchOption.pchCreateUsingSpecific;
        CLTool.PrecompiledHeaderThrough = "StdAfx.h";
        CLTool.PrecompiledHeaderFile = '$(IntDir)\$(TargetName).pch';
        CLTool.MinimalRebuild = true;
        CLTool.BasicRuntimeChecks = basicRuntimeCheckOption.runtimeBasicCheckAll;
        CLTool.WarningLevel = warningLevelOption.warningLevel_3;
        CLTool.PreprocessorDefinitions = "WIN32;_WINDOWS;_DEBUG";
		CLTool.DebugInformationFormat = debugEditAndContinue;
		CLTool.Optimization = optimizeDisabled;
		CLTool.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDebugDll;

        //  Add linker dependencies.
		var LinkTool = config.Tools('VCLinkerTool');
		LinkTool.AdditionalDependencies = "opengl32.lib glu32.lib";
		LinkTool.LinkIncremental = linkIncrementalType.linkIncrementalYes;
		LinkTool.GenerateDebugInformation = true;
		LinkTool.ProgramDatabaseFile = "$(TargetDir)$(TargetName).pdb";
		LinkTool.SubSystem = subSystemOption.Windows;

	    //  Create the release settings.
		config = proj.Object.Configurations('Release');
		config.IntermediateDirectory = '$(ConfigurationName)';
		config.OutputDirectory = '$(SolutionDir)$(ConfigurationName)';
		config.CharacterSet = charSet.charSetUnicode;
		config.useOfMfc = useOfMfc.useMfcDynamic;

        //  Compiler - set the precompiled header settings.
		var CLTool = config.Tools('VCCLCompilerTool');
		CLTool.UsePrecompiledHeader = pchOption.pchCreateUsingSpecific;
        CLTool.PrecompiledHeaderThrough = "StdAfx.h";
        CLTool.PrecompiledHeaderFile = '$(IntDir)\$(TargetName).pch';
        CLTool.WarningLevel = warningLevelOption.warningLevel_3;
        CLTool.Optimization = optimizeOption.optimizeMaxSpeed;
        CLTool.EnableIntrinsicFunctions = true;
        CLTool.PreprocessorDefinitions = "WIN32;_WINDOWS;NDEBUG";
		CLTool.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDll;
        
        //  Add linker dependencies.
		var LinkTool = config.Tools('VCLinkerTool');
		LinkTool.AdditionalDependencies = "opengl32.lib glu32.lib";
		LinkTool.LinkIncremental = linkIncrementalType.linkIncrementalNo;
		LinkTool.SubSystem = subSystemOption.Windows;
	}
	catch(e)
	{
		throw e;
	}
}

What a pain. After much experimenting, this should set up the compile and link in debug and release perfectly. Test the AppWizard again - you should be able to build and run the project now.

Customising the App Wizard

By this point, the App Wizard should be working - in the sense that it generates a working application. It is now up to you to go though the TODOs in the code and put in your AppWizard name, description and so on. You can change images as well.

The last thing that I did was change the 'Example 1 Checkbox' that was generated by the AppWizard to 'Render Grid and Axies'. The symbol for it was changed to CHECKBOX_EXAMPLE_RENDERING. This can be used conditionally in the code, as in the ChildView.cpp file below:

C++
void C[!output PROJECT_NAME]View::DoOpenGLDraw()
{
[!if CHECKBOX_EXAMPLE_RENDERING]
	//	Here we'll do a little bit of example rendering, by drawing a cube.
	
	//	Set the clear colour to black and clear the colour buffer.
	glClearColor(0, 0, 0, 1);
	
	// ...etc etc...

[!else]
	//	Do your OpenGL drawing here. Don't forget to call glFlush at the end!
[!endif]
}

Nothing too clever here, depending on the state of the checkbox, we'll either get some example drawing code, or just a comment reminding the user to add their own.

Give It A Try

This is fairly straightforward, but there is not much good documentation for AppWizards, especially for Visual Studio .NET 2008. At this early stage, you may well find problems with this code on different machines - but let me know and I will fix them as they crop up.

Updates

It'll take a while to update the code on the CodeProject. Until then, I have put a small page on one of my websites to store this code - so updates will be available from SharpGL - Other Resources before they are online at the CodeProject.

History

  • 14th January, 2009 - First revision of the article written

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)