Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
45 views

Lecture 6 - Handling Input Events

The document discusses various callback functions in OpenGL for handling different types of input events. It describes functions like glutDisplayFunc to register a display callback, glutReshapeFunc to handle window resizing, glutKeyboardFunc and glutMouseFunc to handle keyboard and mouse input, and glutIdleFunc to perform background tasks when the system is idle. It provides examples of how to set up these callbacks on window initialization and use them to process user input and drive animation.

Uploaded by

Patience Karinga
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
45 views

Lecture 6 - Handling Input Events

The document discusses various callback functions in OpenGL for handling different types of input events. It describes functions like glutDisplayFunc to register a display callback, glutReshapeFunc to handle window resizing, glutKeyboardFunc and glutMouseFunc to handle keyboard and mouse input, and glutIdleFunc to perform background tasks when the system is idle. It provides examples of how to set up these callbacks on window initialization and use them to process user input and drive animation.

Uploaded by

Patience Karinga
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

Lecture 6

Handling input
events
glColor3f(0.0, 0.0, 0.0);
draw_object(A);
draw_object(B);
glColor3f(1.0, 0.0, 0.0);
draw_object(C);

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
Program Structure – Main
Function
1. Configure and open window
2. Initialize OpenGL
3. Register input callback functions
– Render
– Resize
– Input: keyboard, mouse, etc
4. Enter event processing loop
Handling Input Events
• You can use these routines to register callback commands that are invoked when specified events
occur.

glutReshapeFunc(void (*func)(int w, int h))


• Indicates what action should be taken when the window is resized.

glutKeyboardFunc(void (*func)(unsigned char key, int x, int y))


glutSpecialFunc(functionKeys);
and
glutMouseFunc(void (*func)(int button, int state,int x, int y))

• Allow you to link a keyboard key or a mouse button with a routine that's invoked when the key or
mouse button is pressed or released.

glutMotionFunc(void (*func)(int x, int y))


• Registers a routine to call back when the mouse is moved while a mouse button is also pressed.

glutIdleFunc(void (*func)(void))
• You can specify a function that's to be executed if no other events are pending - for example,
when the event loop would otherwise be idle .
• This routine takes a pointer to the function as its only argument.
• Pass in NULL (zero) to disable the execution of the function.
• Registering callbacks…

User-defined
// Register call backs. functions
glutDisplayFunc(display);
glutReshapeFunc(reshapeMainWindow);
glutKeyboardFunc(graphicKeys);
glutSpecialFunc(functionKeys);
glutMotionFunc(mouseMovement);
glutIdleFunc(idle);
What to do when there’s nothing to do…
Catching a mouse …
How to react on special keys (F1..F12, etc)
How to react on “ordinary” keys…
How to resize on resize event…
Knowing how to display the model
5
Program
#include <GL/glut.h>

Void main(int argc, char** argv)


{
int mode = GLUT_RGB|GLUT_SINGLE;
glutInitDisplayMode(mode);
glutInitWindowSize(500,500);
glutCreateWindow(“Simple”);
init();
glutDisplayFunc(display);
glutReshapeFunc(resize); Register your call back
glutKeyboardFunc(key); functions
glutMainLoop();
}
glutMainLoop()
#include <GL/glut.h>

Void main(int argc, char** argv)


{
int mode = GLUT_RGB|GLUT_SINGLE;
glutInitDisplayMode(mode);
glutInitWindowSize(500,500);
glutCreateWindow(“Simple”);
init();
glutDisplayFunc(display);
glutReshapeFunc(resize);
glutKeyboardFunc(key); The program goes into a
glutMainLoop(); infinite
} loop waiting for events
Keyboard
Event queue ….

MainLoop() Mouse

Window

Mouse_callback() Keypress_callback() window_callback()


{ { {
…. …. ….
{ { {

Event Queue
Events in OpenGL
Event Example OpenGL Callback
Function
Keypress KeyDown glutKeyboardFunc
KeyUp
Mouse leftButtonDown glutMouseFunc
leftButtonUp
Motion With mouse press glutMotionFunc
Without glutPassiveMotionFunc

Window Moving glutReshapeFunc


Resizing
System Idle glutIdleFunc
Timer glutTimerFunc

Software What to draw glutDisplayFunc


a) Reshape Function

Callbacks: reshapeMainWindow()
// Respond to window resizing, preserving proportions.
// Parameters give new window size in pixels.

void reshapeMainWindow (int newWidth, int newHeight)


{

}
glViewport Function
• glViewport: This function defines the current viewport
transform.
• It defines as a region of the window, specified by the
bottom-left position and a width/height.

e.g:
void reshape (int w, int h)
{
if(w < h)
glViewport(0, 0, (GLsizei) w, (GLsizei) w);
else
glViewport(0, 0, (GLsizei) h, (GLsizei) h);
}
Example
#include <GL/glut.h> void reshape(int w, int h)
#include <stdlib.h> {
glViewport (0, 0, (GLsizei) w, int main(int argc, char** argv)
static GLfloat spin = 0.0; (GLsizei) h); {
void init(void) glMatrixMode(GL_PROJECTION); glutInit(&argc, argv);
{ glLoadIdentity(); glutInitDisplayMode
glClearColor (0.0, 0.0, 0.0, 0.0); glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, (GLUT_DOUBLE | GLUT_RGB);
1.0); glutInitWindowSize (250, 250);
glShadeModel (GL_FLAT);
glMatrixMode(GL_MODELVIEW); glutInitWindowPosition (100, 100);
}
glLoadIdentity(); glutCreateWindow (argv[0]);
void display(void)
} init ();
{
void mouse(int button, int state, int glutDisplayFunc(display);
glClear(GL_COLOR_BUFFER_
x,
BIT); glutReshapeFunc(reshape);
int y)
glPushMatrix(); glutMouseFunc(mouse);
{
glRotatef(spin, 0.0, 0.0, 1.0); glutMainLoop();
switch (button) {
glColor3f(1.0, 1.0, 1.0); return 0;
case GLUT_LEFT_BUTTON:
glRectf(-25.0, -25.0, 25.0, 25.0); }
if (state == GLUT_DOWN)
glPopMatrix();
glutIdleFunc(spinDisplay);
glutSwapBuffers();
break;
}
case GLUT_MIDDLE_BUTTON:
if (state == GLUT_DOWN)
{
glutIdleFunc(NULL);
spin = spin + 2.0;
break;
if (spin > 360.0)
default:
spin = spin - 360.0;
break;
glutPostRedisplay();
}
}
}
b) Window Initialization in init()
•To maximize efficiency, operations that need only be called
once (s.a setting the background color and coordinate
system) are in a procedure called init().
•Operations to render (and possibly re-render) the scene are
in the display() procedure, which is the registered GLUT
display callback.

void init()
{
glClearColor (0.0, 0.0, 0.0, 1.0); black clear color
glColor3f(1.0, 1.0, 1.0);
opaque window
glMatrixMode (GL_PROJECTION);
glLoadIdentity (); fill/draw with white
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); viewing
}
volume
c) glutIdleFunc( idle )

• glutIdleFunc sets the global idle callback to


be func so a GLUT program can perform
background processing tasks or continuous
animation when window system events are not
being received.
• If enabled, the idle callback is continuously
called when events are not being received.
• The callback routine has no parameters.
• The current window and current menu will not be
changed before the idle callback.
• Programs with multiple windows and/or menus
should explicitly set the current
window and/or current menu and not rely on its
current setting.
• Note that if you decide to use a dedicated idle
callback function then you will need to add a call
to glutPostRedisplay() at the end of it.
• Else the idle function will be called again and
again but the render function will not.
• The idle function can also be exchanged with
the Timer Function.
Used mainly for animation and continuous update

void idle( void )


{
/* change something */
t += dt;
glutPostRedisplay();
}

Idle Callback
d)
glutKeyboardFunc
( my_key_events )
void glutKeyboardFunc(void (*func)(unsigned char
key, int x, int y));

• Sets the keyboard callback for the current window.


• When a user types into the window, each key press
generating an ASCII character will generate a
keyboard callback.
• The state of modifier keys such as Shift cannot be
determined directly; their only effect will be on the
returned ASCII data.
• The x and y callback parameters indicate the mouse
location in window relative coordinates when the key
was pressed.
Process user input

void my_key_events (char key, int x, int y )


{
switch ( key ) {
case ‘q’ : case ‘Q’ :
exit ( EXIT_SUCCESS);
break;
case ‘r’ : case ‘R’ :
rotate = GL_TRUE;
break;
}
}
e) Mouse Callback

• Captures mouse press and release events

• glutMouseFunc( my_mouse );

void myMouse(int button, int state, int x, int y)


{
if (button == GLUT_LEFT_BUTTON && state ==
GLUT_DOWN)
{

}
}
• glutMouseFunc sets the mouse callback for the current window.
• When a user presses and releases mouse buttons in the window, each
press and each release generates a mouse callback.
• The button parameter is one
of GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON,
or GLUT_RIGHT_BUTTON.
• For systems with only two mouse buttons, it may not be possible to
generate GLUT_MIDDLE_BUTTON callback.
• For systems with a single mouse button, it may be possible to generate only
a GLUT_LEFT_BUTTON callback.
• The state parameter is either GLUT_UP or GLUT_DOWN indicating whether
the callback was due to a release or press respectively.
• The x and y callback parameters indicate the window relative coordinates
when the mouse button state changed.
f) Posting redisplays

• Many events may invoke the display callback function


– Can lead to multiple executions of the display callback on a single
pass through the event loop
• We can avoid this problem by instead using
glutPostRedisplay();
which sets a flag.
• GLUT checks to see if the flag is set at the end of the event
loop
– If set then the display callback function is executed
– Therefore the use of glPostRedisplay() ensures the window gets
redrawn at most once each time GLUT goes through the event
loop.
void reshape(int w, int h)

Example {
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
#include <GL/glut.h>
glMatrixMode(GL_MODELVIEW);
#include <stdlib.h> glLoadIdentity();
static GLfloat spin = 0.0; }
void init(void) void mouse(int button, int state, int x, int y)
{ {
switch (button) {
glClearColor (0.0, 0.0, 0.0, 0.0);
case GLUT_LEFT_BUTTON:
glShadeModel (GL_FLAT); if (state == GLUT_DOWN)
} glutIdleFunc(spinDisplay);
void display(void) break;
{ case GLUT_MIDDLE_BUTTON:
if (state == GLUT_DOWN)
glClear(GL_COLOR_BUFFER_BIT);
glutIdleFunc(NULL);
glPushMatrix(); break;
glRotatef(spin, 0.0, 0.0, 1.0); default:
glColor3f(1.0, 1.0, 1.0); break;
glRectf(-25.0, -25.0, 25.0, 25.0); }}

glPopMatrix();
int main(int argc, char** argv)
glutSwapBuffers(); {glutInit(&argc, argv);
} glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (250, 250);
{ glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
spin = spin + 2.0;
init ();
if (spin > 360.0) glutDisplayFunc(display);
spin = spin - 360.0; glutReshapeFunc(reshape);
glutPostRedisplay(); glutMouseFunc(mouse);
} glutMainLoop();
return 0;
}
g) glutMainLoop()
• In event-driven programming, like you have in interactive OpenGL
applications, the main application loop generally does three things:

1. Check the current event queues, and process any events (e.g., mouse
movement, key presses, etc.) that have occurred since the last check

2. Update the application state - things like player and object positions,
game physics, etc. - in preparation of the next rendering frame

3. Render the current frame.

• GLUT does these steps implicitly in its glutMainLoop() for you.


• Almost all of the GLUT callbacks you register (e.g., glutKeyboardFunc(),
glutReshapeFunc(), etc.) take care of 1., and call functions you set up to
presumably do 2.
• If you register an idle function with glutIdleFunc(), it's called after all the
current events are processed.
• A good approach to play nicely with GLUT is to implement your idle
function to only call glutPostRedisplay(), which tells GLUT to schedule a
call to your display function once all the events are processed and before
your idle function is called.
Example

• void idle(void) {
time += 0.05;
glutSetWindow(window);
glutPostRedisplay();
}

• The idle callback does not do any actual drawing; it


only advances the time scene state global variable.
• That is left to the window's display callback which will
be triggered by the call to glutPostRedisplay.
h) glutSwapBuffers();
#include <GL/glut.h>
static GLfloat spin = 0.0;
void reshape(int w, int h)
void init(void) int main(int argc, char** argv)
{
{ {
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glClearColor (0.0, 0.0, 0.0, 0.0); glutInit(&argc, argv);
glMatrixMode(GL_PROJECTION);
glShadeModel (GL_FLAT); glutInitDisplayMode (GLUT_DOUBLE |
glLoadIdentity();
} GLUT_RGB);
glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
void display(void) glutInitWindowSize (250, 250);
glMatrixMode(GL_MODELVIEW);
{ glutInitWindowPosition (100, 100);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT); glutCreateWindow (argv[0]);
}
glPushMatrix(); init ();
void mouse(int button, int state, int x,
glRotatef(spin, 0.0, 0.0, 1.0); glutDisplayFunc(display);
int y)
glColor3f(1.0, 1.0, 1.0); glutReshapeFunc(reshape);
{
glRectf(-25.0, -25.0, 25.0, 25.0); glutMouseFunc(mouse);
switch (button) {
glPopMatrix(); glutMainLoop();
case GLUT_LEFT_BUTTON:
glutSwapBuffers(); return 0;
if (state == GLUT_DOWN)
} }
glutIdleFunc(spinDisplay);
break;
{
case GLUT_MIDDLE_BUTTON:
spin = spin + 2.0;
if (state == GLUT_DOWN)
if (spin > 360.0)
glutIdleFunc(NULL);
spin = spin - 360.0;
break;
glutPostRedisplay();
default:
}
break;
}
}
glutSwapBuffers

• glutSwapBuffers swaps the buffers of the current window if


double buffered.
• Performs a buffer swap on the layer in use for the current
window.
• Specifically, glutSwapBuffers promotes the contents of the
back buffer of the layer in use of the current window to
become the contents of the front buffer.
• The contents of the back buffer then become undefined.
• The update typically takes place during the vertical retrace of
the monitor, rather than immediately after glutSwapBuffers is
called.
• An implicit glFlush is done by glutSwapBuffers before it
returns.
• If the layer in use is not double buffered, glutSwapBuffers
has no effect.
Single vs Double Buffering
Single Buffering
• Single buffering works by sending the image OpenGL has generated to the graphics card and then to
the monitor as soon as you call glFlush().
• In an ideal world, the monitor would show this in real-time as soon as it was sent from the graphics
card. Unfortunately in the real-world we have monitor refresh times, this is the speed at which the
monitor pushes out a complete image on the screen.
• If you send images to the monitor outside of its refresh rate, then you are going to be missing quite a
few frames as the graphics card can push out images much faster than the monitor can receive them.
• Because of these two devices are not acting in sync, you find that you don’t see a lot of what is going
on and you end up with a garbled mess on the screen.

Double Buffering
• GLUT has support for double buffering through GLUT_DOUBLE.
• Combined with swapping out glFlush() for glutSwapBuffers(), we get a nice constant image on the
screen. Double buffering gives OpenGL a ‘hidden’ render which it can write to while the previous
render is being shown on the screen.
• Think of it this way, you are drawing an animation using two blackboards in front of a crowd of people,
but they only see one blackboard at a time. You draw frame number 1 on blackboard number one and
when it’s complete, you swap blackboard number 1 and blackboard number 2. You then erase
anything on blackboard number 2 and draw the next frame, once it’s complete you once again swap
blackboard number 1 and blackboard number 2.
#include <GL/glut.h>
i) glPopMatrix()
#include <stdlib.h>
void reshape(int w, int h)
static GLfloat spin = 0.0; int main(int argc, char** argv)
{
void init(void) {
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
{ glutInit(&argc, argv);
glMatrixMode(GL_PROJECTION);
glClearColor (0.0, 0.0, 0.0, 0.0); glutInitDisplayMode (GLUT_DOUBLE |
glLoadIdentity();
glShadeModel (GL_FLAT); GLUT_RGB);
glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
} glutInitWindowSize (250, 250);
glMatrixMode(GL_MODELVIEW);
void display(void) glutInitWindowPosition (100, 100);
glLoadIdentity();
{ glutCreateWindow (argv[0]);
}
glClear(GL_COLOR_BUFFER_BIT); init ();
void mouse(int button, int state, int x,
glPushMatrix(); glutDisplayFunc(display);
int y)
glRotatef(spin, 0.0, 0.0, 1.0); glutReshapeFunc(reshape);
{
glColor3f(1.0, 1.0, 1.0); glutMouseFunc(mouse);
switch (button) {
glRectf(-25.0, -25.0, 25.0, 25.0); glutMainLoop();
case GLUT_LEFT_BUTTON:
glPopMatrix(); return 0;
if (state == GLUT_DOWN)
glutSwapBuffers(); }
glutIdleFunc(spinDisplay);
}
break;
case GLUT_MIDDLE_BUTTON:
{
if (state == GLUT_DOWN)
spin = spin + 2.0;
glutIdleFunc(NULL);
if (spin > 360.0)
break;
spin = spin - 360.0;
default:
glutPostRedisplay();
break;
}
}
}
The Matrix Stack
• You may notice that when you assign a colour
to an object, that colour will also attach itself to
every object you draw after the colour
statement.
• Or the same for rotations and translations. This
is because OpenGL is based on a state
machine.
• Every time you perform an action such as glTranslate or glRotate, then you
are modifying the transformation matrix, and once something has been
done, it cannot be easily undone, so OpenGL makes use of states.
• State means that once an operation has been performed, it will affect
everything to come, but it means we can go back to a previous state and
forget about our current one.
• To do this, we only need to make use of two commands.
They are:
glPushMatrix(); //set where to start the current object transformations
and:
• glPopMatrix(); //end the current object transformations
• The first call, glPushMatrix(), tells OpenGL to
store the current state that we are in.
This then allows us to perform a bunch of
different effects.
• Then when we want to go back to our previous
state, we call glPopMatrix().
• This works fine for rotations and translations, but
OpenGL allows for many other commands, so it
gives us the ability to store attributes with
glPushAttrib() and glPopAttrib().
• Some of the attributes that we can store include,
GL_LIGHT and GL_COLOUR, but there are
many more.
Manipulating the matrix stack
void glPushMatrix(void);

• Pushes all matrices in the current stack down one level. The
current stack is determined by glMatrixMode().
• The topmost matrix is copied, so its contents are duplicated in
both the top and second-from-the-top matrix.
• If too many matrices are pushed, an error is generated.

void glPopMatrix(void);
• Pops the top matrix off the stack, destroying the contents of
the popped matrix.
• What was the second-from-the-top matrix becomes the top
matrix.
• The current stack is determined by glMatrixMode().
• If the stack contains a single matrix, calling glPopMatrix()
generates an error.
Example: Draws an automobile, assuming the existence of routines that draw the car body, a wheel, and a bolt by Pushing
and Popping the Matrix
draw_wheel_and_bolts()
{
long i;
draw_wheel();
for(i=0;i<5;i++){
glPushMatrix();
glRotatef(72.0*i,0.0,0.0,1.0);
glTranslatef(3.0,0.0,0.0);
draw_bolt();
glPopMatrix();
}
}
draw_body_and_wheel_and_bolts()
{
draw_car_body();
glPushMatrix();
glTranslatef(40,0,30); /*move to first wheel position*/
draw_wheel_and_bolts();
glPopMatrix();
glPushMatrix();
glTranslatef(40,0,-30); /*move to 2nd wheel position*/
draw_wheel_and_bolts();
glPopMatrix();
... /*draw last two wheels similarly*/
}
The Modelview Matrix Stack
• The modelview matrix contains the cumulative product of
multiplying, viewing and modeling transformation matrices.
• Each viewing or modeling transformation creates a new matrix
that multiplies the current modelview matrix; the result, which
becomes the new current matrix, represents the composite
transformation.
• The modelview matrix stack contains at least thirty-two 4 × 4
matrices; initially, the topmost matrix is the identity matrix.
Some implementations of OpenGL may support more than
thirty-two matrices on the stack.
The Projection Matrix Stack

• The projection matrix contains a matrix for the


projection transformation, which describes the
viewing volume.
• Generally, you don’t want to compose projection
matrices, so you issue glLoadIdentity() before
performing a projection transformation.
• Also for this reason, the projection matrix stack need
be only two levels deep; some OpenGL
implementations may allow more than two 4 × 4
matrices.
glScalef (0.6, 0.1, 0.08);
#include <GL/glut.h>
glutWireCube (1.0); case 'f': /* f key rotates at finger */
glPopMatrix(); finger = (finger + 5) % 360;
static int shoulder = 0, elbow = 0, finger = 0;
glPopMatrix(); glutPostRedisplay();
void init(void) glutSwapBuffers(); break;
{ } case 'F':
glClearColor (0.0, 0.0, 0.0, 0.0); void reshape (int w, int h) finger = (finger - 5) % 360;
glShadeModel (GL_FLAT); { glViewport (0, 0, (GLsizei) w, (GLsizei) h); glutPostRedisplay();
} glMatrixMode (GL_PROJECTION); break;
void display(void)
glLoadIdentity ();
{ default:
gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
glClear (GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); break;
glPushMatrix();
glLoadIdentity(); }
glTranslatef (-1.0, 0.0, 0.0);
glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0); glTranslatef (0.0, 0.0, -5.0); }
glTranslatef (1.0, 0.0, 0.0); } int main(int argc, char** argv)
void keyboard (unsigned char key, int x, int y) { switch {
glPushMatrix(); (key) { glutInit(&argc, argv);
glScalef (2.0, 0.5, 1.0); case 's': /* s key rotates at shoulder */ glutInitDisplayMode (GLUT_DOUBLE |
glutWireCube (1.0); shoulder = (shoulder + 5) % 360; GLUT_RGB);
glPopMatrix(); glutPostRedisplay(); glutInitWindowSize (700, 500);
glTranslatef (1.0, 0.0, 0.0); break; glutInitWindowPosition (100, 100);
glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0); glutCreateWindow (argv[0]);
case 'S':
glTranslatef (1.0, 0.0, 0.0); init ();
shoulder = (shoulder - 5) % 360;
glutPostRedisplay(); glutDisplayFunc(display);
glPushMatrix();
break; glutReshapeFunc(reshape);
glScalef (2.0, 0.5, 1.0);
glutWireCube (1.0); case 'e': /* e key rotates at elbow */ glutKeyboardFunc(keyboard);
glPopMatrix(); elbow = (elbow + 5) % 360; glutMainLoop();
glPushMatrix(); glutPostRedisplay(); return 0;
glTranslatef(1.6, 0.22, 0.0); break; }
glRotatef ((GLfloat) finger, 0.0, 0.0, 1.0); case 'E':
elbow = (elbow - 5) % 360;
glutPostRedisplay();
break;

You might also like