View Full Version : Doom3 MD5 Blend Skeleton

06-05-2010, 09:18 PM

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.

Basically to reproduce the same as this:



Ilian Dinev
06-05-2010, 11:43 PM
Animation weight per bone, normalize sums.

06-06-2010, 12:22 AM
Allright any code or pseudo code that lead me on the way...

Ilian Dinev
06-08-2010, 02:24 AM
Right now you have something like

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(&amp;frames1[i], &amp;frames2[i], w1, &amp;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" .

06-08-2010, 10:41 PM
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?

Or am I missing something?

Ilian Dinev
06-08-2010, 10:48 PM
Just set the left arm's weight in the walk-anim to 0.1, and 0.9 in the wave anim.

You can try additive-transformation (kinda like additive-blending, trouble happens on overflow), too.

07-09-2010, 05:00 AM
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,

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( &amp;q1,
&amp;md5->md5joint[ i ].quat );

QuatNormalize( &amp;md5->md5joint[ i ].quat,
&amp;md5->md5joint[ i ].quat );


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?


Cheers mate!

Ilian Dinev
07-09-2010, 06:42 AM
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)

07-09-2010, 07:38 PM
I try to normalize q1 and q2 no success...

What do you mean by "simply multiplying quats by a value may be a wrong."?

Slerp is not the issue it never happen to normalize 0,0,0,0.

In your pseudo code above after calculating w2 I can't see where you plug it.

Would you mind sharing you own code for BlendFrames?

Ilian Dinev
07-11-2010, 02:31 AM
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.

07-15-2010, 08:05 PM
Im still stuck... anybody?