340 likes | 536 Views
CSC 2141 Introduction to Computer Graphics. More on Drawing in OpenGL: Examples. Recall: Callbacks. Programming interface for event-driven input Define a callback function for each type of event the graphics system recognizes This user-supplied function is executed when the event occurs
E N D
CSC 2141 Introduction to Computer Graphics More on Drawing in OpenGL: Examples
Recall: Callbacks • Programming interface for event-driven input • Define a callback function for each type of event the graphics system recognizes • This user-supplied function is executed when the event occurs • GLUT example: glutMouseFunc(mymouse) void mymouse(GLint button, GLint state, GLint x, GLint y) mouse callback function
GLUT Event Loop • Recall that the last line in main.c for a program using GLUT must be glutMainLoop(); which puts the program in an infinite event loop • In each pass through the event loop, GLUT • looks at the events in the queue • for each event in the queue, GLUT executes the appropriate callback function if one is defined • if no callback is defined for the event, the event is ignored
Using the mouse position • In the next example, we draw a small square at the location of the mouse each time the left mouse button is clicked • This example does not use the display callback but one is required by GLUT; We can use the empty display callback function mydisplay(){} • main()function same as before
Globals and myInit() Glsizei wh =500, ww=500; /* window dimensions */ GLfloat size = 3.0; /* one-half of side length of square */ myInit(void) { /* set viewing conditions */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D (0.0, (GLdouble) ww, 0.0, (GLdouble) wh ); glMatrixMode(GL_MODELVIEW); /* adjust viewport */ glViewport(0, 0, ww, wh); glClearColor(0.0, 0.0,0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); }
Drawing squares at cursor location void mymouse(int btn, int state, int x, int y) { if(btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN) exit(0); /*terminate the program through OpenGL */ if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN) drawSquare(x, y); } void drawSquare(int x, int y) /* (x,y) is the center */ { y=wh-y; /* invert y position */ glColor3ub( (char) rand()%256, (char) rand ()%256, (char) rand()%256); /* a random color */ glBegin(GL_POLYGON); glVertex2f(x+size, y+size); glVertex2f(x-size, y+size); glVertex2f(x-size, y-size); glVertex2f(x+size, y-size); glEnd(); glFlush(); }
Positioning • The position in the screen window returned by callback functions is with respect to the origin at the top-left corner (GLUT convention) • Consequence of refresh done from top to bottom • OpenGL uses a world coordinate system with origin at the bottom left • Must invert y coordinate returned by mouse callback using the height of the window • y = wh – y; (0,0) y wh h-y ww
Obtaining the window size • To invert the y position we need the window height • Height can change during program execution • New height returned to reshape callback • Track with a global variable
Recall: The Reshape callback glutReshapeFunc(myreshape) void myreshape( int w, int h) • Returns width and height of new window (in pixels) • A redisplay is posted automatically at end of execution of the callback • The reshape callback is good place to put viewing functions because it is invoked when the window is first opened
Recall: The Reshape callback • Do we redraw all the objects that were in the window before it was resized? • We need a mechanism to store and recall them • Typically done by encapsulating all drawing in the display callback and reposting the display redraws all. • In this example: our drawing is interactive based on mouse input and unless we store the squares drawn, we cannot recall them • Let’s choose to clear the window if resized.
Recall: The Reshape callback • What do we do if the aspect ratio of the new window is different from that of the old window? • No single answer • Distortions may be okay • Or not • then set the viewport such that it has the same aspect ratio as the drawing area. • Part of the window may not be used. • In this example, we clear the window when resized so no distortion to old squares. New squares are drawn with the same fixed size.
Example Reshape • This reshape preserves shapes by making the viewport and the idealized drawing window have the same aspect ratio void myReshape(GLsizei w, GLsizei h) { ww = w; /* update window dimensions */ wh = h; /* adjust clipping box */ glMatrixMode(GL_PROJECTION); /* switch matrix mode */ glLoadIdentity(); gluOrtho2D (0.0, (GLdouble) w, 0.0, (GLdouble) h ); glMatrixMode(GL_MODELVIEW); /* return to modelview mode */ /* adjust viewport */ glViewport(0, 0, w, h); /* clear the window each time it is resized */ glClear(GL_COLOR_BUFFER_BIT); glFlush(); }
Using globals • The form of all GLUT callbacks is fixed • void mydisplay() • void mymouse(GLint button, GLint state, GLint x, GLint y) • Must use globals to pass information to callbacks float size; /*global */ void mydisplay() { /* draw something that depends on size }
Recall: Using the keyboard glutKeyboardFunc(myKeyboard) void myKeyboard(unsigned char key, int x, int y) • Returns ASCII code of key depressed and mouse location void myKeyboard(unsigned char key, int x, int y) { if(key == ‘Q’ || key == ‘q’) exit(0); }
Special and Modifier Keys • GLUT defines the special keys in glut.h • Function key 1: GLUT_KEY_F1 • Up arrow key: GLUT_KEY_UP • if(key == ‘GLUT_KEY_F1’ …… • Can also check of one of the modifiers • GLUT_ACTIVE_SHIFT • GLUT_ACTIVE_CTRL • GLUT_ACTIVE_ALT is depressed by glutGetModifiers()
Using the motion callback • We can draw squares (or anything else) continuously as long as a mouse button is depressed by using the motion callback • glutMotionFunc(drawSquare) • We can draw squares without depressing a button using the passive motion callback • glutPassiveMotionFunc(drawSquare)
Changing and disabling callback functions • We can change most callback functions during program execution by specifying a new callback function • We can also disable a callback function by setting it to NULL glutIdleFunc(NULL);
Using the idle callback • The idle callback is executed whenever there are no events in the event queue • glutIdleFunc(myidle) • Useful for animations void myidle() { /* change something */ t += dt glutPostRedisplay(); } Void mydisplay() { glClear(); /* draw something that depends on t */ glutSwapBuffers(); }
Example animation: rotating square x=cosƟ y=sinƟ The point lies on a unit circle regardless of the value of Ɵ
myDisplay() void myDisplay() { glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); thetar = theta * (2 * 3.14159) / 360.0; /* convert degrees to radians */ glVertex2f(cos(thetar), sin(thetar)); glVertex2f(-sin(thetar), cos(thetar)); glVertex2f(-cos(thetar), -sin(thetar)); glVertex2f(sin(thetar), -cos(thetar)); glEnd(); glutSwapBuffers(); /* double buffering */ }
Change Ɵ as the program runs… • In main() function specify callback glutIdleFunc(myIdle); • And, define callback function as void myIdle () { theta += 2; if (theta >= 360.0) theta -= 360.0; glutPostRedisplay(); }
One further change.. • Turn on and off the rotation feature by mouse input • Register mouse callback as glutMouseFunc(myMouse); • Define mouse callback as void myMouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON)&&(state == GLUT_DOWN) glutIdleFunc(myIdle); if (button ==GLUT_RIGHT_BUTTON)&&(state == GLUT_DOWN) glutIdleFunc(NULL); }
Too fast? • Use a timer callback instead • Which in turn will execute the display callback at a fixed rate (e.g. n frames per second) glutTimerFunc(1000/n, myTimer, 0); • But no support for cancelling a timer callback. • instead you can ignore a callback based on its value.
Try running this with single buffering • Do you notice partial display of rotated square?
Multiple Windows • GLUT supports multiple windows id = glutCreateWindow(“Second window”); And select this as the current window by glutSetWindow(id); • You can make this window have different properties by invoking glutInitDisplayMode before glutCreateWindow • Each window can set its own callback functions • Callback registrations refer to the current window.
Menus • GLUT supports pop-up menus • A menu can have submenus • Three steps • Define entries for the menu • Define action for each menu item • Action carried out if entry selected • Attach menu to a mouse button
Defining a simple menu • In main.c glutCreateMenu(myMenu); glutAddMenuEntry(“clear Screen”, 1); glutAddMenuEntry(“exit”, 2); glutAttachMenu(GLUT_RIGHT_BUTTON); clear screen exit entries that appear when right button depressed identifiers
Menu actions • Menu callback • Note each menu has an id that is returned when it is created void myMenu(int id) { if(id == 1) glClear(); if(id == 2) exit(0); }
Menu actions • Hierarchical menus are allowed • Add submenus by glutAddSubMenu(char *submenu_name, int submenu_id) int sub_menu; sub_menu=glutCreateMenu(size_menu); //add to the current menu glutAddMenuEntry(“Increase square size”,2); glutAddMenuEntry(“decrease square size”,3); glutCreateMenu(top_menu); glutAddMenuEntry(“Quit”, 1); glutAddSubMenu(“Resize”, sub_menu); glutAttachMenu(GLUT_MIDDLE_BUTTON);
Text • We often want to control size, color and font • Two ways in OpenGL • 1. Stroke Text: • constructed as other graphics primitives • Use vertices to draw line segments or curves outlining the character • Advantage: define once and apply transformations to generate any size and orientation • Disadv: defining a full character set is complex…
Text • 2. Raster text • Simple and fast • Character is defined as rectangular array of bits (0s or 1s) called bit blocks or bitmap • A raster character can be placed in frame buffer directly. When you overlay a bitmap on the frame buffer, the pixels that correspond to 1s are set to the current color. • You can increase character size only by replicating pixels. • Larger characters: blocky appearance • Transformations like rotation do not make sense (can’t rotate pixel positions!)
Raster text glutBitmapCharacter(GLUT_BITMAP_8_BY_13, c) • GLUT_BITMAP_8_BY_13: is a set of bitmaps predefined in GLUT. It’s a fixed width font, i.e. all characters have the same width. • c is the integer equivalent of an ASCII character • Above function places c at the “current” raster position (part of state) and automatically advances the current position by the character width after the character is drawn. • Position can be altered by glRasterPos*(rx, ry); glutBitmapWidth(GLUT_BITMAP_8_BY_13, c) • Returns the width of the character c in pixels
Typical function to display a string void bitMapString( float x, float y, char s[]) { int i = 0; glRasterPos2f( x, y); while (s[i] != '\0') { glutBitmapCharacter( GLUT_BITMAP_8_BY_13, s[i]); ++i; } }
Stroke Text glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, c) • You can use a predefined font as GLUT_STROKE_MONO_ROMAN • Or, you can define your own fonts! • Be careful: the way this function works may affect OpenGL state (the transformation matrices, you might need to save them) • For now use raster text.