In the previous two sections, we focused on the creation of 3D objects and on utilizing basic OpenGL rendering techniques with a virtual camera. Now, we are ready to incorporate user inputs, such as mouse and keyboard inputs, to enable more dynamic interactions using camera control features such as zoom and rotate. These features will be the fundamental building blocks for the upcoming applications and the code will be reused in later chapters.
The GLFW library provides a mechanism to handle user inputs from different environments. The event handlers are implemented as callback functions in C/C++, and, in the previous tutorials, we bypassed these options for the sake of simplicity. To get started, we first need to enable these callback functions and implement basic features to control the rendering parameters.
To handle keyboard inputs, we attach our own implementation of the callback
functions back to the event handler of GLFW. We will perform the following operations in the callback
function:
locked
to track whether the mouse button is pressed down, as well as the angles of rotation and zoom level) that will be updated by the callback
functions:GLboolean locked = GL_FALSE; GLfloat alpha=210.0f, beta=-70.0f, zoom=2.0f;
callback
function prototype:void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (action != GLFW_PRESS) return;
switch
statement to handle each key press case:switch (key) {
case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GL_TRUE); break;
case GLFW_KEY_SPACE: freeze=!freeze; break;
case GLFW_KEY_LEFT: alpha += 5.0f; break; case GLFW_KEY_RIGHT: alpha -= 5.0f; break; case GLFW_KEY_UP: beta -= 5.0f; break; case GLFW_KEY_DOWN: beta += 5.0f; break;
zoom
variable:case GLFW_KEY_PAGE_UP: zoom -= 0.25f; if (zoom < 0.0f) zoom = 0.0f; break; case GLFW_KEY_PAGE_DOWN: zoom += 0.25f; break; default: break; } }
To handle mouse click events, we implement another callback
function similar to the one for the keyboard. The mouse click event is rather simple as there is only a limited set of buttons available:
callback
function prototype:void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
if (button != GLFW_MOUSE_BUTTON_LEFT) return;
lock
variable to store the mouse hold event. The lock
variable will be used to determine whether the mouse movement is used for rotating the object:if (action == GLFW_PRESS) { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); locked = GL_TRUE; } else { locked = GL_FALSE; glfwSetInputMode(window, GLFW_CURSOR,GLFW_CURSOR_NORMAL); } }
For handling mouse movement events, we need to create another callback
function. The callback
function for mouse movement takes the x and y coordinates from the window instead of unique key inputs:
callback
function prototype that takes in the mouse coordinates:void cursor_position_callback(GLFWwindow* window, double x, double y) {
//if the mouse button is pressed if (locked) { alpha += (GLfloat) (x - cursorX) / 10.0f; beta += (GLfloat) (y - cursorY) / 10.0f; } //update the cursor position cursorX = (int) x; cursorY = (int) y; }
Finally, we will implement the mouse scroll callback function, which allows users to scroll up and down to zoom in and zoom out of the object.
callback
function prototype that captures the x
and y
scroll variables:void scroll_callback(GLFWwindow* window, double x, double y) {
zoom += (float) y / 4.0f; if (zoom < 0.0f) zoom = 0.0f; }
With all of the callback
functions implemented, we are now ready to link these functions to the GLFW library event handlers. The GLFW library provides a platform-independent API for handling each of these events, so the same code will run in Windows, Linux, and Mac OS X seamlessly.
To integrate the callbacks with the GLFW library, call the following functions in the main
function:
//keyboard input callback glfwSetKeyCallback(window, key_callback); //framebuffer size callback glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //mouse button callback glfwSetMouseButtonCallback(window, mouse_button_callback); //mouse movement callback glfwSetCursorPosCallback(window, cursor_position_callback); //mouse scroll callback glfwSetScrollCallback(window, scroll_callback);
The end result is an interactive interface that allows the user to control the rendering object freely in space. First, when the user scrolls the mouse (see the following screenshots), we translate the object forward or backward. This creates the visual perception that the object is zoomed in or zoomed out of the camera:
Here is another screenshot at a different zoom level:
These simple yet powerful techniques allow users to manipulate virtual objects in real-time and can be extremely useful when visualizing complex datasets. Additionally, we can rotate the object at different angles by holding the mouse button and dragging the object in various directions. The screenshots below show how we can render the graph at any arbitrary angle to better understand the data distribution.
Here is a screenshot showing the side view of the Gaussian function:
Here is a screenshot showing the Gaussian function from the top:
Finally, here is a screenshot showing the Gaussian function from the bottom:
This sample code illustrates the basic interface needed to build interactive applications that are highly portable across multiple platforms using OpenGL and the GLFW library. The use of callback
functions in the GLFW library allows non-blocking calls that run in parallel with the rendering engine. This concept is particularly useful since input devices such as the mouse, keyboard, and joysticks all have different input rates and latency. These callback
functions allow for asynchronous execution without blocking the main rendering loop.
The glfwSetKeyCallback
, glfwSetFramebufferSizeCallback
, glfwSetScrollCallback
, glfwSetMouseBcuttonCallback
, and glfwSetCursorPosCallback
functions provide controls over the mouse buttons and scrolling wheel, keyboard inputs, and window resizing events. These are only some of the many handlers we can implement with the GLFW library support. For example, we can further extend the error handling capabilities by adding additional callback
functions. Also, we can handle window closing and opening events, thereby enabling even more sophisticated interfaces with multiple windows. With the examples provided thus far, we have introduced the basics of how to create interactive interfaces with relatively simple API calls.
For complete coverage of GLFW library function calls, this website provides a comprehensive set of examples and documentation for all callback functions as well as the handling of inputs and other events: http://www.glfw.org/docs/latest/.
13.58.247.31