Tutorial 1: Introduction to Visual Studio and OpenGLWnd

This tutorial introduces using Visual Studio and using OpenGL.  The goal is to introduce features of the environment that you may not have experience with in the past.  It is not a large-scale tutorial.  We will be utilizing a superclass I provide called COpenGLWnd.  This class allows a window in Visual Studio to have OpenGL content.

The specific features that will be introduced are:

Event Driven Programming

You should be aware that programming in any GUI environment is VERY different from the programming you've done on the command line.  The main difference is that the environment is Event Driven.  This means that everything you will do (well, for now) will be done in response to a message from Windows.  This may be indicated by function call or by a message handler you will supply.  The major ramification of this is that you will not write loops that get input and then process it.  It's a very different world, so keep an open mind.

Notice:  This is the way programming is done, be it Windows, X-Windows, or the Macintosh.  Command-line programs are for utilities and beginner programmers!

Model/Render Programming

There are two parts of any computer graphics application.  The Model describes what you are going to draw.  The Renderer converts that to an image.  You will utilize OpenGL to create a Renderer for your model.  Note that your renderer may be very specific at first and your models very simple.  As an example, your model might be "angle", where angle is the current rotation angle of a square.  Your renderer will know that it needs to draw a square at that angle.

Suggest Exercise

When you are done with that tutorial, it is suggested that you do the following task:

Create a program that displays a blue 5 pointed star.  Your program must allow you to define the x,y location of the center of the star and its size (from the center to a point, effectively the radius of the star).  It must allow you to rotate the star to any angle.  The location, angle, and size are to be input via a dialog box.  You will also have a menu option that will rotate the star 10 degrees counterclockwise.  

Notice:  Do not use any OpenGL rotation operations to accomplish the rotation (it's not that easy, anyway).  Compute the vertices yourself directly.  I will give this hint:  If you think of a corner as a certain distance from the center and at an angle of p * 360/5 degrees plus the rotation, you should be able to easily figure out the x,y location of the corner.

As a special challenge:  Draw the star as a solid color.

Creating your project

We will be utilizing Visual Studio 2005 in this course.  See the suggested software page for more details.  All of the software discussed here is available for free to MSU computer science students.

First, you need to start Visual Studio 2005.  You'll find it in the Start menu under Visual Studio 2005.  Note that there is now only ONE Visual Studio, not separate systems for different languages.  This assignment assumes Visual Studio 2005.  When you have the program started, select File/New/Project.  In the New Project dialog box, select the Visual C++ project type and MFC Application.  You need to give your project a name and a location.  You may name it what you wish, but for now I would suggest naming it "Tutorial".  Enter the project name and location into the dialog box.

Press Ok.  You'll be presented with the MFC Application Wizard.  Select Application Type and choose:  Single Document and uncheck "Document/View architecture support".  You can now press Finish.  Note that we will not be using the multiple document interface (only one OpenGL window on the screen at a time) and we'll not be using the Document/View structure because we'll only allow one view.  

Your project is now completed and ready to compile.  

About the User Interface

You'll see several things on the screen.  To the left is the class/resource/file browser.  On the right is a large multiple document interface (MDI) area for editing.  On the bottom are information windows.  Press the ClassView tab and expand "Tutorial classes".  This is a list of all C++ classes for your application.  The classes are:

Compile and Running

As created by Visual C++, this application is ready to compile and run (but it won't do much, yet).  Select Build/Build Tutorial.exe.  This will compile your program.  Then you can select Build/Execute Tutorial.exe.  Note that you have menus and can create windows, etc.  Exit the program.

Installing CWndOpenGL

We want to utilize OpenGL in your window.  It takes quite a bit of code to get OpenGL fired up under windows.  Fortunately, I've already written it all and put into a superclass you can utilize.  Go to Installing and Using CWndOpenGL and follow the instructions on installing CWndOpenGL, then return here when you are done.

Drawing Something

The function that is called to draw is CChildView::OnGLDraw.  In the ClassView window, open up the CChildView class and double-click on the OnGLDraw member function.  It will pop up in the right side ready for your brilliant additions.  You probably created a simple function that just clears the screen to black when you installed the class library.  We'll add to that, now.

The OnDraw function has a single parameter passed to it:  pDC of type CDC *.  A CDC object is the "Display Context".  A display context records the state of the mechanism for drawing on the screen in the Windows API.  It knows what the current font is, what the current line drawing width and color is, and other characteristics of drawing.  Because of this, most functions for drawing are members of this class as you will see.  We won't be using the Windows API, so this variable may not be used this term at all.

Add the following code in OnGLDraw after the glClear:

   // Simple rectangle example
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0.0,   // left
           1.0,   // right
           0.0,   // bottom
           1.0,   // top
           1.0,  // near
           -1.0);  // far

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   glColor3d(1., 0., 0.);

   glBegin(GL_POLYGON);
      glVertex2d(0.25, 0.25);
      glVertex2d(0.75, 0.25);
      glVertex2d(0.75, 0.75);
      glVertex2d(0.25, 0.75);
   glEnd();

Compile and run your program.  The window will display a red rectangle.  So, what did this do?

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0.0,   // left
           1.0,   // right
           0.0,   // bottom
           1.0,   // top
           1.0,  // near
           -1.0);  // far

This configured the window for 2D drawing.  We'll keep this tutorial in 2D for now.  I have specified the lower left corner coordinates as 0,0 and the upper right corner as 1,1.  Near means we won't draw anything with a Z value less than -1.  Far means we won't draw anything with a Z value greater than 1.  Basically, we are setting up for 2D drawing.  We'll come back to what most of the code means later.  Suffice it to say you can now draw.

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

This is another thing we'll come back to later.  For now, just do as I say...

   glColor3d(1., 0., 0.);

Bet you figured out what this does.  It set the color to RED.  The parameters are Red, Green, and Blue.  The maximum value is 1.0.  Minimum is 0.0.

   glBegin(GL_POLYGON);
      glVertex2d(0.25, 0.25);
      glVertex2d(0.75, 0.25);
      glVertex2d(0.75, 0.75);
      glVertex2d(0.25, 0.75);
   glEnd();

And, this draws a polygon.  How about drawing a few other things:

glBegin(GL_LINES);
   glVertex2d(0.1, 0.1);
   glVertex2d(0.8, 0.22);
   glVertex2d(0.78, 0.9);
   glVertex2d(0.2, 0.9);
glEnd();

This draws two lines.  The first line is from the first vertex to the second.  The next line is the third vertex to the fourth.

Try:

glBegin(GL_LINE_STRIP);
   glVertex2d(0.1, 0.1);
   glVertex2d(0.8, 0.22);
   glVertex2d(0.78, 0.9);
   glVertex2d(0.2, 0.9);
glEnd();	

You'll get something different here.  What is it?

BTW, feel free to change the colors if you get bored with red.

Try GL_POINTS and GL_QUADS.  Look up the documentation for glBegin to see how they are used.

Putting something in our document

We're going to add some things to our program, now.  I want to create a simple graphical model that knows where to draw a line.  We'll add ways to change these values momentarily.

We want to add the following private member variables to CChildView:

Why the m_?  It is a common convention in Microsoft code that member variables are prefixed with m_.  This is a great habit to get into and will be required in this class.  Then it's clear in your code when you're working with a member variable as opposed to a local variable. 

To add member variables to an existing class in VC++, right click on the class in the class browser.  Select Add Member Variable.  We'll enter these variables, all of them private.  Do that now.  You can also double-click on the class and jump right to the header and enter them manually.  Ensure that your program compiles before proceeding.

Now, we need to go to the constructor for CChildView and set default values for these variables.  In the class browser, expand CChildView and doubleclick on CChildView(), the default constructor.  Enter the following code:

   m_linefmx = 0.5;
   m_linefmy = 0.5;
   m_linetox = 0.2;
   m_linetoy = 0.55;

Ensure that this compiles.

Display the Line

Now, let's display the line.  You may want to change the color before you draw it.  You should be able to figure out how to do this part yourself...

Creating a Menu Option

Now, we're going to create a menu option to change the line end.  This will entail several steps.

Adding the Menu Resource

In the right pane where the class browser is, select the ResourceView tab and expand Tutorial resources.  If there is no ResourceView tab, select the View/Resource View menu option.  Resources are additional elements of your program not expressed as actual code.  They include the menu, icons, etc.  We're going to edit a menu, so expand the Menu item. 

You'll find one menu resource here.  IDR_MAINFRAME is the only menu for this application.  Double-click it.   You'll see a windows with a menu.  To the right of "help" in the menu is a box.  Double-click on that box and enter "&Tutorial stuff" for the Caption.  Close the box.  You've just created a new menu.  (The & underlines the L and makes it accessible via the keyboard).

Now, click on Tutorial Stuff and click on the empty box in the Tutorial stuff submenu.  Enter:  &Line end to 0.1, 0.1.  Compile and run your program.  Note that the new menu exists and the new menu option is included, but disabled.  That's because we're not processing the option. 

Aside:  The menu editor is very easy to use.  Feel free to move items around to the order you like by just dragging them.

Adding a menu handler

When you select a menu option in Windows, a message is sent to your program.  You have to decide where to handle that message.  In this example, the menu option changes the values in the view, so I'll handle it in the document.  Right click on the menu option in the resource (menu) editor.  Choose Add Event Handler.. from this menu.  This brings up the message handler dialog box.  The message we will handle is COMMAND, the message sent when the menu option is selected.  We want it handled by CChildView, so select that class from the class list.  Press the Add and Edit button. 

You are jumped right to the function and can fill in the functionality.  Enter the following code:

   m_linetox = 0.1;
   m_linetoy = 0.1;

   Invalidate();

So, what does this do?  Well, it's obvious that it changes the values of the variables.  Then, it calls Invalidate() to tell  the window that the underlying document has changed.  This forces it to redraw itself.

Compile and run this.  Try the menu option. You can easily add another menu option to move the line end elsewhere and see what that does.

Creating a Dialog Box

The most common way to modify parameters is through dialog boxes.  We'll now create a simple dialog box we can use to set both ends of the line we are drawing.  There are four steps in create a dialog box:

  1. Create the dialog box resource
  2. Create a class associated with the dialog box
  3. Map variables in the class to controls in the dialog box
  4. Instantiate the dialog box in response to a menu

Create the dialog box resource

Select the Project/Add Resource menu.   In the dialog box click dialog and press New.  You now have a new, empty dialog box.  First, we need to give this dialog box an identifier value.  In the lower right window, you can see the properties for the dialog box.  Change the ID to  IDD_LINEDLG.  Then enter for the caption:  "Where the line?"  (you'll have to scroll up to find the Caption property).   

Now, we need to add controls to the dialog box.  On the screen you can select Toolbox to see a palette of available controls.  If you move the mouse over these controls you'll see a description of them in the help area of the VC++ window (bottom line).  Select "Edit control" and draw an edit box in your dialog box.  Do this four times for the line end values we'll be entering.  Finally, select "Static text" and enter captions for each of these controls.

Aside:  There is lots of material on-line about using the dialog box editor.  We'll only concern ourselves here with getting a dialog box on the screen.  Consult the on-line help or the scribble tutorial for more information on making your dialog box look nice.

The last thing we need to do is associate an ID with each control in the dialog box using the properties page.  The default ID is something like IDC_EDIT3, which is not descriptive.  To set the ID, right click each control and name them IDC_FMX, IDC_FMY, IDC_TOX, and IDC_TOY.  When you are done, you can close the dialog box editor.

Create a class associated with the dialog box

Now we'll create a C++ class that is associated with this dialog box.  Choose the Project/Add Class menu option.  This template will be MFC class.  Select that and press Open.  Enter CLineDlg as the name.  For the base class, choose CDialog.  The Dialog ID should already be set to IDD_LINEDLG.  Click Finish. 

If you go to ClassView now, you'll see your new class already exists.  VC++ created two files:  LineDlg.h and LineDlg.cpp for you automatically. 

By the way:  You'll notice that I used a leading capital C for my class name. This is a convention Microsoft uses throughout MFC and I recommend that you consider it for your projects as well. If you will use the leading C, MFC will automatically figure out the file name to use for the class header and implementation modules as the class name without the C and with the appropriate suffix. If you don't do this, it will just use the class name. This is why I called the class "CLineDlg" rather than just LineDlg.

Map variables in the class to controls in the dialog box

Now, we want to associate variables in the dialog box class with the controls in the dialog box.  This is much easier than filling in and managing the controls yourself.  Edit the dialog box again (double-click on IDD_LINEDLG in the ResourceView).  Right-click on the first edit control and select Add Variable.  Set the values in the dialog box like this:

When set, press Finish.  So the same for IDC_FMY and the other two. 

Aside:  What this has done is create public variables in the CLineDlg class.  When the dialog box is displayed the first time, the boxes are filled in from these variables.  When the Ok button is pressed, these variables are set to the (potentially edited) values from the dialog box controls.

Instantiate the dialog box in response to a menu

Now we want to create a menu option that brings up the dialog box.  First, create a new menu option in the Tutorial Stuff menu called "Line Dialog" and associate a handler in CChildView with the menu option.  Get that working first.  Scroll up if you need to review how to do this step.

Now, add this code to the menu handler you just created:

   CLineDlg dlg;

   dlg.m_fmx = m_linefmx;
   dlg.m_fmy = m_linefmy;
   dlg.m_tox = m_linetox;
   dlg.m_toy = m_linetoy;

   if(dlg.DoModal() == IDOK)
   {
      m_linefmx = dlg.m_fmx;
      m_linefmy = dlg.m_fmy;
      m_linetox = dlg.m_tox;
      m_linetoy = dlg.m_toy;

      Invalidate();
   }

Then, go to the head of the file and add: 

#include "LineDlg.h"

Compile this and run it.  You should now be able to use the dialog box to set parameters of the line on the screen.

Fixing the Aspect Ratio problem

One problem with the way I've set up this problem is that the window is 0,0 to 1,1, no matter what shape it is.  Since I ask you to draw a 5 pointed star, that will be tough to do if your window aspect ratio change all over the place.  Try resizing your image and see what happens.

Replace:

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0.0,   // left
           1.0,   // right
           0.0,   // bottom
           1.0,   // top
           1.0,  // near
           -1.0);  // far

with:

int width, height;
GetSize(width, height);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, // left
	1.0, // right
	0.0, // bottom
	GLdouble(height) / GLdouble(width), // top
	1.0, // near
	-1.0); // far

Now, try this and see what happens.