summaryrefslogtreecommitdiffstats
path: root/dev/a4-dance/animated_character.cc
blob: 84edcdea042f425c65298292fe24d7badcc23f7b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#include "animated_character.h"
#include "amc_util.h"

#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>


AnimatedCharacter::AnimatedCharacter(const std::string &asf_filename) :
    fps_(120), elapsed_since_last_frame_(0.0f), current_frame_(0)
{
    LoadSkeleton(asf_filename);
}

AnimatedCharacter::AnimatedCharacter() :
    fps_(120), elapsed_since_last_frame_(0.0f), current_frame_(0)
{
}

AnimatedCharacter::~AnimatedCharacter() {
}


void AnimatedCharacter::LoadSkeleton(const std::string &asf_filename) {
    skeleton_.LoadFromASF(asf_filename);
}


void AnimatedCharacter::Play(const MotionClip &motion_clip) {
    motion_queue_.clear();
    motion_queue_.push_back(motion_clip);
    current_frame_ = 0;
}


void AnimatedCharacter::Queue(const MotionClip &motion_clip) {
    if (motion_queue_.size() == 0) {
        Play(motion_clip);
    }
    else {
        motion_queue_.push_back(motion_clip);
    }
}


void AnimatedCharacter::ClearQueue() {
    motion_queue_.clear();
}


void AnimatedCharacter::OverlayClip(const MotionClip &clip, int num_transition_frames) {
    overlay_clip_ = clip;
    overlay_transition_frames_ = num_transition_frames;
    overlay_frame_ = 0;
}


void AnimatedCharacter::AdvanceAnimation(double dt) {
    if (motion_queue_.size() == 0) {
        pose_ = Pose();
    }
    else {
        elapsed_since_last_frame_ += dt;
        
        double frames_to_advance = fps_ * elapsed_since_last_frame_;
        double whole_frames;
        double frac = modf(frames_to_advance, &whole_frames);
        int nframes = (int)whole_frames;
        elapsed_since_last_frame_ = frac / fps_;
        
        for (int i=0; i<nframes; i++) {
            // advance the main motion track
            current_frame_++;
            // handle end case
            if (current_frame_ >= motion_queue_[0].size()) {
                // loop back to the first frame
                current_frame_ = 0;
                // if there are more motions in the queue then pop this one and goto the next
                if (motion_queue_.size() > 1) {
                    motion_queue_.erase(motion_queue_.begin());
                }
            }
            
            // advance the overlay clip if there is one
            if (overlay_clip_.size()) {
                overlay_frame_++;
                // handle end case
                if (overlay_frame_ >= overlay_clip_.size()) {
                    // done playing overlay, reset frame counter and clear the overlay clip
                    overlay_frame_ = 0;
                    overlay_clip_ = MotionClip();
                }
            }
            
            // update the pose based on new frames
            CalcCurrentPose();

            // add to the translation matrix for the case when relative root motion is used
            accum_translation_matrix_ = accum_translation_matrix_ * pose_.root_relative_translation();
        }
    }
}


void AnimatedCharacter::CalcCurrentPose() {
    if (!overlay_clip_.size()) {
        // no overaly track, motion is entirely from the base track (i.e., the motion queue)
        pose_ = motion_queue_[0][current_frame_];
    }
    else {
        // there is an active overlay track
        if (overlay_frame_ < overlay_transition_frames_) {
            // fade in the overlay
            float alpha = (float)overlay_frame_/(float)overlay_transition_frames_;
            pose_ = motion_queue_[0][current_frame_].Lerp(overlay_clip_[overlay_frame_], alpha);
        }
        else if (overlay_frame_ > overlay_clip_.size() - overlay_transition_frames_) {
            // fade out the overlay
            float alpha = (float)(overlay_clip_.size() - overlay_frame_)/(float)overlay_transition_frames_;
            pose_ = motion_queue_[0][current_frame_].Lerp(overlay_clip_[overlay_frame_], alpha);
        }
        else {
            // overlay is completely faded in, we don't see the base track at all
            pose_ = overlay_clip_[overlay_frame_];
        }
    }
}


Skeleton* AnimatedCharacter::skeleton_ptr() {
    return &skeleton_;
}


void AnimatedCharacter::set_fps(int fps) {
    fps_ = fps;
}


int AnimatedCharacter::fps() {
    return fps_;
}



void AnimatedCharacter::Draw(const Matrix4 &model_matrix, const Matrix4 &view_matrix, const Matrix4 &proj_matrix,
                             bool use_absolute_position)
{
    Matrix4 character_root_transform;
    if (use_absolute_position) {
        // set root position based on the absolute position in the mocap data
        character_root_transform = model_matrix * pose_.RootTransform();
    }
    else {
        // set root position based on the relative updates accumulated each frame
        character_root_transform = model_matrix * accum_translation_matrix_ * pose_.root_rotation();
    }
    
    for (int i=0; i<skeleton_.num_root_bones(); i++) {
        DrawBoneRecursive(skeleton_.root_bone(i), character_root_transform, view_matrix, proj_matrix);
    }
}


void AnimatedCharacter::DrawBoneRecursive(const std::string &bone_name, const Matrix4 &parent_transform,
                                          const Matrix4 &view_matrix, const Matrix4 &proj_matrix)
{
    // Step 1:  Draw this bone
    
    /** TODO: You will need to define a current transformation matrix for this bone that takes into account not just the parent_transform but also the local rotation of the bone due to the current pose.
     
        Think of the vertices that make up the geometry of each bone as being defined in "bone space", where the joint that the bone rotates around is located at the origin and the bone extends in the direction and length specified by the skeleton. (See Skeleton::BoneDirectionAndLength()).
     
        To determine which matrices need to be composed to create the current transformation matrix and the order to multiply them together, think about what needs to happen to each vertex of a cylinder defined in "bone space" in order to get the vertex to the correct position in 3D space.
     
        First, the vertex must be transformed into the bone's "rotation axis space" because the rotation axes are not guaranteed to line up perfectly with the bone's X,Y,Z axes.  The bone's rotation axes are a property of the skeleton -- they are set for each skeleton and do not change for each pose.  You can access a matrix that transforms from "bone space" to "rotation axis space" from the skeleton_ member variable.
     
        Second, now that the vertices are in the bone's "rotation axis space", the rotation from the character's current pose can be applied.  The current pose is stored in the pose_ member variable.

        Third, with the rotations applied relative to the appropriate rotation axes, the vertices must now be transformed back into regular "bone space".  At this point, the bone should be properly rotated based upon the current pose, but the vertices are still defined in "bone space" so they are close to the origin.
     
        Finally, the vertices need to be tranformed to the bone's parent space.
    */

    /*
        To start, we give you a current transformation matrix (ctm) that only takes this last step into account.
    */
    Matrix4 ctm = parent_transform;

    
    // Here is a good way to check your work -- draw the coordinate axes for each
    // bone.  To start, this will just draw the axes for the root node of the
    // character, but once you add the recursive call to draw the children, this
    // will draw the axes for each bone.
    Matrix4 S = Matrix4::Scale(Vector3(0.15f, 0.15f, 0.15f));
    // quick_shapes_.DrawAxes(ctm * S, view_matrix, proj_matrix);

    
    // TODO: Eventually, you'll want to draw something different depending on which part
    // of the body is being drawn.  An if statement like this is an easy way to do that.
    if (bone_name == "lhipjoint" || bone_name == "rhipjoint") {
        Matrix4 hipjoint = Matrix4::Scale(Vector3(0.15f, 0.15f, 0.15f));
        quick_shapes_.DrawSphere(ctm * hipjoint, view_matrix, proj_matrix, Color(0,0,0));
    }
    if (bone_name == "lfemur" || bone_name == "rfemur") {
        Matrix4 femur_sc = Matrix4::Scale(Vector3(0.15f, 0.2f, 0.2f));
        Matrix4 femur_tr = Matrix4::Translation(Vector3(0, -0.5f, 0));
        Matrix4 femur_rt = Matrix4::RotationZ(GfxMath::ToRadians(30.0f));
        quick_shapes_.DrawSphere(ctm * femur_sc * femur_rt * femur_tr, view_matrix, proj_matrix, Color(0, 0, 0));
    }
    if (bone_name == "ltibia" || bone_name == "rtibia") {
        if (bone_name == "ltibia") {
            Matrix4 tibia_sc = Matrix4::Scale(Vector3(0.1f, 0.4f, 0.1f));
            Matrix4 tibia_tr = Matrix4::Translation(Vector3(0.0f, -0.5f, 0.0f));
            Matrix4 tibia_rt = Matrix4::RotationZ(GfxMath::ToRadians(30.0f));
            quick_shapes_.DrawSphere(ctm * tibia_sc * tibia_rt * tibia_tr, view_matrix, proj_matrix, Color(0, 0, 0));
        }
        else {
            Matrix4 tibia_sc = Matrix4::Scale(Vector3(0.1f, 0.4f, 0.1f));
            Matrix4 tibia_tr = Matrix4::Translation(Vector3(0.0f, -0.5f, 0.0f));
            Matrix4 tibia_rt = Matrix4::RotationZ(GfxMath::ToRadians(-30.0f));
            quick_shapes_.DrawSphere(ctm * tibia_sc * tibia_rt * tibia_tr, view_matrix, proj_matrix, Color(0, 0, 0));
        }
        
    }
    //Feet did not render properly, get rid of them
    if (bone_name == "lowerback") {
        Matrix4 lowerback_sc = Matrix4::Scale(Vector3(0.15f, 0.15f, 0.15f));
        Matrix4 lowerback_tr = Matrix4::Translation(Vector3(0.0f, -1.0f, 0.0f));
        quick_shapes_.DrawSphere(ctm * lowerback_sc * lowerback_tr, view_matrix, proj_matrix, Color(0, 0, 0));
    }
    if (bone_name == "upperback") {
        quick_shapes_.DrawLineSegment(ctm * S, view_matrix, proj_matrix, Color(0, 0, 0), Point3(0, 0.4, 0), Point3(0, -0.4, 0), 0.8);
    }
    if (bone_name == "thorax") {
        quick_shapes_.DrawLineSegment(ctm * S, view_matrix, proj_matrix, Color(0, 0, 0), Point3(0, 0.4, 0), Point3(0, -0.4, 0), 0.6);
    }
    if (bone_name == "lowerneck" || bone_name == "upperneck") {
        quick_shapes_.DrawLineSegment(ctm * S, view_matrix, proj_matrix, Color(0, 0, 0), Point3(0, 0.4, 0), Point3(0, -0.4, 0), 0.2);
    }
    if (bone_name == "head") {
        Matrix4 head_sc = Matrix4::Scale(Vector3(0.15f, 0.2f, 0.15f));
        quick_shapes_.DrawSphere(ctm * head_sc, view_matrix, proj_matrix, Color(0, 0, 0));
    }
    if (bone_name == "lclavicle" || bone_name == "rclavicle") {
        Matrix4 clavicle_sc = Matrix4::Scale(Vector3(0.2f, 0.1f, 0.1f));
        quick_shapes_.DrawSphere(ctm * clavicle_sc, view_matrix, proj_matrix, Color(0, 0, 0));
    }
    if (bone_name == "lhumerus" || bone_name == "rhumerus" || bone_name == "lradius" || bone_name == "rradius") {
        if (bone_name == "lhumerus" || bone_name == "rhumerus") {
            Matrix4 humerus_sc = Matrix4::Scale(Vector3(0.08f, 0.04f, 0.04f));
            quick_shapes_.DrawSphere(ctm * humerus_sc, view_matrix, proj_matrix, Color(0, 0, 0));
        }
        else {
            if (bone_name == "rradius") {
                Matrix4 radius_sc = Matrix4::Scale(Vector3(0.15f, 0.05f, 0.05f));
                Matrix4 radius_rt = Matrix4::RotationZ(GfxMath::ToRadians(-90.0f));
                Matrix4 radius_tr = Matrix4::Translation(Vector3(0.0f, 1.0f, 0.0f));
                quick_shapes_.DrawCylinder(ctm * radius_sc * radius_rt * radius_tr, view_matrix, proj_matrix, Color(0, 0, 0));
            }
            else {
                Matrix4 radius_sc = Matrix4::Scale(Vector3(0.15f, 0.05f, 0.05f));
                Matrix4 radius_rt = Matrix4::RotationZ(GfxMath::ToRadians(90.0f));
                Matrix4 radius_tr = Matrix4::Translation(Vector3(0.0f, 1.0f, 0.0f));
                quick_shapes_.DrawCylinder(ctm * radius_sc * radius_rt * radius_tr, view_matrix, proj_matrix, Color(0, 0, 0));
            }
        }
    }
    if (bone_name == "lwrist" || bone_name == "rwrist") {
        if (bone_name == "lwrist") {
            Matrix4 wrist_sc = Matrix4::Scale(Vector3(0.1f, 0.05f, 0.05f));
            Matrix4 wrist_tr = Matrix4::Translation(Vector3(-0.5f, 0.0f, 0.0f));
            quick_shapes_.DrawSphere(ctm * wrist_sc * wrist_tr, view_matrix, proj_matrix, Color(0, 0, 0));
        }
        else {
            Matrix4 wrist_sc = Matrix4::Scale(Vector3(0.1f, 0.05f, 0.05f));
            Matrix4 wrist_tr = Matrix4::Translation(Vector3(0.5f, 0.0f, 0.0f));
            quick_shapes_.DrawSphere(ctm * wrist_sc * wrist_tr, view_matrix, proj_matrix, Color(0, 0, 0));
        }
        
    }
    if (bone_name == "lhand" || bone_name == "rhand" || bone_name == "lthumb" || bone_name == "rthumb" || bone_name == "rfingers" || bone_name == "lfingers") {
        if (bone_name == "lhand" || bone_name == "rhand") {
            Matrix4 hand_sc = Matrix4::Scale(Vector3(0.05f, 0.05f, 0.05f));
            quick_shapes_.DrawSphere(ctm * hand_sc, view_matrix, proj_matrix, Color(0, 0, 0));
        } else if (bone_name == "lthumb" || bone_name == "rthumb") {
            Matrix4 thumb_sc = Matrix4::Scale(Vector3(0.03f, 0.03f, 0.03f));
            Matrix4 thumb_tr = Matrix4::Translation(Vector3(1.0f, 0.0f, 0.0f));
            quick_shapes_.DrawCylinder(ctm * thumb_sc * thumb_tr, view_matrix, proj_matrix, Color(0, 0, 0));
        } else if (bone_name == "rfingers" || bone_name == "lfingers") {
            Matrix4 fingers_sc = Matrix4::Scale(Vector3(0.01f, 0.01f, 0.01f));
            quick_shapes_.DrawCylinder(ctm * fingers_sc, view_matrix, proj_matrix, Color(0, 0, 0));
        }
    }
    
    
    
    // Step 2: Draw the bone's children
    /**
     
    // TODO: Determining the proper child_root_transform is the key here.  It depends on the
    // current transformation matrix, but you also need to take into account the
    // direction and length of the bone in order to reach the root of the children.
    Matrix4 child_root_transform = ????;
     
    for (int i=0; i<skeleton_.num_children(bone_name); i++) {
        DrawBoneRecursive(skeleton_.child_bone(bone_name, i), child_root_transform, view_matrix, proj_matrix);
    }
    **/

    CalcCurrentPose();
    Matrix4 child_root_transform = ctm * skeleton_.RotAxesSpaceToBoneSpace(bone_name) * pose_.JointRotation(bone_name) * skeleton_.BoneSpaceToRotAxesSpace(bone_name) * skeleton_.BoneSpaceToChildrenSpace(bone_name);
     
    for (int i=0; i<skeleton_.num_children(bone_name); i++) {
        DrawBoneRecursive(skeleton_.child_bone(bone_name, i), child_root_transform, view_matrix, proj_matrix);
    }
}