This Bilinear Animation Interpolation project aims to recreate the 2D animation blending that is used by many game engines to seamlessly blend between the skeleton poses of multiple animations at a time. 2D animation blending has numerous uses, from 8-directional movement to changing the way a character's upper body is rotated to aim in a particular direction (both vertically and horizontally). This project has implemented the former.
One major function of this project was that to implement an animation Interpolation system, one needs many animations to use for Interpolation. Because the FBX converter provided by CSCE 450 is incompatible with multiple animations and only compatible with Mixamo FBX files with model data and a single animation, using that method to convert to text files would be immensely ineffient for memory space and code complexity. To fix this, I downloaded a model from Mixamo, and then the animations separately. I then imported the model and animations into Blender and then exported them to a combined FBX file. Using this FBX file, I wrote a custom importer class that uses the Open Asset Importer Library (Assimp) to load the model, texture, and animation data into C++ classes for use in OpenGL.
To determine which animations to interpolate between, a grid of animation indices was constructed. This grid class will accept 2 float values (angle and speed in this case) and output a list containing the four corners of a quad as well as the alpha and beta values used in a typical bilinear interpolation computation.
The interpolation between animations is implemented using a Compute Shader in OpenGL. This is a type of shader that allows you to do an arbitrary calculation on an arbitrary number of workgroups to compute an arbitrary amount of data. In this case, before any rendering occurs, four buffers containing frame data for the bone transforms of the four animations from the grid are sent to the compute shader.
Additionally, a progression value for determining the current progress through an animation is stored as a uniform value for each animation. This allows for animations that are different in length. The progression of the animation depends on the previous progression with some delta progression change that depends on delta time and the speed at which the animation progresses (1 second for a walk cycle and 0.5 seconds for a run cycle). This progression allows you to ensure that the interpolation between animations of different speeds is synced. Essentially, at walking speed, the walk cycle progresses at one cycle per second, but as you approach running speed, the progression of the walk cycle increases to 2 cycles per second. This works in reverse for a run cycle.
Given the aforementioned parameters, the compute shader runs n bones times. So, for each bone the compute shader first interpolates the bone position and rotation between 2 frames of each animation. The resulting 4 transforms are then ran through a bilinear interpolation function that first interpolates between the first two animation transforms and then the second two animation transforms. The result of those two interpolations are then sent into a final interpolation that outputs the final local transform for the bone. After all the bone workgroups are finished, there is a complete posed array of bone transforms. These are then sent through two more compute shaders that compute the world transform of each bone and then the bone offset for using in skinning, respectively. The offset buffer is then sent to the vertex shader that uses the skinning equation on the GPU from Assignment 2.
To compute the direction and speed for interpolation, joystick input is collected using GLFW's built-in gamepad support. The raw joystick input goes from 0.0 to 1.0, and using the analog right trigger at max joystick magnitude allows you to smoothly increase the speed from 1.0 to 2.0. The right joystick may be used to control the camera angle and direction the character model is facing. Additionally, for more interactivity, the user may use the right bumper button to shoot a continuous stream of projectiles as the button is held down.
With more time, this project may be expanded to implement a more advanced animation processor that allows you to interpolate between animation states and more. For example, you could have a state for standing and a state for crouching movement, as you switch between crouching and standing, you may interpolate between the two results of different 2D animation interpolation grids.