Doom3 MD5 Blend Skeleton

Hi!

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:

http://tfc.duke.free.fr/coding/md5-specs-en.html

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:

TIA

Animation weight per bone, normalize sums.

Allright any code or pseudo code that lead me on the way…

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(&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?

Or am I missing something?

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.

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?

TIA,

Cheers mate!

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 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?

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.

Im still stuck… anybody?