Advanced Animation Programming
(August - December 2020)

Programmer

Wolf Skeleton on Platform.png

After taking Intermediate Graphics & Animation Programming, the logical next step was Advanced Animation Programming. Unlike the previous class, we were given the option to work in animal3D (C), Unity (C#), Unreal (C++), or any other framework that would allow us to complete our assignments. Cameron Schneider and I decided to continue to use animal3D, as we were already familiar with its quirks.

The class covered many areas of animation programming including pose-to-pose animation, forward and inverse kinematics (FK and IK), blend trees, and having animations respond to user input. There wasn't much range for us to customize our work until the final project, where we were tasked with creating something that incorporated each of the main topics of the course. Cameron and I decided to implement character control for a wolf walking up a slope. We started by obtaining a skeleton and animation clips for a wolf, the latter of which we converted into an HTR file and loaded into animal3D. From there, Cameron was responsible for implementing raycasting so we could detect whether the wolf was on a slope, and I revamped our blend tree so it would be easier to construct and modify. I also set up the code that would actually run the raycasts, which we used to determine the positions of the IK constraints.

The large orange orbs are positioned one unit below the sources of the raycasts, and the cyan dots are the positions that the raycasts are hitting. These raycasts are bidirectional which allows us to determine whether a paw is above or below the floor. If the raycast source is below the floor, we move the IK constraint for that leg upwards until it's touching the floor, at which point we apply the IK solver.

 

The wolf bobs up and down as it goes up the ramp because its height is currently determined by a comparison between the distance from the shoulder to the paw in the wolf's idle pose (a stored constant) and the current distance between the shoulder and the floor. If the shoulder-to-floor distance is greater than the expected length, the wolf moves down to compensate, and if the shoulder-to-floor distance is less than 85% of the constant, the opposite occurs. This implementation was necessary because animal3D, being a graphics framework, lacks a built-in collision resolution system.

Blend Tree Construction.png

The code shown above demonstrates the various blend tree construction functions I implemented. When a blend tree is created, the user can choose to pre-allocate blend nodes or handle their allocation elsewhere and store it at a later point. Once the tree is constructed, each node is created by passing in the address of the node and an enum indicating its purpose. The following functions assign whatever data is necessary for that blend node to work properly, whether those are clip controllers or pointers to interpolation values.

 

The next function sets up parent-child relationships between nodes. In this situation, the nodes at index 0, 1, and 2 are all being set as children of the node at index 3, and the index 3 node is set as the root. Finally, there's a check to ensure that the tree was assembled correctly and the inputs and outputs of each blend node are configured.

I implemented the blend tree in this manner so that a GUI could hypothetically be constructed to edit trees at runtime. A node could easily be added, deleted or edited using some other utility functions, and the tree could then be rebuilt with BindStates() without needing to restart the program.