Im currently trying to implement animation blending (typical run animation + gun shooting). I base myself on this code to implement MD5 skeleton into my game:
Now I have a problem, I figure how to interpolate two animations using the function from the resource above: InterpolateSkeletons, however I can’t figure out how to add animations. If I interpolate the run animation with the gun shooting animation at both 50% well, both movement is done at 50%.
My question is how can I add the 2 skeleton (or more) in order to have a final skeleton to be able to compute the vertices.
struct AnimationFile{
int NumKeyFrames;
KeyFrame frames[NumBones][NumKeyFrames];
};
Make it:
struct AnimationFile{
int NumKeyFrames;
float AnimWeights[NumBones]; // = 1.0 by default
KeyFrame frames[NumBones][NumKeyFrames];
};
Your code so far is as if AnimWeights[]={1.0}
struct AnimationFile{
int NumKeyFrames;
float AnimWeights[NumBones]; // = 1.0 by default
KeyFrame frames[NumBones][NumKeyFrames];
};
void BlendFrames(
const KeyFrame* frame1, const KeyFrame* frame2, float lerp_value, // input-args . lerp_value=0..1
KeyFrame* outFrame) // output-args
{
...// here compute outFrame via a lerp or slerp of frame1 and frame2
}
void GetKeyFrames(
const AnimationFile* anim, float Time, // input-args
KeyFrame outFrames[NUM_BONES]) // output-args
{
... // here fill-in the outFrames[] with lerp or slerp-ed transformation values between keyframes.
}
void BlendAnims(
const AnimationFile* anim1,const AnimationFile* anim2, float Time, float lerp_value, // input-args
KeyFrame outFrames[NUM_BONES]) // output-args
{
KeyFrame frames1[NUM_BONES];
KeyFrame frames2[NUM_BONES];
GetKeyFrames(anim1,Time, frames1);
GetKeyFrames(anim2,Time, frames2);
for(int i=0;i<NUM_BONES;i++){
float w1 = anim1->AnimWeights[i] * lerp_value;
float w2 = anim2->AnimWeights[i] * (1.0f- lerp_value);
float w_sum = w1 + w2;
w1/= w_sum; // normalize
w2/= w_sum;
BlendFrames(&frames1[i], &frames2[i], w1, &outFrames[i]);
}
}
All you have to add to the rendering code are the “w1”, “w2” and “w_sum” . Right now, your code has “w1 = lerp_value”, “w2 = 1.0-lerp_value”, “w_sum = 1.0f” .
I see what you mean, but the result will still be ex: 50% ~ 50% between 2 anim…
What I need is more like a “non-linear action” where the first anim (the walk animation) will still run at 100% and the hand in the air anim will also still run at 100%, and the final skeleton is basically walk + hand in air.
In other words the two animation are added…
From what I see your code will end up blending the 2 anim based on the anim weight factor of every bone. Which require me to set a value manually on every bone right?
Im trying to implement the pseudo code above based on your recommendations… I integrate AnimWeights and set like you said 0.1, 0.9 however the result is rather strange this is my code:
void Md5BlendAction( md5 *md5,
md5action *md5action0,
md5action *md5action1,
float lerp )
{
unsigned int i = 0;
while( i != md5->n_joint )
{
v4 q1,
q2;
float w1 = md5action0->action_weight[ i ] * lerp,
w2 = md5action1->action_weight[ i ] * ( 1.0f - lerp ),
ws = w1 + w2;
w1 /= ws;
w2 /= ws;
md5->md5joint[ i ].loc.x = ( md5action0->md5joint[ i ].loc.x * w1 ) +
( md5action1->md5joint[ i ].loc.x * w2 );
md5->md5joint[ i ].loc.y = ( md5action0->md5joint[ i ].loc.y * w1 ) +
( md5action1->md5joint[ i ].loc.y * w2 );
md5->md5joint[ i ].loc.z = ( md5action0->md5joint[ i ].loc.z * w1 ) +
( md5action1->md5joint[ i ].loc.z * w2 );
q1.x = md5action0->md5joint[ i ].quat.x * w1;
q1.y = md5action0->md5joint[ i ].quat.y * w1;
q1.z = md5action0->md5joint[ i ].quat.z * w1;
q1.w = md5action0->md5joint[ i ].quat.w * w1;
q2.x = md5action1->md5joint[ i ].quat.x * w2;
q2.y = md5action1->md5joint[ i ].quat.y * w2;
q2.z = md5action1->md5joint[ i ].quat.z * w2;
q2.w = md5action1->md5joint[ i ].quat.w * w2;
QuatSlerp( &q1,
&q2,
lerp,
&md5->md5joint[ i ].quat );
QuatNormalize( &md5->md5joint[ i ].quat,
&md5->md5joint[ i ].quat );
++i;
}
}
The cause seems to be the quaternion… Am I right to do the operation on the joints or should I do it on the join weights? Or I am missing something?
q1 and q2 are not normalized, maybe this is troubling you.
Plus, simply multiplying quats by a value may be a wrong. (how do you normalize a {0,0,0,0}, does the slerp work nicely in that case)
I’m out of ideas; things seem to work automatically for me with dual-quats. Otherwise I’d try summing delta-angles (HPB) instead of quat slerps and such.
I already told you about both of these things, though.