CS 315 Homework 4 - Dance, CubeBot! Dance!
Due Fri Oct 17 at 11:59pm
Overview
You will be adding the ability to interact with your rendered scene in the previous assignment. You will add functionality so that the user can move parts of your robot and put him into cool poses. Next, you will be adding the ability to "save" these poses as key frames in an animation--which you will then be able to play back and watch!
You may work individually or in pairs on this assignment--with well-organized code, you should easily be able to swap out either partner's robot!
Objectives
- Utilize the hierarchical transformation system developed in the last assignment
- Explore keyframing as an animation technique
- Practice implementing animations and create a cool animation to show off!
- Practice working with jQuery and adding interaction (a bit)
Necessary Files
This assignment will build upon your last; thus you'll need a copy of that project!
I have included an updated .html
file (with some simple CSS styling), which you can find in the
cs315_hwk4.zip file.
Put the style/
folder in the top level directory of your project. Any changes you made to the html file for homework 3 you should re-include in this one (though you will need to modify the instructions somewhat!) I have also included a anim_controls.js
class to handle some of the jQuery stuff for you; put this in the top-level directory of your project.
There is also a new README.txt for this assignment. Please fill it out and submit it with the rest of the assignment!
Assignment Details
There are a few different parts to this assignment, all of which will work together. I've listed them below in a possible order of completion, though you may need to jump around a bit
1. Posing with Quaternions
In order to allow your robot to animate smoothly, you should define its pose in quaternions which can then be smoothly interpolated (as we discussed in class). This will involve refactoring your code:
-
Make it so your
pose
global variable containsquat
values, notmat4
values. You should redine your existing pose rotations using thequat.setAxisAngle()
method, in order to test that everything works- You will need to
normalize()
your quaternions (so that they are unit length)
- You will need to
-
You will then need to modify your drawing transformations so that instead of applying the mat4 from the pose, you apply the quat! The easiest way to integrate this will be to convert the quat back into a mat4, using the
mat4.fromQuat
method. - Be sure and test that your posing continues to work!
2. Adjusting the Pose
Now that you're using the correct data format, it is time for the hard work of allowing the user to pose the robot. To do this, you will be extending the trackball rotation algorithm you implemented in hwk 3 to allow you to rotate not just the camera, but each part of the robot!
-
The first step to this is to be able to choose which part of the robot (e.g., which pivot) you want to rotate. To start with, we'll have the user just select the part from a list of radio buttons defined in the html. You can access which radio button is selected via a jQuery selector--you'll have jQuery look up the input of the form (the set of radio buttons) that is checked, and then get the value (the body part) of that button. You can do this using code such as:
$("#pickers input[type='radio']:checked").val();
Note that the value has been set up to be a "key" you can use to access the global
pose
hash, to talk about the specific transformation! -
Once you have determined which body part the user is rotating, you will need to actually rotate it! This will use the same
trackball rotation
you implemented in homework 3. However, instead of apply a rotation to the view matrix, you should apply a rotation to that particular pose!
-
For a nice feeling interaction, you may need to be careful about how you map the mouse's position to the pivot's rotation--since we're not rotating around the center of the screen, you might want to adjust your rotation vector so it reflects a projection onto the unit sphere centered at the pivot's center!
- You can get this location on the screen by applying the MVP matrix to the pivot's location!
- You may also need to watch out for the direction of the rotation, since we're spinning the object on the screen and not the camera.
- You should be able to easily create a new
quat
representing the translation around the desired vector, and then you can compose yourquat
s using thequat.multiply()
method. Note that multiplying quaternions is generally a bit faster than multiplying matrices, so we'll save a bit of time there! - Remember to call the
render()
method after you've adjusted the pivot matrix to redraw your robot with its updated pose! - You should refactoring your code to avoid duplicating the trackball calculations.
-
For a nice feeling interaction, you may need to be careful about how you map the mouse's position to the pivot's rotation--since we're not rotating around the center of the screen, you might want to adjust your rotation vector so it reflects a projection onto the unit sphere centered at the pivot's center!
- This is is one of the trickiest parts of the assignment, so take some time to think through and test this! Think about what transformations you're applying, and how you are determiing those transforms.
Once this is done, the user should be able to select any body part's radio button, and then click and drag to rotate that part on the screen. And because you have a working hierarchical transformation, rotating a pivot should apply to all the children as well!
3. Saving Keyframes
Once the user can give the robot a particular pose, you should add a way to save that pose as a keyframe for your robot's animation.
-
In this system, a keyframe will be defined by a hash of rotations (in quaternion format), where the key of each hash will be the pivot point that should be rotated. For example, a keyframe might look like:
{ "torso" : quat(1,2,3,4), "head" : quat(5,6,7,8), ... }
In other words: a keyframe is simply a
pose
matrix! (Or more accurately--your pose matrix is a keyframe).
-
Your program should store a hash of keyframes, where the "key" for each keyframe is the time (in seconds) when that keyframe should be shown in the animation. For example:
{ 1 : { "torso" : quat(1,2,3,4), "head" : quat(5,6,7,8), ... } 2 : { "torso" : quat(1,2,3,4), ... } 2.3 : { ... } }
You should store this hash as a global variable in your program (
animation
would be an appropriate name). -
When the "Save Keyframe" button is pressed, the provided code will call a
save_keyframe(time)
method, passing in the time (in seconds) which the user specifies the keyframe should be saved. You will need to implement this method (e.g., in your cubebot.js file). The parameter will give you the hash key (and you should already have the current pose) that you can use to save the keyframe. -
As one last piece, you should also display this animation hash to the user (it will look a lot like gibberish, but we'll use it later---and it can potentially help you with debugging).
-
Get a nice string representation of the hash using the built-in
JSON.stringify()
method. Then use jQuery to inject this String into your html---particularly into the "#keyframes" div. Select that div, and then set its html content using jQuery's
.html()
method, In sum, you should do something like:
$('#keyframes').html(JSON.stringify(animation));
-
Get a nice string representation of the hash using the built-in
JSON.stringify()
method. Then use jQuery to inject this String into your html---particularly into the "#keyframes" div. Select that div, and then set its html content using jQuery's
.html()
method, In sum, you should do something like:
- You may wish to skip down to step 5 (which is about saving and loading an animation), to make testing easier so you don't have to spend a lot of time re-posing your robot for keyframes!
4. Animating the Robot
Finally, you can animate your robot! This will involve going through each of your animation keyframes and interpolating between them!
-
When the user hits the "Play Animation button", a method named
play_animation
will be called. You will need to implement this method (e.g., in your cubebot.js file). -
The first step will be to add a basic animation loop using the
requestAnimationFrame()
method we discussed in class. You can use a sentinel variable to determine if you are animating and so should call the animation, or if you are posing and so should not.- And equivalently, you probably want to disable posing while animating!
- You can test that this works by adding a simple rotation transformation to your torso's pivot (like we did in class), and you should be able to see the robot spin.
- Remember that when you call
render()
via therequestAnimationFrame()
method, a time parameter is passed in--you will need to use this to determine what pose to show!
-
Showing the animation involves starting at the first key frame, then interpolating to the next, then interpolating to the next. To enable this, your key frames will need to be in order. But because we're storing animations as a hash, the keys are required to have a particular order. Thus you will need to sort them so you know who comes first!
-
Luckily, there is some built-in methods to help you.
var sorted_keys = Object.keys(a).sort(function(a,b){return a-b}); //sort numerically
- You should do this once when the
play_animation()
method is first called, so you don't waste time sorting.
-
Luckily, there is some built-in methods to help you.
-
You will then need to determine which two keyframes you are interpolating between--and how far you are between them!
- You can get the current "time" of the animation using the parameter passed in by
requestAnimationFrame()
, dividing by 1000 to get the number of seconds. - You can then search through your sorted keys list until you find the entries just before and just after the time of the current frame.
-
You will then need to determine your parametic
t
value, which reflects where you are between the frames. At
of 0 means you're at the earlier keyframe, at
of 1 means you're at the later keyframe, at
of 0.5 means you're halfway between etc.- Some simple algebra and division should be useful for finding this value.
- CAUTION! be careful about when working with the first and last keyframe, so you don't try to move to a keyframe that doesn't exist
-
Finally, you will need to determine the current pose based on this
t
value by interpolating. You can use thequat.slerp()
method to easily interpolate between the quaternions of each keyframe to get the actual pose quaternion- Remember that you'll need to do this interpolation for each body part.
-
quat.slerp()
generally produces best results, but if you're having problemsquat.lerp()
(a linear interpolation rather than a spherical interpolation) might do the job. - Again, You may need to normalize your quaternions (so that they are unit length)
- You can get the current "time" of the animation using the parameter passed in by
-
Once this works, you should be able to play an animation and see your robot dance!
- You do not need to have your animation "loop"; you can just hit the play button to see the dance. If you want to have it loop, you can either (a) have the dance "restart" from keyframe 1, or (b) interpolate between the first and last keyframe over some amount of time (that may give you some odd movements at the reset).
5. Loading an Animation
It would be nice to be able to save and load a specified animation (set of keyframes), so that you don't have to continually repose your robot, and so that you can share animations with your friends!
-
However, there isn't a clean and safe way to have client-side JavaScript save information to a file, so we'll have to do a bit of a manual workaround.
- Remember how you outputted that JSON to the html page? Well that's exactly the information needed to specify an animation!
- After you've developed a pose you like, you should copy-paste this information (yes, seriously) into a
.json
file (e.g.,myDance.json
), which you should place in theassets/
folder. You are required to specify and save a dance for this assignment. - You can load these files by entering the file name (the name of the file without the '.json', and without the full path--it will look for files in
assets/
) into the textbox and clicking "Load Animation". This will call a method namedload_animation(animation)
, passing in an animation hash. You will need to implement this method (e.g., in your cubebot.js file).
- Include a dance file (with at least 3 keyframes) in your project submission.
Extensions
Not a lot of time for extensions on this one. It might be neat to use the viewport to show the individual keyframes as they are saved (e.g., at the bottom of the canvas).
Submitting
BEFORE YOU SUBMIT: make sure your code is fully functional! Grading will be based primarily on functionality. I cannot promise that I can easily find any errors in your code that keep it from running, so make sure it works.
Upload your entire project (including the lib/
folder) to the Hwk4 submission folder on vhedwig (see
the instructions
if you need help).
Also include a filled-in copy of the
README.txt
file in your project directory!
The homework is due at midnight on Fri Oct 17.
Grading
This assignment will be graded out of 25 points:
- [2pt] Your program uses quaternions to represent pose transformations
- [1pt] Your can be rendered in a pose specified by quaternions
- [2pt] Your program uses trackball rotation to adjust the pose of each of the robot's body parts
- [1pt] Selecting a different radio button rotates a different pose
- [1pt] Rotating a parent's pivot also rotates the children parts (via a hierarchical transformation)
- [1pt] Your program continues to support camera rotations via shift-click!
- [1pt] Your program stores an animation (a hash of keyframe poses)
- [2pt] The "save keyframe" button saves a keyframe
- [1pt] You display the current set of keyframes to the user (in
div#keyframe
) - [2pt] The "play animation" button causes your robot become animated and dance!
- [1pt] Your animation plays keyframes in the proper order
- [3pt] Your robot dances by interpolating its entire skeleton between keyframes
- [1pt] Your robot begins at the first keyframe and stops at the last (without errors)
- [1pt] You can load in an animation specified in a .json file
- [1pt] You have included a saved "dance" of at least 3 keyframes
- [1pt] Your HTML file includes additional details on how to control the robot, etc.
- [1pt] Coding style (e.g., good use of helper methods, comments, etc)
- [1pt] You have avoided duplicating code (e.g., for trackball rotations)
- [1pt] README is complete