Creating an interactive environment with GLFW

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.

Getting ready

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.

How to do it...

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:

  1. Define the following global variables (including a new variable called 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;
  2. Define the keyboard callback function prototype:
    void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
    {
  3. If we receive any event other than the key press event, ignore it:
      if (action != GLFW_PRESS)
        return;
  4. Create a switch statement to handle each key press case:
      switch (key)
      {
  5. If the Esc key is pressed, exit the program:
        case GLFW_KEY_ESCAPE:
          glfwSetWindowShouldClose(window, GL_TRUE);
          break;
  6. If the space bar is pressed, start or stop the animation by toggling the variable:
        case GLFW_KEY_SPACE:
          freeze=!freeze;
          break;
  7. If the direction keys (up, down, left, and right) are pressed, update the variables that control the angles of rotation for the rendered object:
        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;
  8. Lastly, if the Page Up or Page Down keys are pressed, zoom in and out from the object by updating the 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:

  1. Define the mouse press callback function prototype:
    void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
    {
  2. Ignore all inputs except for the left click event for simplicity:
      if (button != GLFW_MOUSE_BUTTON_LEFT)
        return;
  3. Toggle the 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:

  1. Define the callback function prototype that takes in the mouse coordinates:
    void cursor_position_callback(GLFWwindow* window, double x, double y)
    {
  2. Upon mouse press and mouse movement, we update the rotation angles of the object with the x and y coordinates of the mouse:
      //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.

  1. Define the callback function prototype that captures the x and y scroll variables:
    void scroll_callback(GLFWwindow* window, double x, double y)
    {
  2. Take the y parameter (up and down scroll) and update the zoom variable:
      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:

How to do it...

Here is another screenshot at a different zoom level:

How to do it...

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:

How to do it...

Here is a screenshot showing the Gaussian function from the top:

How to do it...

Finally, here is a screenshot showing the Gaussian function from the bottom:

How to do it...

How it works...

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.

See also

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/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.247.31