CPSC 324, Fall 2002
Lab 2: OpenGL 2D
FOR THIS LAB, we turn to graphics programming with OpenGL. For now, we will stick to simple two-dimensional drawing, without transformations.
For our OpenGL programming , we will be using QT, a GUI toolkit that is the basis for KDE (although the KDE desktop layers a bunch of extra functionality onto QT). QT is available for UNIX, Windows, and MacOS X, although it is only free on UNIX. You will not need to know much about QT to complete the assignments in this course, since you will be using starter programs that already written, and you will mostly be writing OpenGL code. The only exception to this is that you might have to add some menu commands to the programs.
There are two exercises for this lab. They will be due next week:
Exercise 1: The first exercise is to create a picture or animation using OpenGL. You should copy the opengl-starter folder from the directory /home/cs324 into your homework directory (or a subdirectory of your homework directory) with commands such ascd homework cp -a /home/cs324/opengl-starter lab2ex1
This command gives the name lab2ex1 to your copy of the folder. One of the files in the folder is named gl_canvas.cc. This is the only file you need to modify. You can draw your picture in the function named GLCanvas::paintGL(). You probably also want to change GLCanvas::initializeGL() to use a different background color. If you want to do an animation, do not use OpenGL transformations to do it. You don't know enough to use them correctly. Just use the value of the variable frameNumber in your drawing commands. Most likely, you will actually want to use something like frameNumber%30 to get an animation that repeats every 30 frames. Try to make a picture that looks like something!
To compile your program, just give the command make. The name of the compiled program will be prog. (The name is set in the file named Makefile.) Your program will have a menu command named "Save PNG Image." Use a command to save a copy of your picture, and put it on your web page. If you write an animation, you should save several frames from the animation and put them on your page. You don't have to turn in your program for this exercise. If I want to see it, I will look in your homework directory.
Exercise 2: The folder /home/cs324/opengl-paint contains a simple painting program. The user can draw lines by dragging the mouse. The lines can be either black or red. The user can also clear the drawing. You should copy this folder into your homework directory.
For the second exercise, you should add features to this program. As a minimum: You should add more colors. You should make it possible to draw filled rectangles and outlines of rectangles, in addition to lines. You should make it possible to draw different styles of lines, such as wide lines and dotted lines. Doing all this perfectly will give you a B level program. To get an A, you will have to implement some interesting additional features, such as a text tool or the ability to choose any RGB drawing color. Please discuss your ideas with me!
You should make a representative drawing with your program and put it on your Web page. You should turn in a printout of your gl_canvas.cc file (preferably one made with the a2ps command.)
The rest of this lab worksheet contains some information about the opengl-paint starter program and about menus and mouse events in QT.
About QT and Linux OpenGL Programming
This course is not about QT, and you will not be tested on it. However, you will need to know a little bit about it to do the programming exercises. However, there is a very complete set of documentation for QT that you can find at http://math.hws.edu/local/gt_docs/, if you are interested.
QT consists largely of a collection of classes that represent GUI components such as windows, buttons, and menus. One of these classes is QGLWidget, which represents a drawing area which can be used for OpenGL graphics. In order to actually use QGLWidget, you have to create a subclass, define at least a paintGL() method in that subclass to do the drawing, and add an object belonging to that subclass to a QT Window. This is all done in the opengl-starter and opengl-paint projects. The name of the QGLWidget subclass in these projects is GLCanvas, and it is defined in the header file gl_canvas.h and in the implementation file gl_canvas.cc. These projects also add some menus to the window, and the GLCanvas class is defined to include support for mouse events, keyboard events, and timer events (which are used for animation).
In addition to gl_canvas.h and gl_canvas.cc, the project folders contain the C++ files main.cc, mainwin.h, and mainwin.cc. You will not have to change the main program file, main.cc; it just opens a window. You will have to work with mainwin.h and mainwin.cc only if you want to add new menus or modify the ones that are already defined. All other programming will done in gl_canvas.h and gl_canvas.cc or in new C++ files that you add to the project.
To compile your programs, you can use the make command. The make command looks for a file named Makefile and takes its instructions from that file. (The commands for compiling OpenGL and QT programs are complicated by the fact that you have to tell the compiler where to find certain resources, and you really wouldn't want to have to keep retyping them!) You will not have to make any changes to the Makefile unless you add new files to a project. The executable produced by the Makefile is named prog. You can change this if you want by editing the line in the Makefile that reads "executable = prog". After you compile, you will notice some new files that end with the extension ".o". These are compiled versions of individual files that need to be "linked" together to produce a complete program.
To add new menus or menu commands, you need to know a little bit about QT's slightly bizarre way of handling events. It uses a system called "signals and slots." Signals are, essentially, events, and slots are functions that are written to handle events. It's actually pretty easy, once you get used to it, but it requires some funny syntax, as well as an extra step in the compilation (which is handled by the Makefile).
To handle menu commands, you can either define a separate function to handle each command, or you can define a single function to handle an entire menu. For the first case, you need a function that has no parameters; for the second case, you need a function that has a single parameter that tells it which of the commands in the menu were selected. The functions must be defined as members of a class. The opengl-starter project uses the first method only, while opengl-paint uses both methods. For the most part, you can just imitate the code that you find in these projects, but I will give you a few guidelines here.
First of all remember that whenever you add a function to a class, you have to both add the function to the class definition in the ".h" file and also define the function in the ".cc" file. For a function that will be used as a "slot", the function must be defined in a section of the class definition labeled "public slots:".
The menus themselves are created in the function "void GLWin::makeMenuBar()" in the file mainwin.cc. To add new commands or menus, you should add code to this function. A menu is created and added to the menu bar with statements such as:QPopupMenu *fileMenu = new QPopupMenu(); bar->insertItem("File",fileMenu); // "File" appears in menu bar
If you are handling a menu command with its own function, add that command to the menu with a statement such as:fileMenu->insertItem("Quit",this,SLOT(doExit()),CTRL+Key_Q);
The first parameter is the text that appears in the menu. The second parameter is the object where the slot function is defined. (It doesn't necessarily have to be "this", but I suggest you stick to that unless you know more about QT than you will learn from me.) The third parameter has the form SLOT(functionName()) where fucntionName() is the function that will be called when the user selects the command from the menu. It must be defined as a "public slot" in the class definition. The fourth parameter, which is optional, gives an accelerator (that is, a keyboard equivalent) for the command.
If you want to use a single function to handle an entire menu, add commands to the menu with statements of the form:colorMenu->insertItem("Red",2);
The second parameter is an integer ID that identifies the command. Each command in the menu must have a different ID code. This parameter is sent as a parameter to the function that handles the menu, so that function must be defined with a single parameter of type int. Then, to set up the event handling, you need a statement of the form:connect(toolsMenu,SIGNAL(activated(int)),this,SLOT(doToolsMenu(int)));
where toolsMenu is a pointer to the menu object and doToolsMenu is the name of the function that will handle the menu events.
You can find examples for all this in the opengl-paint project. You'll notice that many of my event-handling functions simply call functions in the GLCanvas class. This might seem a little redundant, but it keeps the various responsibilities (menu-handling vs. graphics) separate. If you want to know more about using menu commands, you could look at the documentation for the QPopupMenu class at http://math.hws.edu/local/qt_docs/qpopupmenu.html.
To handle mouse or keyboard events, you need to provide definitions for one or more of the following functions in the file gl_canvas.cc:void GLCanvas::mousePressEvent(QMouseEvent *evt) void GLCanvas::mouseReleaseEvent(QMouseEvent *evt) void GLCanvas::mouseMoveEvent(QMouseEvent *evt) void GLCanvas::keyPressEvent(QKeyEvent *evt) void GLCanvas::keyReleaseEvent(QKeyEvent *evt)
You will find examples of mouse handling in opengl-paint and of keyboard handling in opengl-starter. Note that as soon as you start adding code to gl_canvas.cc, you will probably have to add variables and functions to the GLCanvas class. Remember that variables and function declarations have to go in the header file, gl_canvas.h, while the functions must be defined in the implementation file, gl_canvas.cc. (Don't forget to add "GLCanvas::" to the beginning of the function name in gl_canvas.cc.)
If you have not already worked with "mouse dragging" in your programs, there is one thing that is a little hard to get used to: The code to handle a dragging operation is scattered through three functions. One function, mousePressEvent, sees the initial mouse button press; it is responsible for initializing any variables that will be needed throughout the drag operation. The second function, mouseMoveEvent, is called over-and-over as the user drags the mouse; it is responsible for any updating that must be done when the mouse moves. Finally, mouseReleaseEvent is called when the user releases the the mouse; it is responsible for clean-up.
(Animation, by the way, is handled by the function "void GLCanvas::timerEvent(QTimerEvent *evt)", which is called over-and-over while the animation is running. The timer that generates the timer events is started in the function "void GLCanvas::startAnimation()" and is stopped in "void GLCanvas::startAnimation()". There will probably be no need for you to modify any of these functions, except that you might want to change the time per frame in startAnimation().)
David Eck, September 2002