Lecture 11: Interactive Programs with Callbacks and Menus CITS 3003 Graphics & Animation Slides: E. Angel and D. Shreiner: Interactive Computer Graphics 6E © Addison-Wesley 2012 Breakdown of Lectures 1. Introduction & Image Formation 2. Programming with OpenGL 3. OpenGL: Pipeline Architecture 4. OpenGL: An Example Program 5. Vertex and Fragment Shaders 1 6. Vertex and Fragment Shaders 2 7. Representation and Coordinate Systems 8. Coordinate Frame Transformations 9. Transformations and Homogeneous Coordinates 10. Input, Interaction and Callbacks 11. More on Callbacks 12. Mid-semester Test Study break 13. 3D Hidden Surface Removal 14. Mid term-test solution and project discussion 15. Computer Viewing 16. Shading 17. Shading Models 18. Shading in OpenGL 19. Texture Mapping 20. Texture Mapping in OpenGL 21. Hierarchical Modelling 22. 3D Modelling: Subdivision Surfaces 23. Animation Fundamentals and Quaternions 24. Skinning 4 Content • How to build interactive programs using GLUT callbacks o Mouse o Keyboard o Reshape o Idle • Introduce menus in GLUT 5 Using a Pointing Device (Mouse) • Mouse Event: When one of the mouse buttons is depressed or released or the scroll wheel is moved. glutMouseFunc() • Motion Event: When the mouse is moved within the window with one of the buttons depressed. glutMotionFunc() • Passive Motion Event: When the mouse is moved within the window without a button being held down. glutPassiveMotionFunc() 6 The mouse callback glutMouseFunc(mymouse); void mymouse(GLint button, GLint state, GLint x, GLint y) • The parameters passed to the function are: • button - which mouse button (GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON) caused the event • state - state of that button (GLUT_UP, GLUT_DOWN) • x, y – the mouse click position (in pixels) in the window 7 The mouse callback glutMouseFunc(mymouse); void mymouse(GLint button, GLint state, GLint x, GLint y) • The parameters passed to the function are: • button - which mouse button (GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON) caused the event • state - state of that button (GLUT_UP, GLUT_DOWN) • x, y – the mouse click position (in pixels) in the window 8 Mouse Scroll/Wheel • button – GLUT_LEFT_BUTTON – GLUT_MIDDLE_BUTTON – GLUT_RIGHT_BUTTON • state – GLUT_UP – GLUT_DOWN • Wheel is still a button – button == 3 (scroll forward) – button == 4 (scroll backwards) – every scroll registers as a “button down + button up” – you may use only one of them (up or down) 9 Mouse Positioning (x, y) • The position on the screen window is usually measured in pixels with the origin at the top-left corner – When the window is refreshed, it is done from top to bottom • OpenGL uses a world coordinate system with the origin at the bottom left corner. Thus, – you must invert the y coordinate passed to your callback function by the height of the window – i.e., y = h – y; – x remains the same (0,0) h w 10 How to obtain the window size • To invert the y position we need to know the window height • Note that the window height value can change during program execution • We can use a global variable to keep track of the window height value • glutGet(GLUT_WINDOW_WITDH) glutGet(GLUT_WINDOW_HEIGHT) 11 Terminating a program • We can also use the simple mouse callback: • Note that we ignore the x and y parameters in the example above. void mymouse(int btn, int state, int x, int y) { if (btn == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) exit(0); } 12 Using the mouse position • In the next example, we show how to draw a small square at the location of the mouse each time the left mouse button is clicked 13 Example 1: Drawing squares at clicked location of cursor void mymouse(int btn, int state, int x, int y) { if (btn == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) exit(0); if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN) drawSquare(x, y); } void drawSquare(int x, int y) { y = h-y; /* invert y position */ points[i] = vec2(x+size, y+size); points[i+1] = vec2(x-size, y+size); points[i+2] = vec2(x-size, y-size); points[i+3] = vec2(x+size, y-size); i += 4; } Note that in the drawSquare function, variable h is a global variable storing the height (in pixels) of the window. h, size, and i are 3 global variables. 14 x,y x+size,y+size Using the motion callback • We can draw squares (or anything else) using the motion callback – glutMotionFunc(mymotion); • We can also draw squares without depressing a button using the passive motion callback – glutPassiveMotionFunc(mypassivemotion); 15 Example 2: Drawing a triangle by specifying 3 vertices int w, h; //the width and height of window int count = 0; void mymouse(int btn, int state, int x, int y) { if (btn == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) exit(0); if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { y = h-y; points[count].x = (float) x/(w/2)-1.0; points[count].y = (float) y/(h/2) – 1.0; count++; } if (count == 3) { glutPostRedisplay(); count = 0; } } 16 Using the keyboard callback glutKeyboardFunc(mykey); void mykey(unsigned char key, int x, int y) Parameters passed to the mykey callback function are: • key – the ASCII code of the key depressed • x, y - and mouse location at the time the key was pressed •Example: void mykey(unsigned char key, int x, int y) { if (key == ’Q’ | key == ’q’) exit(0); } 17 Special and Modifier Keys • GLUT defines the special keys in glut.h – Function key 1: GLUT_KEY_F1 – Up arrow key: GLUT_KEY_UP – e.g., if (key == GLUT_KEY_F1 …… • Can also check whether one of the modifiers – GLUT_ACTIVE_SHIFT – GLUT_ACTIVE_CTRL – GLUT_ACTIVE_ALT is depressed by glutGetModifiers() This allows emulation of a three-button mouse with one- or two-button mice 18 Reshaping the window • We can reshape and resize the display window by pulling the corner of the window • What happens to the display? • The window in the application must be redrawn • There are three possibilities: 1. We can display the whole world but force it to fit in the new window (this can alter the aspect ratio). 2. We can display part of the world. 3. We can display the scale the whole world to fit in the window and keep the aspect ratio. 19 Reshape possiblities • Three reshape possibilities original Default reshaped 20 The Reshape callback glutReshapeFunc(myreshape); void myreshape(int w, int h) • Parameters passed to the function: w, h – the width and height of new window (in pixels) • What happens when the window is resized: o A redisplay is posted automatically at end of execution of the callback o GLUT has a default reshape callback but you probably want to define your own 21 The Reshape callback (cont.) Suppose that the our original window is 500 (width) x 500 (height) pixels and the clipping volume is: left=-0.2, right=0.2, bottom=-0.2, top=0.2, near=2.0, far=20.0. void reshape(int w, int h) { glViewport(0, 0, w, h); //glOrtho(left,right,bottom,top,near,far) glOrtho(-0.2*(float)w/(float)h, 0.2*(float)w/(float)h, -0.2, 0.2, 2.0, 20.0); } Note that near and far clipping planes should be positive; otherwise the clipping volume would be taken as behind the camera. No need to call glutPostRedisplay( ) here (see previous slide) 22 right left far top viewer The Reshape callback (cont.) Same setting as described on the previous slide. What does the following reshape callback function do? void reshape(int w, int h) { glViewport(0, 0, w, h); //glOrtho(left,right,bottom,top,near,far) if (w > h) glOrtho(-0.2*(float)w/(float)h, 0.2*(float)w/(float)h, -0.2, 0.2, 2.0, 20.0); else glOrtho(-0.2, 0.2, -0.2*(float)h/(float)w, 0.2*(float)h/(float)w, 2.0, 20.0); } 23 https://www.youtube.com/watch?v=V87lLvKscIY The Idle Callback • Invoked when there are no other events. Its default is the null function pointer. • Uses: – continue to generate graphical primitives through a display function while nothing else is happening – to produce an animated display. • In main, we specify an idle callback, – glutIdleFunc(idle); 24 Example : Idle Callback Void display() { // update/recalculate x, y, z locations of vertices // based on previous x, y, z locations and/or time } void idle() { glutPostRedisplay(); //sometimes you just have to call the redisplay //because what needs to change is already in there } 25 Toolkits and Widgets • Most window systems provide a toolkit or library of functions for building user interfaces that use special types of windows called widgets • Widget sets include tools such as o Menus o Slidebars o Dials o Input boxes But toolkits tend to be platform dependent • GLUT provides a few widgets including menus 26 Menus • GLUT supports pop-up menus o A menu can have submenus • Three steps for setting up a menu: 1) Define entries for the menu 2) Define action for each menu item o Action carried out if an entry is selected 3) Attach menu to a mouse button 27 Defining a simple menu • In the main or init function: menu_id = glutCreateMenu(mymenu); glutAddmenuEntry(”clear Screen”, 1); glutAddmenuEntry(”exit”, 2); glutAttachMenu(GLUT_RIGHT_BUTTON); entries that will appear when right button is pressed identifiers clear screen exit name of the callback functiona unique ID returned by glut 28 Menu actions • Example of a simple menu callback function: • Note when each menu is created, a unique id is returned by glut • To add a submenu, use glutAddSubMenu: glutAddSubMenu(char *submenuName, int submenuId) • To add a menu entry, use glutAddMenuEntry: glutAddMenuEntry(char *entryname, int entryID) • To attach the current menu, use glutAttachMenu. void mymenu(int id) { if (id == 1) glClear(); if (id == 2) exit(0); } 29 Menu – an example // submenu for two light sources int lightMenuId = glutCreateMenu(lightMenu); glutAddMenuEntry("Move Light 1", 11); glutAddMenuEntry("Change RGB of Light 1", 12); glutAddMenuEntry("Move Light 2", 21); glutAddMenuEntry("Change RGB of Light 2", 22); // submenu for camera int cameraMenuId = glutCreateMenu(cameraMenu); // add these submenus to the main menu glutCreateMenu(mainMenu); glutAddSubMenu("Light sources", lightMenuId); glutAddSubMenu("Camera", cameraMenuId); glutAttachMenu(GLUT_RIGHT_BUTTON); Put these lines of code in an init function Callback functions Always create the submenus before the main menu 30 Menu – an example // callback function for the light menu void lightMenu(int id) { switch (id) { case 11: // action for moving light 1 case 12: // action for changing RGB of light 1 ... } } // callback function for the camera menu void cameraMenu(int id) { ... } 31 Further Reading “Interactive Computer Graphics – A Top-Down Approach with Shader-Based OpenGL” by Edward Angel and Dave Shreiner, 6th Ed, 2012 • Sec. 2.7 Control Functions • Sec. 2.11 Adding Interaction up to Sec 2.11.4 The Idle Callback • Sec 2.12 Menus • C++ code in the Chapter04 - Chapter09 folders 32
欢迎咨询51作业君