Search icon CANCEL
Subscription
0
Cart icon
Cart
Close icon
You have no products in your basket yet
Save more on your purchases!
Savings automatically calculated. No voucher code required
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
C++ Game Animation Programming - Second Edition

You're reading from  C++ Game Animation Programming - Second Edition

Product type Book
Published in Dec 2023
Publisher Packt
ISBN-13 9781803246529
Pages 480 pages
Edition 2nd Edition
Languages
Concepts
Authors (2):
Michael Dunsky Michael Dunsky
Profile icon Michael Dunsky
Gabor Szauer Gabor Szauer
Profile icon Gabor Szauer
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 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

Event handling in GLFW

Many modern OSs are event-based – the programs don’t just sit there and ask the OS over and over if any mouse or keyboard input has occurred or if the window has been moved, minimized, or resized. All these events are stored in an event queue and must be handled by the application code. If you never request the events of that queue, your application window won’t even close in a proper manner, that is, it can only be killed using Task Manager.

You can find the example code for these additions in the 04_event_handling folder.

Let’s have a look at how GLFW handles the events from the OS.

The GLFW event queue handling

You have already seen a bit of the event handling in the code for the Window class – we used these two GLFW calls to close our window and end the application:

int glfwWindowShouldClose(GLFWwindow *win);
void glfwPollEvents();

The first call, glfwWindowShouldClose(), checks whether an application window should be closed. This event is generated after the user clicks on the top-right close icon of the window. We are using this as a condition to step out of our while() loop, end the mainLoop() method of the Window class, and start the cleanup process.

Important note

The call to glfwPollEvents() is required in order to empty the event queue. It will also run any configured callbacks. If you forget this call, your window will do nothing, not even close down.

You should call glfwPollEvents() at the end of the main loop to process the newly arrived events.

There is another call to clear the event queue and fire the callbacks:

void glfwWaitEvents();

This one puts the thread to sleep and waits until at least one event has been generated for the window. Usually, this is used in non-interactive applications that are waiting for any input from the user.

Mixing the C++ classes and the C callbacks

A simple starting point is to react to the window close request and just output a message to the user. To get this to work, we need two parts – the function called by GLFW and a call that sets the function as a callback.

This sounds easy to do, but only at first glance. As GLFW is pure C code, it has no knowledge about C++ classes, member functions, the this pointer, and all the other moving parts. However, there are some solutions to this.

The first way is that we could use a static function of our Window class as it is technically similar to a C function. At the moment, we won’t use more than one application window, but if we add support for a pop-out window later, we might be in trouble with the static class function. It is the same for all objects of that class, and as it can only access static members, you have to take extra steps to avoid even more trouble when starting with multithreaded code.

So, let’s consider the second way and use a “free” function, outside the class, to dispatch the call to the C++ class. However, instead of having to define two separate functions for every callback, we will use a Lambda.

Lambda functions

A lambda is a small piece of code, running as an anonymous function. It has no visible name, takes the number and types of arguments from its definition, and runs the code. Internally, the lambda function is converted into a small class by your compiler; there is no magic applied here. It’s only a convenient way to help reduce the code you write. If you want to know more about lambda functions, you can find a link to a tutorial in the Additional resources section.

The authors of GLFW are aware of this problem and have added a small helper to every window that might be created – a pointer that can be set and read by the user:

void glfwSetWindowUserPointer(GLFWwindow *win, void *ptr);

You can store any arbitrary data in the user pointer – it doesn’t have to be the this pointer of the class object, but it is only a pointer and must be accessible by your code. We will use it to store the pointer in our C++ Window object, and inside the lambda, this pointer will be read and used just like in any other C++ call.

The callback function itself looks a bit weird if you have never used C-style callbacks:

GLFWwindowclosefun glfwSetWindowCloseCallback (GLFWwindow *window, GLFWwindowclosefun callback);

It requires a pointer to a function and returns either NULL, if this is the first call, or the pointer to a previously set callback function. You could change this callback during runtime, which means moving to a different dialog to display any unsaved changes.

The last part of the puzzle is the window close function, which is called by the callback:

typedef void(* GLFWwindowclosefun) (GLFWwindow *window)

The GLFWwindowclosefun function is created using typedef, just like the other functions used for callbacks. This is done to avoid writing the expression in the second braces every time we use the function. As this is still C code, sadly, no modern C++ enhancements are available to change it.

And this is how you should put all the parts together – by adding the following lines to the init() function of the Window.cpp file:

  glfwSetWindowUserPointer(mWindow, this);
  glfwSetWindowCloseCallback(mWindow, [](GLFWwindow *win) {
    auto thisWindow = static_cast<Window*>(
      glfwGetWindowUserPointer(win));
    thisWindow->handleWindowCloseEvents();
  });

Here, the lambda is introduced by the square brackets, [], followed by the parameters the function takes. You could even capture some data from the outside of the function using the brackets, making it available, like in normal functions. We can’t use this capturing method for C-style callbacks, as such captures are not compatible with a function pointer.

Inside the lambda function, we can retrieve the user pointer set by glfwSetWindowUserPointer(), cast it back to a pointer to an instance of our Window class (this is our application window), and call the member function to handle the event. The function does not need to get the GLFWwindow parameter, as we already saved it as a private member in the Window class. The result of glfwSetWindowCloseCallback() can be safely ignored. It returns the address of the callback function that was set in a previous call. This is the first call in the code, so it will simply return NULL.

The class member function needs to be added to Window.cpp:

void Window::handleWindowCloseEvents() {
  Logger::log(1, "%s: Window got close event... bye!\n",
    __FUNCTION__);
}

Currently, the handleWindowCloseEvents() function just prints out a log line and does nothing else. But this is the perfect place to check whether the user really wants to quit or if unsaved changes have been made.

This function has to be declared in the Window.h header file, too:

private:
  void handleWindowCloseEvents();

If you start the compiled code and close the window, you should get an output like this:

init: Window successfully initialized
handleWindowCloseEvents: Window got close event... bye!
cleanup: Terminating Window

You can check the other events in the GLFW documentation and add other callback functions plus the respective lambdas. Additionally, you can check the example code for more calls – it has simple support for window movement, minimizing and maximizing, and printing out some log messages when the events are processed.

Important note

Some OSs stall the window content update if your application window has been moved or resized. So, don’t be alarmed if this happens – it is not a bug in your code. Workarounds are available to keep the window content updated on these window events, and you can check the GLFW documentation to find a way to solve this.

Now that our application window behaves in the way we would expect, we should add methods for a user to control what happens in our program.

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 $15.99/month. Cancel anytime}