3. OpenGL Keyboard Interaction (Version 2.0)

Introduction

The keyboard is one of those things that is essential to computer gaming, and computer use in general. Find the perfect one for your needs at https://qwertybro.com. Could you imagine trying to type with the mouse? Or the number of buttons that would be required on a mouse to allow for movement *and* shooting in a First Person Shooter? The good news is that keyboard interaction, thanks to GLUT, is an extremely easy thing to handle. I am going to look at 2 different GLUT methods for key presses, one for when a key is first pressed, and one for when a key is released. I will then be showing how to do some key buffering to allow multiple key presses at once.

Coding

Key Press

To get started with the keyboard, just like we created display and reshape methods for GLUT to use, we are going to need to create another method. The first method we are going to deal with is the key pressed method; this method will tell us which keys are currently pressed.

I am going to name this method keyPressed and GLUT states that it has 3 parameters, one for the key that is currently pressed, and two more that give us the location of the mouse when the key was pressed.

void keyPressed (unsigned char key, int x, int y) {
}

Now that we have an empty method, let’s go ahead and give this information to GLUT to use. Down in our main method, we will access the glutKeyboardFunc method and supply it with our method.

glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses

And that is pretty much all there is to it.

In order to access they keys pressed, inside your keyPressed method you can use either a switch statement or an ‘if’ statement, which checks they key variable against which ASCII character you want to check, and it will look something like this:

If (key == ‘a’) { // If they ‘a’ key was pressed
// Perform action associated with the ‘a’ key
}

Key Up

The key up function registers when a key is released. This can be very useful for setting some value when the key is first pressed, and wanting to keep that value until the key is released. Once again, as this is handled by GLUT, we will need to go and create a method to handle this, and just like the keyPressed method, this one will take in 3 parameters. These three parameters are exactly the same as 3 in the keyPressed method. But we are going to call this method keyUp. So let’s go ahead and create a method for this.

void keyUp (unsigned char key, int x, int y) {
}

And once again we need to register this method with GLUT, and this time we will use the glutKeyboardUpFunc method.

glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events

To use the keyUp method, is exactly the same as using the keyPressed method, so we can call the exact same code if we wanted.

if (key == 'a') { // If they ‘a’ key was released
// Perform action associated with the ‘a’ key
}

Key Buffering

The next step is of course, to do some key buffering, which will allow us to handle multiple key presses later on in our OpenGL projects. As you might be aware, a char variable is the same as a byte and can take an integer value from 0 to 255. This is because a char variable has a size of 2^8, or 256. This allows us to handle up to 256 different keys on a keyboard, or the entire ASCII set of characters.

So let’s start off by creating an array of Boolean values that will hold the states of our keys. True will be pressed, and False will be not pressed. I’m going to call this variable keyStates as it is easy to understand.

bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)

And now in our keyPressed method, let’s add a new line, which will set the value of the key we have pressed. This just sets the current position in the array of keyStates, at the position of the currently pressed key, to true.

void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}

And now we just need to add almost the exact same code to the keyUp method we created earlier. Only this time, we will be setting the state of the current key to false, meaning it is no longer pressed.

void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}

All that is left now for us to learn is how to actually use this. The good news is that now that you have the key information, you can use it however you want; you just have to setup somewhere in your code to manage this. I’ll go through an example on this to get you started.

To do this, I am going to create another method, this time called keyOperations, where we are going to check for which keys have been pressed, and then perform their actions. This method doesn’t need to return anything, and doesn’t need to accept any parameters. Just declare it before your display() method, as we are actually going to call it from our display method.

void keyOperations (void) {
}

And as we are calling this from our display method, go ahead and call it from the very start of our display method, preferably before everything else. The reason I say to call it before everything else, is if you call it halfway through your rendering, then half of your render will be affected by any values updated by the key presses, and the other half of your render will not be affected, giving a mismatch and possible artifacts.

void display (void) {
keyOperations();
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
…
}

Finally, we fill in our keyOperations method with a whole bunch of if statements to check if the keys we want are pressed, and then perform the required actions.

void keyOperations (void) {
if (keyStates['a']) { // If the 'a' key has been pressed
// Perform 'a' key operations
}
}

Special Keys

While all of the above is perfectly fine for using regular key presses, there are some keys you may want to use, for example, the arrow keys, which can be represented differently on multiple systems. Luckily, GLUT allows us to use these keys, as long as we call a different method to glutKeyboardFunc. Instead we must use glutSpecialFunc which checks for presses of special keys. For example, if we want to check if the left arrow has been pressed, we can use GLUT_KEY_LEFT. The best news is that the method you create to handle your special keys, will be almost identical to the glutKeyboardFunc method you created earlier. Below is an example of using special keys.

First off, create a new method which I am going to call keySpecial, which takes three parameters, all of them being an integer value, the first for the key pressed, and the second two are for the x and y position of the mouse at the time the method was called. And then call this method in our main method along with the rest of our key presses.

void keySpecial (int key, int x, int y) {

}

And then for the up function for special keys, declare a new method for handling the up event for special keys.

void keySpecialUp (int key, int x, int y) {

}
int main (int argc, char **argv) {
...
glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping

glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events

glutSpecialFunc(keySpecial); // Tell GLUT to use the method "keySpecial" for special key presses
glutSpecialUpFunc(keySpecialUp); // Tell GLUT to use the method "keySpecialUp" for special up key events

glutMainLoop(); // Enter GLUT's main loop
}

If you want to buffer these keys, you can create another array just like we did for regular key buffering, and dedicate this array to any special keys pressed.

bool* keySpecialStates = new bool[246]; // Create an array of boolean values of length 256 (0-255)

After you create this array, make sure you set the values to true and false, just as you did in the regular key methods, and then create another method to control what happens when we press our special keys, I am calling mine keySpecialOperations, which already has code for checking if the left arrow key has been pressed.

void keySpecialOperations(void) {
if (keySpecialStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed
// Perform left arrow key operations
}
}

Hopefully this has helped you get an understanding of how to manage key presses in GLUT and OpenGL so you can get some keyboard interaction going in the later tutorials.

If you have any questions, please email me at swiftless@gmail.com

Tutorial Code

#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file

bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)

void keyOperations (void) {
if (keyStates['a']) { // If the 'a' key has been pressed
// Perform 'a' key operations
}
}

void display (void) {
keyOperations();

glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations

glFlush(); // Flush the OpenGL buffers to the window
}

void reshape (int width, int height) {
glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)
gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes
glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly
}

void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}

void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}

int main (int argc, char **argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window

glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering

glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping

glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events

glutMainLoop(); // Enter GLUT's main loop
}
  • March 25, 2010
  • 58