Dragon Animation

Description:

The purpose of this project was to create an animated dragon that used hierarchal skeleton animation and keyframes. A mesh was created using Blender, an open-source 3D modeling program, and the main program was written in C++ with OpenGL on linux.

Links:
Source Code and executable (Ubuntu)
User Guide
Project Report
Video
Images

Student Info:

Name: Brian Stewart
e-mail: bjstewar@slugmail.edu
Class: CMPS 161 - Computer Visualization and Animation
Year: 4th
Quarter: Winter
The dragon model being edited

Project Report:

The purpose of my project was to create a 3D animated dragon using skeletal animation. This meant creating a hierarchal bone structure which could be moved and manipulated, creating key-frames for the animation, attaching the skeleton to the dragon model, and interpolating between the key-frames for smooth animation.

The first part of the project required building the bone structure. This consisted of a linked tree of bone objects which each contained several values. This included angle values for rotating the bone along the x, y, and z axes, a length, a name to identify the bone by, a pointer to the parent, and an array of pointers to children. Some functions were written to use this structure. There is dump_skeleton and load_skeleton. These allow the skeleton and all the information for each bone to be saved and loaded. Also, change_bone(), which steps through a depth first traversal of the skeleton changing the “cur” pointer each time it is called. And find_by_name(), which also does a depth-first traversal and returns a pointer to a bone given its name. Finally there is draw_skeleton(), which recursively draws the skeleton based on the given angles and length of each bone. Originally this worked by traversing the tree and using glRotatef () to hierarchically rotate each bone. The function also recursively passed the ending position of a bone so that the the call would know where to start the next bone. Bones are drawn as lines with a sphere at the end of them, so they are easy to see.

At this point, a quaternion object was written to use with the structure. This was done for two reasons. Firstly quaternions are needed to avoid the gimbal lock of Euler angles and create smooth animation. Secondly, I could not see a way to obtain the position of bone after they had been rotated by the OpenGL matrix stack using glRotatef(). The positions are important because they are necessary in order to attach the dragon mesh to the bones by proximity. The quaternion object contains methods for creating a quaternion based on Euler angles, multiply a point by a quaternion, multiplying two quaternions by each other and doing spherical interpolation, among other basic functions. In order to save time, a new method called, pre_compute_quat() was written to do a single traversal of the tree and computer both the quaternions, based on each Euler angle for each bone, and the starting positions of each bone. This way, drawing the skeleton would not be slowed down by having to recalculate a quaternion from a Euler angle for each bone for each frame.

Next animation functions were created. A new file type was invented for animations. This consists of each bone in the skeleton followed by its rotation along each axis, its length, and the time at which the key-frame occurs. Simple functions for loading and saving these files were written. Key-frames are set up by having each bone contain an array of pointers to key-frames structures. Each key-frames structure contains the angles to rotate the bone by, a length, a time which the key-frames occurs, and a quaternion and beginning point which are precomputed before animation starts. In order to animate the bones, a function called animate() was written. Animate() takes a time t and draws the bone structure based on that time. If the time given is past the time of the last key-frame, then the mod operator recomputes t so that the animation just repeats. Basically animate() takes the time and figures out which two key-frames it is between. It then sends the indexes of those two key-frames to a recursive animation function which traverses the bone structure and draws each bone based on the spherical interpolation between those two key-frames. The quaternions for each key-frames are precomputed to speed up animation.

The next major part of the project was the dragon mesh. First a model was created. In order to easily create a model, I chose to use the open-source 3d modeling program Blender, which I have some experience with. After modeling the dragon, I used the object file type to get it into my program. The object file type basically consists of a list of vertexes and a list of faces. I wrote a simple function to read in this data. A model object was created to store information about the model. The object consists of all the vertexes and faces in the model, a function to calculate the normals of the model, and a function to draw the model. Also the key_set object which contains all the skeleton and information, is made a friend class so that it can manipulate the vertexes based on the movements of the skeleton.

Next, in order to move the points based on the skeleton, I wrote a function to attach each bone to a certain set of vertexes of the model. Also an array of pointers to model vertexes was added to each bone. In order to attach each vertex to a bone a function is used that calculates the distance from the bone to every point in the mesh. This is done once and each vertex pointer is stored in the array for each bone. Since connecting a bone to a certain set of points based on a set distance is not always desirable, two methods to increase precision was devised. The first was to create a distance variable for each bone which can be changed using keyboard inputs. After changing one, the bone points were recalculated using the new distance. Since it is necessary to save this information, the skeleton file type was modified to include the indexes of each point that a bone is attached to. Since it is helpful to visually see each point that a bone is attached to, a new object was created to draw spheres at each vertex. This object takes the model vertex array and goes through each one drawing a small sphere at its location. It also checks if the given vertex is one that is attached to the “cur” pointer of the skeleton. If so, it draws the sphere red. This makes it easy to see which points are attached to which bones. The second method to aid in attaching points to bones, is the use of picking. By using the spheres object, a check is done each time the user clicks on the screen. The spheres are all given id's that match that of the vertex indexes. When the user clicks on one. that index is used to attach the point to the current bone.

In order to actually move each point several new variables were added. The mesh needs to be moved based on it's starting position relative to the starting position of the skeleton. A duplicate array of vertexes for the mesh are created and stored. In addition, each bone is given an addition array of integers which specify the indexes of the vertexes in the duplicate vertex array which correspond to the vertex pointers in their points array. This allows each bone access to the original point location and the current point location. Also three more angle values are added which represent the current bone position. The older angle values represent the starting bone position. In order to rotate the points of the mesh correctly several steps are taken. First the points are subtracted from the original starting point of the bone. The the points are rotated by the conjugate of the original quaternion. This rotates them in the opposite direction they need to be rotated in. Then they are rotated by the current position quaternion. The current position quaternion represents the original starting rotation, plus whatever else has been changed in a key-frame. By doing this, a quaternion based on the change of the angle from the original angle does not have to be calculated. Finally, The points are translated to the correct location by adding them to the current starting position. All the values, the two starting position, and the two quaternion values were precomputed to speed animation.

In order to help create the animations I created several pop-up menu items and key commands. The program has three modes. Edit Mode allows the user to either click on bones, or cycle through them using the 'c' key. Once a bone is selected, all the points that it is connected to show up in red. The user can click on points to either add or remove them from the bone's influence. The user can save the skeleton with all the bone information and can also recalculate the bone points using the preset distance. If the user loads the animation file, they can select Animation Mode, which will animate the dragon. The last mode is Keyframe Mode. This mode allows the user to step through each key-frames by hitting the 'f' key. The key-frame can be edited by selecting the bone, changing the angle, and then selecting “Save Animation” in the pop-up menu. Also the duration of the current key-frames to the next can be changed by hitting the 'I' key and then '+' or '-'

This project sets up a system to create animations using skeletal animation. Unfortunately I did not have enough time to create all the animation that I wanted to. I only had time to make a simplistic flying cycle. Even so, I feel the project as a whole was a success.