You are on page 1of 17

You can download glut from here: http://www.xmission.com/~nate/glut.

html

download glut-3.7.6-bin.zip glut.h in C:\Program Files\Microsoft Visual Studio 8\VC\include\GL\ glut32.lib in C:\Program Files\Microsoft Visual Studio 8\VC\lib glut32.dll in C:\Windows\System32
In the Menu of VC++ go through to -> project -> settings -> Link and add (do not remove the others!) the following libraries to the Object/libary modules line: glut32.lib glu32.lib opengl32.lib glaux.lib

Simple OpenGL Program Initialization The first thing we need to do is call the glutInit() procedure. It should be called before any other GLUT routine because it initializes the GLUT library. The parameters to glutInit() should be the same as those to main(), specifically main(int argc, char** argv) and glutInit(&argc, argv), where argcp is a pointer to the program's unmodified argc variable from main. Upon return, the value pointed to by argcp will be updated, and argv is the program's unmodified argv variable from main. Like argcp, the data for argv will be updated. The next thing we need to do is call the glutInitDisplayMode() procedure to specify the display mode for a window. You must first decide whether you want to use an RGBA (GLUT_RGBA) or color-index (GLUT_INDEX) color model. The RGBA mode stores its color buffers as red, green, blue, and alpha color components. The forth color component, alpha, corresponds to the notion of opacity. An alpha value of 1.0 implies complete opacity, and an alpha value of 0.0 complete transparancy. Color-index mode, in contrast, stores color buffers in indicies. Your decision on color mode should be based on hardware availability and what you application requires. More colors can usually be simultaneously represented with RGBA mode than with color-index mode. And for special effects, such as shading, lighting, and fog, RGBA mode provides more flexibility. In general, use RGBA mode whenever possible. RGBA mode is the default. Another decision you need to make when setting up the display mode is whether you want to use single buffering (GLUT_SINGLE) or double buffering (GLUT_DOUBLE). Applications that use both front and back color buffers are double-buffered. Smooth animation is accomplished by rendering into only the back buffer (which isn't displayed), then causing the front and back buffers to be swapped. If you aren't using annimation, stick with single buffering, which is the default.

Finally, you must decide if you want to use a depth buffer (GLUT_DEPTH), a stencil buffer (GLUT_STENCIL) and/or an accumulation buffer (GLUT_ACCUM). The depth buffer stores a depth value for each pixel. By using a "depth test", the depth buffer can be used to display objects with a smaller depth value in front of objects with a larger depth value. The second buffer, the stencil buffer is used to restrict drawing to certain portions of the screen, just as a cardboard stencil can be used with a can of spray paint to make a printed image. Finally, the accumulation buffer is used for accumulating a series of images into a final composed image. None of these are default buffers. We need to create the characteristics of our window. A call to glutInitWindowSize() will be used to specify the size, in pixels, of your inital window. The arguments indicate the height and width (in pixels) of the requested window. Similarly, glutInitWindowPosition() is used to specify the screen location for the upper-left corner of your initial window. The arguments, x and y, indicate the location of the window relative to the entire display.

Creating a Window To actually create a window, the with the previously set characteristics (display mode, size, location, etc), the programmer uses the glutCreateWindow() command. The command takes a string as a parameter which may appear in the title bar if the window system you are using supports it. The window is not actually displayed until the glutMainLoop() is entered. Display Function The glutDisplayFunc() procedure is the first and most important event callback function you will see. A callback function is one where a programmer-specified routine can be registered to be called in response to a specific type of event. For example, the argument of glutDisplayFunc() is the function that is called whenever GLUT determines that the contents of the window needs to be redisplayed. Therefore, you should put all the routines that you need to draw a scene in this display callback function. Reshape Function The glutReshapeFunc() is a callback function that specifies the function that is called whenever the window is resized or moved. Typically, the function that is called when needed by the reshape function displays the window to the new size and redefines the viewing characteristics as desired. If glutReshapeFunc() is not called, a default reshape function is called which sets the view to minimize distortion and sets the display to the new height and width. Main Loop The very last thing you must do is call glutMainLoop(). All windows that have been created can now be shown, and rendering those windows is now effective. The program will now be able to handle events as they occur (mouse clicks, window resizing, etc). In addition, the registered

display callback (from our glutDisplayFunc()) is triggered. Once this loop is entered, it is never exited! Geometric Objects Point, Lines and Polygons Each geometric object is described by a set of vertices and the type of primitive to be drawn. A vertex is no more than a point defined in three dimensional space. Whether and how these vertices are connected is determined by the primitive type. Every geometric object is ultimately described as an ordered set of vertices. We use the glVertex*() command to specify a vertex. The '*' is used to indicate that there are variations to the base command glVertex(). Some OpenGL command names have one, two, or three letters at the end to denote the number and type of parameters to the command. The first character indicates the number of values of the indicated type that must be presented to the command. The second character indicates the specific type of the arguments. The final character, if present, is 'v', indicating that the command takes a pointer to an array (a vector) of values rather than a series of individual arguements. For example, in the command glVertex3fv(), '3' is used to indicate three arguments, 'f' is used to indicate the arguments are floating point, and 'v' indicates that the arguments are in vector format. Points: A point is repesented by a single vertex. Vertices specified by the user as twodimensional (only x- and y-coordinates) are assigned a z-coordinate equal to zero. To control the size of a rendered point, use glPointSize() and supply the desired size in pixels as the argument. The default is as 1 pixel by 1 pixel point. If the width specified is 2.0, the point will be a square of 2 by 2 pixels. glVertex*() is used to describe a point, but it is only effective between a glBegin() and a glEnd() pair. The argument passed to glBegin() determines what sort of geometric primitive is constructed from the vertices. glBegin(GL_POINTS); glVertex2f(0.0, 0.0); glVertex2f(0.0, 3.0); glVertex2f(4.0, 3.0); glVertex2f(6.0, 1.5); glVertex2f(4.0, 0.0); glEnd(); Lines: In OpenGL, the term line refers to a line segment, not the mathematician's version that extends to infinity in both directions. The easiest way to specify a line is in terms of the vertices at the endpoints. As with the points above, the argument passed to glBegin() tells it what to do with the vertices. The options for lines includes: GL_LINES: Draws a series of unconnected line segments drawn between each set of vertices. An extraneous vertex is ignored.

GL_LINE_STRIP: Draws a line segment from the first vertex to the last. Lines can intersect arbitrarily. GL_LINE_LOOP: Same as GL_STRIP, except that a final line segment is drawn from the last vertex back to the first. With OpenGL, the description of the shape of an object being drawn is independent of the description of its color. When a paraticular geometric object is drawn, it's drawn using the currently specified coloring scheme. In general, an OpenGL programmer first sets the color, using glColor*() and then draws the objects. Until the color is changed, all objects are drawn in that color or using that color scheme.

Example: glBegin; glColor3f(1.0, 1.0, 0.0); // yellow glVertex2(-1.0, 1,0); glVertex2f(2.0, 2.0); glColor3f(1.0, 0.0, 0.0); // red glVertex2f(0.0, 0.0); glVertex2f(1.0, -1.0); glVertex2f(-2.0, -2.0); glEnd(); Polygons: Polygons are the areas enclosed by single closed loops of line segments, where the line segments are specified by the vertices at their endpoints. Polygons are typically drawn with the pixels in the interior filled in, but you can also draw them as outlines or a set of points. In OpenGL, there are a few restrictions on what constitutes a primitive polygon. For example, the edges of a polygon cannot intersect and they must be convex (no indentations). There are special commands for a three-sided (triangle) and four-sided (quadrilateral) polygons, glBegin(GL_TRIANGLES) and glBegin(GL_QUADS), respectively. However, the general case of a polygon can be defined using glBegin(GL_POLYGON). Example: glBegin(GL_POLYGON); glColor3f(1.0, 1.0, 0.0); // yellow glVertex2f(0.0, 0.0)</A; glVertex2f(0.0, 3.0)</A; glVertex2f(4.0, 3.0)</A;

glVertex2f(6.0, 1.5)</A; glVertex2f(4.0, 0.0)</A; glEnd();

Drawing 3-D Objects GLUT has a series of drawing routines that are used to create three-dimensional models. This means we don't have to reproduce the code necessary to draw these models in each program. These routines render all their graphics in immediate mode. Each object comes in two flavors, wire or solid. Available objects are:

glutWireSphere() or glutSolidSphere() glutWireCube() or glutSolidCube() glutWireTorus() or glutSolidTorus() glutWireIcosahedron() or glutSolidIconsahedron() glutWireOctahedron() or glutSolidOctahedron() glutWireTetrahedron() or glutSolidTetrahedron() glutWireDodecahedron() or glutSolidDodecahedron() glutWireCone() or glutSolidCone() glutWireTeapot() or glutSolidTeapot()

Transformations

A modeling transformation is used to position and orient the model. For example, you can rotate, translate, or scale the model - or some combination of these operations. To make an object appear further away from the viewer, two options are available - the viewer can move closer to the object or the object can be moved further away from the viewer. Moving the viewer will be discussed later when we talk about viewing transformations. For right now, we will keep the default "camera" location at the origin, pointing toward the negative z-axis, which goes into the screen perpendicular to the viewing plane. When transformations are performed, a series of matrix multiplications are actually performed to affect the position, orientation, and scaling of the model. You must, however, think of these matrix multiplications occuring in the opposite order from how they appear in the code. The order of transformations is critical. If you perform transformation A and then perform transformation B, you almost always get something different than if you do them in the opposite order. Scaling: The scaling command glScale() multiplies the current matrix by a matrix that stretches, shrinks, or reflects an object along the axes. Each x-, y-, and z-coordinate of every point in the object is multiplied by the corresponding argument x, y, or z. The glScale*() is the only one of the three modeling transformations that changes the apparent size of an object: scaling with values greater than 1.0 stretches an object, and using values less than 1.0 shrinks it. Scaling with a -1.0 value reflects an object across an axis. Translation: The translation command glTranslate() multiplies the current matrix by a matrix that moves (translates) an object by the given x-, y-, and z-values. Rotation: The rotation command glRotate() multiplies the current matrix that rotates an object in a counterclockwise direction about the ray from the origin through the point (x,y,z). The angle parameter specifies the angle of rotation in degrees. An object that lies farther from the axis of rotation is more dramatically rotated (has a larger orbit) than an object drawn near the axis. Display Lists A display list is a group of OpenGL commands that have been stored for later execution. When a display list is invoked, the commands in it are executed in the order in which they were issued. Most OpenGL commands can be either stored in a display list or issued in immediate mode, which causes them to be executed immediately. A display list must be bracked with the glNewList() and glEndList() commands. The argument for glNewList() is the unique name that identifies the list.

A display list contains only OpenGL commands. The values in the display list can't be changed and it is not possible to either add or remove commands from the list once it has been stored. You can delete the entire display list and create a new one, but you can't edit it. The command glNewList() specifies the start of a display list. The first parameter is a nonzero positive integer that uniquely identifies the display list. The second parameter specifies whether the OpenGL command should be executed immediately as well as placed in the display list (GL_COMPILE_AND_EXECUTE) or just added to the display list without executing them (GL_COMPILE). The comand glEndList(), obviously, specifies the end of a display list. After you have created a display list, you can execute it by calling glCallList(). With glCallList(), you can execute the same display list many times, call a display list from another routine, or mix calls to execute display lists with calls to perform immediate-mode graphics. Any changes in the current color and current matrix made during the execution of the display list remain in effect after it has been called. Therefore, manipulating the matrix stack may be required to change the state of the variables before executing a display list and then restoring these values after the list has been executed. glNewList(1,GL_COMPILE); glColor3f(1.0, 0.0, 0.0); // red glBegin(GL_TRIANGLES); glVertex2f(0.0, 0.0); glVertex2f(1.0, 0.0); glVertex2f(0.0, 1.0); glEnd(); glTranslate(1.5, 0.0, 0.0); // move object glEndList(); glNewList(2,GL_COMPILE); glColor3f(0.0, 1.0, 0.0); // blue glBegin(GL_QUAD); glVertex2f(0.0, 0.0); glVertex2f(1.0, 0.0); glVertex2f(1.0, 1.0); glVertex2f(0.0, 1.0); glEnd(); glTranslate(-1.5, 0.0, 0.0); // move object glEndList(); It is important to note that OpenGL commands are not necessarily executed as soon as they are issued. It is necessary to call the command glFlush() to ensure that all previously issued commands are executed. glFlush() is generally called at the end of a sequence of drawing commands to ensure all objects in the scene are drawn before beginning to accept user input. Color

Drawing on a computer screen is different from drawing o paper in that the paper starts out white, and all you have to do is draw the picture. On the computer, the memory holding the picture is usually filled with the last picture you drew, so you typically need to clear it to some background color before you start to draw the new scene. To change the background color, we call glClearColor() and specify the color we have chosen for the background. The default clearing color is (0,0,0,0) which is black. A subsequent call to glClear() will clear the specified buffers to their current clearing values. To clear the color buffer use the argument GL_COLOR_BUFFER_BIT. To set a color, use the command glColor3f(). It takes three floating point parameters which are between 0.0 and 1.0. The parameters are, in order, the red, green, and blue components of the color. You can think of these as specifying a "mix" of colors, where 0.0 means don't use any of this color and 1.0 means use all of that component. For example, glColor3f(1.0, 0.0, 0.0) makes the brightest red that the system can draw, with no green or blue components. All zeros makes black (an absence of colored light) and all ones makes white (a presence of all colored light). Eight commom colors and their commands are:

glColor3f(0.0, 0.0, 0.0); //black glColor3f(1.0, 0.0, 0.0); //red glColor3f(0.0, 1.0, 0.0); //green glColor3f(1.0, 1.0, 0.0); //yellow

glColor3f(0.0, 0.0, 1.0); //blue glColor3f(1.0, 0.0, 1.0); //magenta glColor3f(0.0, 1.0, 1.0); //cyan glColor3f(1.0, 1.0, 1.0); //white

Shading So far, we have applied just a single color to each polygon that we have drawn (flat shading). However, it is possible to specify a unique color at each vertex. OpenGL will smoothly interpolate the color between the vertices. This is known as Gouraud Shading (or smooth shading). OpenGL will linearly interpolate the RGB values for the pixel values along the edges between the vertices causing a gradual shift of color. glBegin(GL_QUADS); // draw 2 shaded polygons glColor3f(1.0, 0.0, 0.0); // color interpolation glVertex2f(-0.8, -0.8); // the four vertices are glColor3f(0.5, 1.0, 0.0); // red, green, cyan, and glVertex2f(0.5, 1.0, 0.0); // blue glColor3f(0.0, 1.0, 0.5); glVertex2f(-0.1, 0.8); glColor3f(0.0, 0.0, 1.0); glVertex2f(-0.8, 0.8); glColor3f(1.0, 1.0, 1.0); // intensity interpolation glVertex2f(0.1, -0.8); // the four vertices are glColor3f(0.7, 0.7, 0.7); // 1, 0.7, 0.5, and 0.1

glVertex2f(0.8, -0.8); glColor3f(0.5, 0.5, 0.5); glVertex2f(0.8, 0.8); glColor3f(0.1, 0.1, 0.1); glVertex2f(0.1, 0.8); glEnd(); Viewing Transformation Recall that to change the view of an object we could either move the object or move the viewer. We will do a series of transformations on the viewer's "camera" that are similar to the transformations done on an object. To start with, we will initialize the viewing matrix by loading it with the identity matrix, using the command glLoadIdentity(), and continue to combine it with new ones according to where we want to place the "camera". The command gluLookAt is used to indicate where the viewer is placed, where it is aimed, and which way is up. For example, gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); places the camera (or eye position) at the point (0,0,5), aims it towards the origin (0,0,0) and specifies the up-vector as (0,1,0). At this point, we should probably mention the concept of hidden surface elimination. This is the process of making objects that are in the forefront "hide" those behind them. Recall that we have a depth buffer that keeps track of the depth of each pixel of the objects in a scene. An objects depth value is the difference between the viewpoint and the object. If you want to use the depth buffer, you simply have to enable it by passing GL_DEPTH_TEST to glEnable() and remember to clear the depth buffer before you redraw each frame by using glClearDepth(). When the buffer is enabled the pixel that is displayed takes the color of the object with the smallest z value (depth), making that object appear closer than the other object(s). Projection Specifying the projection transformation is like choosing a lens for a camera. You can think of this transformation as determining the field of view and therefore which objects are inside it and, to some extent, how they should look. There are two basic types of projections provided for you by OpenGL, orthographic and perspective. Orthographic: Orthographic projection maps objects directly onto the screen without affecting their relative sizes. This projection is used mainly in architectural and computer-aided design applications, where the actual measurements of the objects are more important than how they might look. The command glFrustum() is used to set the projection transformation. The command glMatrixMode() is used, with the argument GL_PROJECTION is used to set up the matrix upon which this projection transformation (and subsequent transformations) is performed. Note that we use the glLoadIdentity() command to initialize the current projection matrix so that only the specified projection transformation(s) have an effect. Finally, we calle the command

glOrtho() to create an orthographic parallel viewing volume. The viewing volume is a box with the left, right, top, bottom, near and far edges specified in the glOrtho() command. When it is time to perform transformations on the models that we have created, we will use the same glMatrixMode() command with GL_MODELVIEW as the argument. This indicates that the succeeding transformations now affect the modelview matrix instead of the projection matrix. Perspective: We will use the perspective projection to get a more realistic rendering of an object. The object(s) will have the unmistakable characteristic of foreshortening: the further an object is from the camera, the smaller it appears in the final image. This is because the viewing volume of perspective projection is a frustum (a truncated pyramid whose top has been cut off by a plane parallel to its base). Objects that are closer to the apex of the pyramid appear smaller while objects closer to the base appear larger. The command to define this frustrum is glFrustum(), which takes the values for left, right, top, bottom, near and far edges. You could perform rotations or translations on this projection matrix to alter its orientation, by this is tricky and should be avoided. The same set of commands to set the projection glMatrixMode(GL_PROJECTION) and glLoadIndentity() should be used to set up the projection transformation, but instead of using the glOrtho() command the glFrustum() command is given. Manipulating the Matrix Stacks The modelview and projection matracies you've been creating, loading, and multiplying have been only the visible tips of their respective iceburgs. Each of these matracies is actually the topmost member of the modelview or projection matrix stacks, respectively. Suppose you want to draw the same object at four different locations. To make life much simpiler, we can use the same object description and successively translate it to each of the desired positions. Since the transformations are stored as matracies, a matrix stack provides an ideal mechanism for doing this sort of successive copying, translating, and throwing away. The command glPushMatrix() copies the matrix on the top of the matrix stack which was last referenced by the glMatrixMode() command. The matrix contents, therefore, are duplicated in both the top and the second-to-the-top matracies. This top matrix can then be translated and drawn, as desires, and ultimately destroyed using the glPopMatrix() command. This leaves you right where you were before and ready to repeat the process for the next version of the object. Light Light effects are very important in OpenGL, for without the use of lights, an 3-D object will still look 2-dimensional. OpenGL provides two types of light sources: directional and positional. A directional light source is considered to be an infinite distance away from the objects in the scene. Thus, its rays of light are considered parallel by the time they reach the object. A

10

positional light, in contrast, is near or within the scene and the direction of its rays are taken into account in lighting calculations. The command glLightfv() is used to specify the position of the light, regardless of whether it is directional or positional. It is also used to specify whether the light source has ambient color, diffuse color, specular color, or emissive color. Ambient: Light that has been scattered so much by the environment that its direction is hard to determine. Diffuse: Light that comes from one direction, so it's brighter if it comes squarely down on a surface than it it barely glances off the surface. Specular: Light that comes from a particular direction, and it tends to bounce off the surface in a preferred direction. Emissive: Light seems to be orginating from an object. You can create up to eight light sources. All of which have been internally defined as GL_LIGHT0, GL_LIGHT1, and so on. To create a light source, you must choose which light you want to use, by name, a position for the light source and certain parameters. Available parameters are color and quality. To define the position, you must supply a vector of four values (x,y,z,w). If the last value, w, is zero, the corresponding light source is a directional one, and the (x,y,z) values describe its direction. By default, the position for GL_POSITION is (0,0,1,0), which defines a directional lighe that points along the negative z-axis. The color components specified for lights means something different that for objects. For a light, the numbers correspond to a percentage of full intensity for each color. If the R, G, and B values for a light's color are all 1.0, the light is the brightest possible white. However, if the values are all 0.5, the color is still white, but only half the intensity, and therefore appears gray. If R=1, G=1 and B=0, the light has full red and full green with no blue, and therefore appears yellow. The forth parameter that is passed is an on-off switch, more or less. A value of 1 turns the light on, and a value of 0 turns the light off. OpenGL allows you to associate three different color-related parameters - GL_AMBIENT, GL_DIFFUSE, and GL_SPECULAR - with any particular light. The default is no ambient light, GL_AMBIENT(0.0,0.0,0.0,1.0). The light is on, but the color is black. Similarly, the default is for a diffuse light, GL_DIFFUSE(1.0,1.0,1.0,1.0) for GL_LIGHT0, which produces a bright, white light. The default values for the other seven lights, GL_LIGHT1,..., GL_LIGHT7 is (0.0,0.0,0.0,0.0). Lights are defined as a variable before they are passed to the glLightfv() command. Finally, you need to activate the light by using glEnable(GL_LIGHTING) and glEnable(GL_LIGHT0). For example, to create a diffuse light at position (1,1,1), you must do the following: GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0}; GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};

11

glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); OpenGL treats the position and direction of a light source just as it treats the position of a geometric primitive. In other words, a light source is subject to the same matrix transformations as a primitive. This means that you can change a light source's position or direction by changing the contents of the modelview matrix. (The projection matrix has no effect on a light's position or direction.) For example, to rotate a light or translate the light position so that the light moves relative to a stationary object, do the following: static GLdouble spin; void display(void) { GLfloat light_position[] = {0.0, 0.0, 1.5, 1.0}; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glPushMatrix(); glRotate(spin,1.0,0.0,0.0); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glPopMatrix(); glutSolidTorus(0.275, 0.85, 8, 15); glPopMatrix(); glFlush(); } Viewport Transformation Using our camera analogy, viewport transformation is the process of deciding on the size of our developed print. Do we want a wallet-sized photograph or a poster? By default, the viewport is to set the entire pixel rectangle of the window that is opened. You can use the glViewport() command, however, to choose a smaller drawing region. For example, you could divide the window to create a split-scene for multiple views in the same window. The command takes the x- and y-coordinate of the lower left corner of the viewport and the width and height of the viewport rectangle. Input Device and Interaction Menu GLUT supports simple cascading pop-up menus. They are designed to let a user select various modes within a program. The functionality is simple and minimalistic and is meant to be that way. Do not mistake GLUT's pop-up menu facility with an attempt to create a full-featured user

12

interface. It is illegal to create or destroy menus, or change, add, or remove menu items while a menu (and any cascaded sub-menus) are in use (that is, popped up). The popup menus can be arranged in a hierachy of menus, sub-menus, sub-sub-menus, and so on. Since a sub-menu identifier must be passed as an argument to the GLUT functions that create the parent menu, the menu must be created from the bottom up. For example, assume we want to create a main menu with entries Option A or Submenu1, where the sub-menu has entries of Options B, C, or Submenu2, where the sub-sub-menu has Options D and E. Submenu identifiers must be declared for each of our menu entries. This is a unique integer value will be assigned as the menus are called by glutCreateMenu(). Start with the lowest level menu and pass as an argument the callback function, menuCB, which defines menu selection options. To create the lowest level menu and its entries: sub2 = glutCreateMenu(menuCB); glutAddMenuEntry("Option D", 4); glutAddMenuEntry("Option E", 5); Then go up one more level and do the same thing for Submenu1: sub1 = glutCreateMenu(menuCB); glutAddMenuEntry("Option B", 2); glutAddMenuEntry("Option C", 3); glutAddSubMenu("SubMenu2", sub2); Finally, we need to go the same thing for the highest level menu: glutCreateMenu(menuCB); glutAddMenuEntry("Option A", 1); glutAddSubMenu("SubMenu1", sub1); glutAddMenuEntry("Quit", 6); These menu options can now be used in the callback function menuCB to process the users menu choice. void menuCB(int item) { switch (item) { case 1: / do whatever option A does break; case 2: / do whatever option B does ...

13

Mouse GLUT supports interaction with the computer mouse that is triggered when one of the three typical buttons is presses. A mouse callback fuction can be initiated when a given mouse button is pressed or released. The command glutMouseFunc() is used to specify the callback function to use when a specified button is is a given state at a certain location. This buttons are defined as either GL_LEFT_BUTTON, GL_RIGHT_BUTTON, or GL_MIDDLE_BUTTON and the states for that button are either GLUT_DOWN (when pressed) or GLUT_UP (when released). Finally, x and y callback parameters indicate the location (in window-relative coordinates) of the mouse at the time of the event. Just as with the menu options above, the callback function can use a switch statement with multiple variations of mouse buttons, actions and locations to do some pretty niffy things. For example, we could use the mouse to cause an object within certain x and y ranges to rotate 90 degrees to the right with a click of the right button, to rotate 90 degrees to the left with a click of the left button, and to flip upside down with a click of the middle button. Keyboard GLUT interaction using keyboard inputs is handled very similarly to those with the mouse. The command glutKeyboardFunc() is used to run the callback function specified and pass as parameters, the ASCII code of the pressed key, and the x and y coordinates of the mouse curor at the time of the event. Special keys can also be used as triggers. The key passed to the callback function, in this case, takes one of the following values (defined in glut.h).

GLUT_KEY_UP GLUT_KEY_RIGHT GLUT_KEY_DOWN GLUT_KEY_PAGE_UP GLUT_KEY_PAGE_DOWN GLUT_KEY_HOME GLUT_KEY_END GLUT_KEY_INSERT

Up Arrow Right Arrow Down Arrow Page Up Page Down Home End Insert

Animation As we have seen, the screen window will only be redrawn whenever the display callback function is involked. This occurs automatically when the window is resized or uncovered. You can explicitly force the display callback to be involked by issuing the command glutPostRedisplay(). To perform an animated simulation, we obviously want to continually update the window, however, we do not want to constantly issue a stream of glutPostRedisplay() calls. We need a mechanism for the window to be continually updated automatically.

14

There are two callback functions that are helpful in this regard: Idle Callback (glutIdleFunc()) and Timer Callback (glutTimerFunc()). The Idle Callback specifies a function that is involked whenever the system is not handling any other callbacks or events. While the Timer Callback specifies a function that is involked after a specified time period. When the Timer Callback function is involked, after a time interval, measured in milliseconds, an identifier is passed in as an argument. Note that the Timer Callback function is only involked once, after the specified time interval in the call to glutTimerFunc(). To have the timer callback involked automatically after each successive time interval, place a second call to glutTimerFunc() inside the Timer Callback function - causing reiterative calls. Most OpenGL implementations provide double-buffering - hardware or software that supplies two complete color buffers. One is displayed while the other is being drawn. When the drawing of a frame is complete, the two buffers are swapped, so the one that was being viewed is now used for drawing, and vice versa. With double-buffering, every frame is shown only when the drawing is complete; the viewer will never see a partially drawn frame. The code will specify a callback function to perform when idle. This should cause some kind of change of variable usually a change in angle (angle = angle + 2.0), for example. It should also include the command glutPostRedisplay(). This will cause the display function to be involked again with the new variable. The display function should include a command to glutSwapBuffers() to cause the next buffer to be created, and subsequently redrawn due to the glutIdleFunc() command. Selection Selection applications allow the user to identify objects on the screen and then to move, modify, delete, or otherwise manipulate these objects. OpenGL provides a selection mechanism that tells you which objects are drawn inside a specified region of the window. To use the selection mechanism you create a stack that holds each object selected and process each selection in turn. To begin the selection process, you must define an array to be used for the returned selection data. This is done by using the command: glSelectBuffer(), where the parameters are the size of the array and a pointer to an array where the data is put. When you are ready to start the selection process, you must switch to selection mode by issuing the glRenderMode() command, specifying GL_SELECT. The application then remains in selection mode until glRenderMode() is called again with GL_RENDER mode. The command returns an integer value that is the number of hits accumulated when the application switches back to render mode. Name Stacks The name stack holds the information that is returned to the program after the user selects a series of objects. To create a name stack, first initialize it with glInitNames(), which simply clears the stack, and then adds integer names to it while issuing corresponding drawing commands. To add a name to the top of the stack, you use the glPushName() command and to

15

remove a name from the top of the stack, you use the glPopName() command. To replace the name at the top of the stack with a different one, you use the glLoadName() command. For example to draw a scene with three objects: glInitNames(); glPushName(0); glPushMatrix(); // save the current transformation state /* create your desired viewing volume here glLoadName(1); drawObject1(); // call the procedure to draw object 1 glLoadName(2); drawObject2(); // call the procedure to draw object 2 glLoadName(3); drawObject3(); // call the procedure to draw object 3 glPopMatrix(); // restore to previous transformation state Calls to glPushName(), glPopName(), and glLoadName() are ignored if you're not in selection mode. So you might want to simplify your code by using these calls throughout your drawing code, and then use the same drawing code for both selection and normal rendering modes. Picking You can use the selection process to select specific objects for further manipulation. To do this, you use a special picking matrix in conjuction with the projection matrix to restrict drawing to a small region of the viewport, typically near the cursor. Then you allow for some form of input, such as clicking a mouse buttonm to initiate selection mode. With selection mode established and with the special picking matrix used, objects that are drawn near the cursor cause selection hits. Thus, during picking you're typically determining which objects are drawn near the cursor. Before calling the standard projection matrix (such as glOrtho() or glFrustum()), we need to use the utility routine gluPickMatrix() to multiply the specified projection matrix by a special picking matrix. The center of the picking region is passed as x and y values (in window coordinates), typically the cursor location. A width and height are also specified which define the pickingn region in screen coordinates. You can think of the width and height values as the sensitivity of the picking device. You should also specify a viewport. Use glGetIntegerv(GL_VIEWPORT, viewport); to get the current values for the viewport array. For example, to create a 5x5 pixel picking region near the cursor location, use gluPickMatrix((GLdouble) x, (GLdouble) y, 5.0, 5.0, viewport); You will probably want to save the contents of the current projection matrix before any manipulation, so the sequence of operations may look like this:

16

glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix( . . .); glFrustum(); // . . . draw scene for picking; perform picking; process picking glPopMatrix(); Hits In selection mode, we can use the name stack and mouse interaction to keep track of which objects are selected and process them accordingly. When you enter selection mode, OpenGL initalizes a pointer to the beginning of the selection array. We can then use mouse input values, such as GL_DOWN and GL_LEFT_BUTTON, to determine if a hit should be recorded and update the hit record with each mouse input. Each time a hit record is written into the array, the pointer is updated accordingly. The hit records are accumulated in the array until glRendermode(GL_RENDER) is called. As noted above, the return value for this command is the number of hits in the array. This can then be passed to another procedure, along with the array itself to act on the selected pieces.

17

You might also like