CS56 Computer Animation: Lab 8

In which, we implement blending and reorienting between motions

The goals of this lab are to


Get the source


The motion assignment has been added to your AnimationFramework repository. To get the source, run

> cd cs56/AnimationToolkit 
> git pull
> cd build
> cmake ..
> make

You should now have a new directory under assignments called a8-blend.


Assignment 8: Cross-fade away

Due November 22




Question 1: Blend (5 points)


In this question, you will blend the walk motion with a strafe motion. Implement your solution in assignments/a8-blend/ABlend.cpp inside of the method blend().

// m1: First input motion
// m2: Second input motion
// alpha: blend value
// returns the result of m1 * (1-alpha) + m2
AMotion blend(const AMotion& m1, const AMotion& m2, double alpha) 

Your implementation should be based on the code from class

duration = duration1 * (1-alpha) + duration2 * alpha
deltaT = 1/fps
for t from 0 to duration, increment by deltaT
    APose pose1 = pose from motion1
    APose pose2 = pose from motion2
		APose newPose = lerp pose1 and pose2 by alpha
		result.appendKey(newPose)
return result

To run the demo from the build directory, type

build> ../bin/a8-blend 



Controls

Your program should have the following features

  1. blend() shoudl create and return a new motion. Use AMotion::appendKey to add poses to your blended motion
  2. The motion returned by blend should have a framerate equal to motion1. Hint: Call setFramerate() before returning result.
  3. The duration of the blended motion should be based on the blend factor, e.g. duration = duration1 * (1-alpha) + duration2 * alpha.
  4. Use APose::Lerp to blend poses from motion1 and motion2


Question 2: Splice (5 points)


In this question, you will modify the walk motion to have the arm motion from gangnam style dancing. Implement your solution in assignements/a8-blend/ASplice.cpp.

// orig: Starting input motion
// upper: Motion for the modifying the upper body (all joints which are descendants of the spine)
// alpha: blend value
// returns a new motion such that 
//     the lower body matches the motion in 'orig'
//     the upper body is the result of blending the upper body motion with 
//         the original's upper body motion, e.g. newupper = upper * (1-alpha) + orig * alpha
AMotion spliceUpperBody(const AMotion& orig, const AMotion& upper, double alpha) 

For this question, use keys for blending instead of durations, e.g.

for i in range numKeys in orig
    APose pose = orig.getKey(i)
		// compute new pose and either call appendKey() or editKey() set the pose

To run the demo from the build directory, type

build> ../bin/a8-splice 



Controls

  • NOTE: If you implement your solution so that key '0' shows the original instead of key '2', that is ok.

    Your program should have the following features

    1. spliceUpperBody() should create and return a new motion. Use AMotion::appendKey to add poses to your blended motion
    2. The motion returned by blend should have a framerate equal to orig
    3. The upper body consists of all descendants of the joint "Beta:Spine1"
    4. Use AQuaternion::Slerp to blend local rotations for the upper body
    5. Use the sequence of poses from upper starting at key ID = 120


    Question 3: Zombie Arms (10 points)


    In this question, you will modify the walk motion to have zombie arms. Implement your solution in assignements/a8-blend/AZombieArms.cpp.

    To run the demo from the build directory, type

    build> ../bin/a8-zombie 
    


    Controls

    Part 1: Freeze Arms (5 points)

    To freeze the arms, we will set both the shoulders and elbows to a constant rotation. Implement your solution in ComputeArmFreeze.

    // motion: input motion
    // returns a new motion with the shoulders and elbows outstretched
    AMotion ComputeArmFreeze(const AMotion& motion)
    

    Freeze arms


    Freeze arms should have the following features:

    1. The left shoulder should local rotation equal to XYZ euler angles (-53, -88, -33)
    2. The right shoulder should local rotation equal to XYZ euler angles (14, 88, -33)
    3. Both elbows should local rotation equal to XYZ euler angles (0, 23, 0)
    4. The motion returned should have a framerate equal to motion
    5. You should modify the joints with names: "Beta:LeftArm", "Beta:RightArm" (shoulders), "Beta:LeftForeArm", and "Beta:RightForeArm" (elbows).

    Part 2: Offset Arms (5 points)

    To offset the arms, we will apply an offset rotation to shoulder's animation curve. We will freeze the elbows as before. Implement your solution in ComputeArmOffset.

    // motion: input motion
    // returns a new motion with the shoulders and elbows outstretched but moving
    AMotion ComputeArmOffset(const AMotion& motion)
    

    To compute the offset, we will use the local rotation of the should on the first frame to compute an offset rotation. \( R_{offset} = R_{desired} (R_i^j)^{-1} \) where $R_i^j$ is the local to parent rotation for the shoulder joint. $R_{desired}$ is the target rotation we want for the joint. For the right joint, the desired rotation will be the XYZ euler angles (14, 88, -33). For the left joint, the desired rotation will be the XYZ euler angles (-53, -88, -33).

    Offset arms


    Offset arms should have the following features:

    1. The left shoulder's desired rotation is XYZ euler angles (-53, -88, -33)
    2. The right shoulder's desired rotation is XYZ euler angles (14, 88, -33)
    3. Both elbows should local rotation equal to XYZ euler angles (0, 23, 0). These frozen as before.
    4. The motion returned should have a framerate equal to motion
    5. You should modify the joints with names: "Beta:LeftArm", "Beta:RightArm" (shoulders), "Beta:LeftForeArm", and "Beta:RightForeArm" (elbows).


    Question 4: Reorient (5 points)


    In this question, you will modify a motion so it starts in a new location and orientation. You will need to perform the same calculation in crossfade to align two motions. Implement your solution in assignements/a8-blend/AReorient.cpp

    // motion: the input motion
    // pos: the new starting position of the motion
    // heading: the new starting orientation as an angle (radians) around the world UP axis (e.g. Y)
    // returns a new motion whose starting pose has a root position and rotation that matches pos and heading
    AMotion reorient(const AMotion& motion, const AVector3& pos, double heading)
    

    To run the demo from the build directory, type

    build> ../bin/a8-reorient 
    




    Controls

    Your demo should have teh following features

    1. The returned motion should have the same framerate as the input
    2. The root position and heading of the first frame should match the desired position and heading. All subsequent frames should be offset so the resulting motions looks like the original.
    3. The best solution should only align the headings. However, you may implement a simpler solution which sets the full XYZ orientation to match the heading. This will affect how motions where the root may tilt (such as jumps) are reoriented.


    Question 5: Crossfade (20 points)


    In this question, you will implement a cross fade transition between two motins. You have a application with a GUI to help you test. You will implement your solution in libsrc/animation/AMotionBlender-basecode.cpp.

    To run the demo from the build directory, type

    build> ../bin/a8-crossfade 
    


    // Member function: AMotionBlender::blend
    //
    // param motion1: the starting motion of the transition
    // param motion2: the ending motion of the transition
    // param startKeyId: the frame of the starting motion at which to start blending
    // param endKeyId: the frame of the ending motion at which to start blending
    // param numBlendFrames: the number of frames in the blend
    // param blend: the computed crossfade motion. This motion should contain

    Part 1 (5 points) Implement AMotionBlender::append.

    This method should grab the keys in range [startKeyId, endKeyId) from the input motion and append them to the output motion. Test your new function by extending AMotionBlender::blend() to append the keys [0, startKeyId) from motion 1 and the keys [endKeyId, motion2.getNumKeys) from motion 2. Your assignment should look as follows: the walk motion plays and then snaps back to the origin to play the jump.


    Part 2 (5 points) Implement AMotionBlender::crossfade.

    So far, the transition snaps from the first motion to the second motion. Fix this problem by implementing AMotionBlender::crossfade. This function should blend the frame between motion 1 and motion 2 and append the blended frames to 'blend'. Extend AMotionBlender::blend to call your new crossfade function. Now, your assignment should look as follows: the motion is smooth but the character slides to the origin to play the jump motion.


    Part 3 (5 points) Align the positions in AMotionBlender::align.

    Now we have a smooth result but the jump unrealistically moves back to the origin. We want the jump to occur at the character's current location! We will fix this by aligning the sequence from motion 2 with the start transition key from motion 1. Note that you can re-use the logic from reorient!

    Translate the keys so that the second sequence is aligned with the first sequence. Extend AMotionBlender::blend to call align. Your assignment should now look as follows.


    However, note that blending between a turn and jump still doesn't work! Let's fix that.


    Part 4 (5 points) Align the root headings in AMotionBlender::align.

    Reorient the keys (e.g. rotate and translate) so that the second sequence is aligned with the first. Now your blend should look good in all cases.


    Testing tip: A correct crossfade should return the original motion whenever you crossfade between the same motion. For example, try blending two walk motions over the starting 10 frames. The result should look identical to the original motion.


    Extra Credit


    Option 1 (2 points) Create a unique animation or character.

    Option 2 (2 points) Infinite motion. Create a character that can walk or dance or jump forever by repeatedly blending the walking motion with itself.

    Option 3 (2 points) Mirror. Try creating a mirrored motion which plays forward and backward.

    Option 4 (4 points) Motion analysis. Detect when feet are in contact with the floor. Draw a cube at the location of the contact so we can visualize it.

    Be sure to include a gif or video of your demo for full credit!


    Submission Requirements (1 points)