Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
C++ Game Animation Programming

You're reading from   C++ Game Animation Programming Learn modern animation techniques from theory to implementation using C++, OpenGL, and Vulkan

Arrow left icon
Product type Paperback
Published in Dec 2023
Publisher Packt
ISBN-13 9781803246529
Length 480 pages
Edition 2nd Edition
Languages
Tools
Concepts
Arrow right icon
Authors (2):
Arrow left icon
Gabor Szauer Gabor Szauer
Author Profile Icon Gabor Szauer
Gabor Szauer
Michael Dunsky Michael Dunsky
Author Profile Icon Michael Dunsky
Michael Dunsky
Arrow right icon
View More author details
Toc

Table of Contents (22) Chapters Close

Preface 1. Part 1:Building a Graphics Renderer
2. Chapter 1: Creating the Game Window FREE CHAPTER 3. Chapter 2: Building an OpenGL 4 Renderer 4. Chapter 3: Building a Vulkan Renderer 5. Chapter 4: Working with Shaders 6. Chapter 5: Adding Dear ImGui to Show Valuable Information 7. Part 2: Mathematics Roundup
8. Chapter 6: Understanding Vector and Matrix 9. Chapter 7: A Primer on Quaternions and Splines 10. Part 3: Working with Models and Animations
11. Chapter 8: Loading Models in the glTF Format 12. Chapter 9: The Model Skeleton and Skin 13. Chapter 10: About Poses, Frames, and Clips 14. Chapter 11: Blending between Animations 15. Part 4: Advancing Your Code to the Next Level
16. Chapter 12: Cleaning Up the User Interface 17. Chapter 13: Implementing Inverse Kinematics 18. Chapter 14: Creating Instanced Crowds 19. Chapter 15: Measuring Performance and Optimizing the Code 20. Index 21. Other Books You May Enjoy

The mouse and keyboard input for the game window

Adding support for the keys pressed on the keyboard, the buttons on the mouse, or moving the mouse around is a simple copy-and-paste task from the window events – create a member function to be called and add the lambda-encapsulated call to GLFW. The next time you press a key or move the mouse after a successful recompilation, the new callbacks will run.

You can find the enhanced example code in the 05_window_with_input folder.

Let’s start by retrieving the key presses before we add the keyboard callbacks and functions. After this, we will continue to get mouse events and also add the respective functions for them to the code.

Key code, scan code, and modifiers

To get the events for the keys the user presses or releases on their keyboard, GLFW offers another callback. The following callback for a plain key input receives four values:

glfwSetKeyCallback(window, key_callback);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)

These values are listed as follows:

  • The ASCII key code of the key
  • The (platform-specific) scan code of that key
  • The action you carried out (press the key, release it, or hold it until the key repeat starts)
  • The status of the modifier key, such as Shift, Ctrl, or Alt

The key can be compared with internal GLFW values such as GLFW_KEY_A, as they emit the 7-bit ASCII code of the letter you pressed. The function keys, the separate keypad, and the modifier keys return values >256.

The scan code is specific to your system. While it stays the same on your system, the code may differ on another platform. So, hardcoding it into your code is a bad idea.

The action is one of the three values GLFW_PRESS, GLFW_RELEASE, or GLFW_REPEAT, if the key is pressed for longer, but note that the GLFW_REPEAT action is not issued for all keys.

The modifier status is a bitmap to see whether the users pressed keys such as Shift, Ctrl, or Alt. You can also enable the reporting of Caps Lock and Num Lock – this is not enabled in the normal input mode.

For example, we could add a simple keyboard logging to the code. First, add a new function to the Window.h header file:

public:
  void handleKeyEvents(int key, int scancode, int action,
    int mods);

As you can see in the preceding code, we don’t need GLFWwindow in our functions, as we already saved it as a private data member of the class.

Next, add the callback to the GLFW function using a lambda:

  glfwSetKeyCallback(mWindow, [](GLFWwindow *win, int key,
    int scancode, int action, int mods) {
    auto thisWindow = static_cast<Window*>(
      glfwGetWindowUserPointer(win));
    thisWindow->handleKeyEvents(key, scancode, action,
      mods);
    }
  );

This is the same as it was for the window event – get the this pointer of the current instance of the Window class from the user pointer set by glfwSetWindowUserPointer() and call the new member functions of the class.

For now, the member function for the keys can be simple:

void Window::handleKeyEvents(int key, int scancode, int action, int mods) {
  std::string actionName;
  switch (action) {
    case GLFW_PRESS:
      actionName = "pressed";
      break;
    case GLFW_RELEASE:
      actionName = "released";
      break;
    case GLFW_REPEAT:
      actionName = "repeated";
      break;
    default:
      actionName = "invalid";
      break;
  }
  const char *keyName = glfwGetKeyName(key, 0);
  Logger::log(1, "%s: key %s (key %i, scancode %i) %s\n",
    __FUNCTION__, keyName, key, scancode,
    actionName.c_str());
}

Here, we use a switch() statement to set a string depending on the action that has occurred and also call glfwGetKeyName() to get a human-readable name of the key. If no name has been set, it prints out (null). You will also see the key code, which is the ASCII code for letters and numbers, as mentioned earlier in this section, and the platform-specific scan code of the key. As a last field, it will print out if the key was pressed, released, or held until the key repeat from the OS started. The default option is used for completeness here; it should never be called in the current GLFW version as it would indicate a bug.

Different styles of mouse movement

GLFW knows two types of mouse movement: the movement adjusted by the OS and a raw movement.

The first one returns the value with all the optional settings you might have defined, such as mouse acceleration, which speeds up the cursor if you need to move the cursor across the screen.

The following is a callback function, which gets informed if the mouse position changes:

glfwSetCursorPosCallback(window, cursor_position_callback);
void cursor_position_callback(GLFWwindow* window,
  double xpos, double ypos)

Alternatively, you can poll the current mouse position in your code manually:

double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);

The raw mode excludes these settings and provides you with the precise level of movement on your desk or mouse mat. To enable raw mode, first, you have to disable the mouse cursor in the window (not only hide it), and then you can try to activate it:

glfwSetInputMode(window, GLFW_CURSOR,
  GLFW_CURSOR_DISABLED);
if (glfwRawMouseMotionSupported()) {
    glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION,
      GLFW_TRUE);
}

To exit raw mode, go back to the normal mouse mode:

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);

Keeping both movement styles apart will be interesting for the kind of application we are creating. If we want to adjust the settings using an onscreen menu, having the mouse pointer react like it would in other applications on your computer is perfect. But once we need to rotate or move the model, or change the view in the virtual world, any acceleration could lead to unexpected results. For this kind of mouse movement, we should use the raw mode instead.

To add a mouse button callback, add the function call to Window.h:

private:
  void handleMouseButtonEvents(int button, int action,
    int mods);

And in Window.cpp, add the callback handling and the function itself:

  glfwSetMouseButtonCallback(mWindow, [](GLFWwindow *win,
    int button, int action, int mods) {
    auto thisWindow = static_cast<Window*>(
      glfwGetWindowUserPointer(win));
    thisWindow->handleMouseButtonEvents(button, action,
      mods);
    }
  );

This is similar to the keyboard callback discussed earlier in this chapter; we get back the pressed button, the action (GLFW_PRESS or GLFW_RELEASE), and also any pressed modifiers such as the Shift or Alt keys.

The handler itself is pretty basic in the first version. The first switch() block is similar to the keyboard function, as it checks whether the button has been pressed or released:

void Window::handleMouseButtonEvents(int button,
  int action, int mods) {
  std::string actionName;
  switch (action) {
    case GLFW_PRESS:
      actionName = "pressed";
      break;
    case GLFW_RELEASE:
      actionName = "released";
      break;
    default:
      actionName = "invalid";
      break;
  }

The second switch() block checks which mouse button was pressed, and it prints out the names of the left, right, or middle buttons. GLFW supports up to eight buttons on the mouse, and more than the basic three are printed out as "other":

  std::string mouseButtonName;
  switch(button) {
    case GLFW_MOUSE_BUTTON_LEFT:
      mouseButtonName = "left";
      break;
    case GLFW_MOUSE_BUTTON_MIDDLE:
      mouseButtonName = "middle";
      break;
    case GLFW_MOUSE_BUTTON_RIGHT:
      mouseButtonName = "right";
      break;
    default:
      mouseButtonName = "other";
      break;
  }
  Logger::log(1, "%s: %s mouse button (%i) %s\n",
    __FUNCTION__, mouseButtonName.c_str(), button,
    actionName.c_str());
}

When running the code, you should see messages like this:

init: Window successfully initialized
handleWindowMoveEvents: Window has been moved to 0/248
handleMouseButtonEvents: left mouse button (0) pressed
handleMouseButtonEvents: left mouse button (0) released
handleMouseButtonEvents: middle mouse button (2) pressed
handleMouseButtonEvents: middle mouse button (2) released
handleWindowCloseEvents: Window got close event... bye!
cleanup: Terminating Window

You could add more handlers. The example code also uses the callbacks for mouse movement, which gives you the current mouse position inside the window, and the callback for entering and leaving the window.

You have been reading a chapter from
C++ Game Animation Programming - Second Edition
Published in: Dec 2023
Publisher: Packt
ISBN-13: 9781803246529
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime