Last edited Oct 8, 2003
This project serves as an introduction to OpenGL and its basic drawing functions. You may use any development environment you like to write your programs, but you must provide us with a way of compiling your program (i.e. a makefile if you chose to use the Linux/Unix environment or a project directory if you want to use Windows and Visual C++, and obviously your source code). This homework assignment will illustrate
This project is relatively simple. It is meant as an introduction to OpenGL, .... and some. The project description is rather lengthy to provide you with more than enough information to complete the homework while minimizing confusion. PLEASE READ THROUGH THIS ENTIRE DOCUMENT AND THE DOWNLOADABLE CODE PROVIDED TO YOU, BEFORE YOU START WRITING / ADDING YOUR CODE. This is to ensure you get an overview of what is going on, and more importantly to understand the program flow and design prior to going into the computer lab.
Download and compile the source code we have written for you to create an OpenGL window with simple menu support. The source code illustrates the use of the OpenGL toolkit to create and open an OpenGL drawing window (as discussed in class), and the creation of context menus. The drawing code is left to you to develop and implement. You should by now, have the basic knowledge of how an OpenGL window is created and the various callback routines used to handle redrawing and resizing the window. This knowledge will be extended by adding user input to an OpenGL program via context menus, mouse, and keyboard events. Context menus are menus which are accessible to the user, by "right-clicking" the mouse button within the OpenGL window created. This homework assignment involves drawing basic geometric primitives (shapes) onto the drawing window. The context menu provides the user with options which your program will need to determine what geometric primitive to draw, and what color to use. You will start with the downloaded code, and extend its functionality to be able to draw all the objects listed within the context menu. Currently, the downloaded code does not do any drawing.
By clicking the left mouse button within the OpenGL window, the selected geometric primitive (selected via the context menu) should be drawn onto the screen around the current mouse pointer position. How this is accomplished will be explained further below. After you successfully downloaded and compiled the provided source code, execute the program and navigate the mouse pointer anywhere within the OpenGL window and click the right mouse button. A context menu should "pop-up" that looks similar to:
Note there are menu items that allow the user to select a geometric primitive (line, triangle, square, pentagon, circle) to draw, and a sub-menu labeled "color" to allow the user to select one of four colors to use (red, green, blue, white) to draw the primitive. By left clicking the mouse within the OpenGL window, the selected object should be drawn. We will define the size of each object in a moment. Initially, when the program first executes you should set the default primitive (object to draw) to a line and the default color to red. Your program should behave as a "State Machine" in regards to menu item selection, much like OpenGL does. If you don't know what this means you probably:
1) Don't go to lecture 2) Don't pay attention during lecture 3) Were asleep during the lecture ..... and shame on you.
Conveniently, there are global variables defined near the top of the provided program that holds "State" information.
/*-------------------------------------------------------------------------------
// Global variables
//-------------------------------------------------------------------------------*/
PRIMATIVE_TYPE gObjects[MAX_OBJECTS]; /* holds objects to draw */
int gNumObjects; /* num objects to draw */
int gCurrentObject;
float gCurrentRed, gCurrentBlue, gCurrentGreen;
int gPosX, gPosY;
The variable gCurrentObject corresponds to the object to be drawn, and the variables gCurrentRed, gCurrentGreen, gCurrentBlue reflect the RGB value to use when drawing the object. These variables are set when the user selects a context menu item. Note, the RGB variables should take on values between 0.0 and 1.0, while gCurrentObject should take on constant values between 5 and 9 as defined by #define constants near the top of the provided program. i.e.
/*----------------------------------
// Object and Color IDs (Menu IDs)
//----------------------------------*/
........
#define LINE 5
#define SQUARE 6
#define PENTAGON 7
#define CIRCLE 8
#define TRIANGLE 9
Because these variables are global, you can use them in any function defined inside the file containing there definition. Any time the user selects a menu item you should change the values contained in the above variables appropriately, and all shapes drawn subsequently should use the selected properties.
To draw the selected object, the user should just simply left-click the drawing area. Use the coordinates of the mouse pointer, at the time the window is left-clicked, as the center point for the object to be drawn. The acquisition of the mouse pointer coordinates will be discussed shortly. The user should be allowed to fill the window with the various objects (maximum of 20 objects), and erase the screen when the menu item titled appropriately enough "Erase" is selected. If the user attempts to draw more than the maximum number of 20 objects, all object currently in the drawing window should be erased and only the new object should be drawn. Basically, this is simulating a restart of the program. The quit menu item allows the program to exit, and has already been implemented for you. A sample run of a completed program should look something like:
Now for some program specifics. Lines drawn by the user, should all have a length of 100 pixels. The midpoint for the line should be the point where the user left-clicks the mouse. Each new line should be drawn such that the slope is chosen randomly (more on this below) when the line is first created, but retains this slope on each redraw of the screen. Each simple regular polygon (i.e. triangle, square, pentagon, circle), must be drawn such that the distance between the center point and any vertex is 50 pixels. Recall that a simple regular polygon is defined as a connected convex closed shape constructed from a sequence of straight lines that are equidistant. Polygons are encountered so frequently in computer graphics that they are generally referred to as N-gons. The general N-gon is a regular polygon with N-sides and/or N-vertices. The vertices are connected by straight line segments (also known as a polyline). So, a 3-gon would be an equilateral triangle, 4-gon a square, 5-gon a pentagon, etc. Let the number of sides/vertices for the polygon be denoted as N. Then a circle is just a glorified N-gon if we let the number of vertices or sides (N) go to infinity. This can easily seen by considering first the case of a 6-gon (hexagon, N=6) centered about the origin:
The vertices lie equidistant from each other and the radial line connecting each vertex is separated every 60 degrees about its parent circle. If the parent circle has radius R and is centered about the origin then all the vertices of the hexagon must lie on the parent circle. In fact, for any vertex denoted by point Pi above, the coordinates of Pi given by the 2-tuple (xi,yi) are found by the relation
xi = R*cos(a), yi = R*sin(a), where a = 2pi/N and i = 0 ...N-1
Connecting the vertices by a line segment generates the hexagon..
1) Modify the window title so that your name is contained in the windows title bar. In addition, the window size should be set to 640 x 480, and the initial position set to (100,100) in screen coordinates. Compile and run the example code provided to you to make sure it runs before making any changes. If all goes well you just earned yourself 30 points.
2) The functions InitMenu() and MenuHandler( int id) creates and handles the context menu events using the OpengGL glut framework. The InitMenu function creates the context menus and associates numeric constants to each menu item. Note each menu item is added using the glut function glutAddMenuEntry() in the order in which it will appear. The numeric value associated with each menu item is passed to the function MenuHandler via an integer variable called "id" each time a menu item is selected by the user. This mechanism of passing a menu id allows the program to determine which menu item was selected by the user. The numeric menu id constants are #define near the top of the program. Function MenuHandler() is fairly simple. It receives a menu id and determines which menu item was selected by a switch statement. If an object is selected by the user then a global variable called gCurrentObject is set to reflect the object chosen. Similarly if a specific drawing color is desired, then the switch statement sets global variables gCurrentRed, gCurrentGreen, gCurrentBlue, that reflects the RGB value to use to draw the object. You can use these global variables to determine what type of object to draw and the color to use.
3) The function myMouseFunc() handles events associated with mouse clicks. myMouseFunc() is called by OpenGL anytime the user clicks the mouse over the OpenGL drawing window. It is registered to handle these events using the glut toolkit function as follows :
glutMouseFunc(myMouseFunc);
The function prototype for myMouseFunc() is:
void myMouseFunc( int button, int state, int mouseX, int mouseY);
parameters mouseX and mouseY are the pixel coordinates of the mouse pointer at the time the mouse button was depressed. The coordinates are relative to the upper-left corner of the window. Knowing the current window height in pixels (gCur_Win_Height) we can translate the pixel coordinates so that they are relative to the bottom left corner of the window. The coordinates are are stored in the global variables gPosX and gPosY, which are again available for your use in your program. The button and state parameter passed to function myMouseFunc() define which mouse button was depressed and the current state of the mouse button. For example, when OpenGL calls myMouseFunc() the value for button and state will be set to one of the following predefined constants:
button = {GLUT_RIGHT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_LEFT_BUTTON}
state = {GLUT_UP, GLUT_DOWN}
For the program at hand, we will only care about left mouse button down events (i.e button=GLUT_LEFT_BUTTON and state=GLUT_DOWN). In myMouseFunc() the if statement checks for this case.
4) Implement a function that handles the drawing of a line within the OpenGL drawing window. Each object that must be drawn to the screen will be described through a structure defined as
typedef struct {
int type;
float red;
float green;
float blue;
int x0, y0;
float ang;
} PRIMATIVE_TYPE ;PRIMATIVE_TYPE gObjects[MAX_OBJECTS]; /* objects to draw */
Structure PRIMATIVE_TYPE consists of elements named type, red, green, blue, x0, y0, and ang that define a primitive objects type (line, square, triangle, etc.), RGB value, center point, and random line slope respectively. An array of PRIMATIVE_TYPE named gObjects is defined immediately after the structure definition to hold information about each object that needs to be drawn to the OpenGL window. You can access each element of gObjects as follows:
gObjects[i].type = LINE;
which means gObjects ith structure array element named type is set to LINE. With this in mind ......
Start by editing the function myMouseFunc(). Recall that this function gets passed arguments (mouseX and mouseY) of the current mouse position when the user left-clicks the mouse, and stores it in global variables (gPosX, gPosY). Use these coordinates to draw a line which extends 100 Pixels in length, with (gPosX, gPosY) as the midpoint for the line. Additionally, the slope for the line should be generated randomly using the standard C function rand() using the following code.
gObjects[gNumObjects].ang = (rand()*2.0*PI)/(RAND_MAX +1);
Don't forget to #include the appropriate header file that defines the function rand(), or your program won't compile. The value returned by rand() is a random number that must be normalized and scaled to a value between 0 to 2*Pi. Note, the random angle and the current values of gCurrentObject , gCurrentRed, gCurrentGreen, gCurrentBlue, and (gPosX, gPosY) needs to be stored as an element of the gObjects array for later use in redraw events. Increment the value gNumObjects to reflect that a new object has been inserted into the gObjects array. Last but not least, call the function glutPostRedisplay() to force OpenGL to call our Display() function and begin drawing the elements contained in gObjects array.
The Display() function is pretty straight forward. It clears the drawing window, and then calls the function DrawObjects() only if gNumObjects is greater than zero. The function DrawObjects() should loop through the elements contained in gObjects and call function DrawLine() if the object is a line or function DrawNGon() otherwise.
Useful OpenGL command for drawing a line are (given line endpoint x1,y1,x2,y2)
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
Make sure that the point left-clicked by the user is the midpoint for the line. Again, the line drawn should a total length of 100 pixels.
5) Implement the function DrawNGon() to draw the selected polygons onto the drawing window. For the circle, initially use 40 vertices to approximate its shape. The parent circle for all the N-gons should have a radius of 50 pixels.
6) Implement the erase option to erase the screen via the context menu. In addition, design and implement the code to also erase the screen when the user goes beyond the maximum objects condition.
7) Comment your code. It is your job to show us you know what you are doing ...... not the other way around.
8) If you are unsure about how the program should run .... Download the Windows version of our completed program executable. Note, the executable DOES NOT implement the code to align the triangle, square, and pentagon.
With that ...... good luck on the homework assignment ...
Points will be assigned as follows:
Window creation of appropriate size, position, and title | 30 |
Drawing code for shapes (lines, triangle, square, pentagon, circle) 10 pts each | 50 |
Implementation of erase option | 10 |
Check for maximum objects drawn condition | 10 |
Extra Credit | 20 |
POTENTIAL TOTAL | 120 |
For those who finish early and would like to earn extra credit, implement code to draw the triangle, square, pentagon, and circle so that
1) The triangle and pentagon are drawn with there apex pointed vertically. (+6 points)
2) The square is drawn so that the top and bottom sides are horizontal. (+6 points)
3) Implement a keyboard event handler that checks for the keystrokes i (the letter i) and I (capital I). If i is selected the number of vertices to draw all (current and future) circles should be reduced by 5. If I is selected the number of vertices to draw circles should be increased by 5. The number of circle vertices should never go below 5 and above 60. (+8 points)
Turnin procedure: You need to follow this turnin procedure as closely as possible!
1) Make a COPY of your project on the local drive. Open the local copy under visual C++. Select the menu item build->clean. This deletes object files and executable from the project directory, making the project directory as small as possible.
2) Make a zip archive of your "cleaned" project directory. Call your zip archive USERNAME_hw2.zip, where USERNAME is your actual username that you use to login to your class account. Note the underscore between USERNAME and hw2.zip.
3) Login to machine iacs5.ucsd.edu, and binary ftp this zip file into your class home directory (/home/hp/iacs5/me152f/yourUSERNAME. Note, DO NOT MAKE A SUB DIRECTORY TO PUT ZIP FILE IN. Leave the zip file in your home directory (top level). type ls to make sure you transferred the file correctly. Log off.
4) DELETE THE LOCAL COPY.
DUE DATE: Oct. 17, 2003