Advertisement

FBX animation data flips half-way through

Started by September 28, 2018 04:58 PM
5 comments, last by GreenGodDiary 6 years, 4 months ago

Hello guys! 

So, I'm currently working on our senior game-dev project and I'm currently tasked with implementing animations in DirectX.
It's been a few weeks of debugging and I've gotten pretty far, only a few quirks left to fix but I can't figure this one out.

So, what happens is when a rotation becomes too big on a particular joint, it completely flips around.
This seems to be an issue in the FBX data extraction and I've isolated it to the key animation data.

First off, here's what the animation looks like with a small rotation:
Small rotation in Maya
Small rotation in Engine
Looks as expected! (Other than the flipped direction, which I'm not too concerned about at this point; however, if you think this is part of the issue please let me know!)

Now, here's an animation with a big rotation (+360 around Y then back to 0):
Big rotation in Maya
Big rotation in Engine

As you can see the animation completely flips here and there.

Here's how the local animation data for each joint is retrieved:


	while (currentTime < endTime) 
	{
		FbxTime takeTime;
		takeTime.SetSecondDouble(currentTime);

		// #calculateLocalTransform
		FbxAMatrix matAbsoluteTransform = GetAbsoluteTransformFromCurrentTake(skeleton->GetNode(), takeTime);
		FbxAMatrix matParentAbsoluteTransform = GetAbsoluteTransformFromCurrentTake(skeleton->GetNode()->GetParent(), takeTime);
		FbxAMatrix matInvParentAbsoluteTransform = matParentAbsoluteTransform.Inverse();
		FbxAMatrix matTransform = matInvParentAbsoluteTransform * matAbsoluteTransform;
      
		// do stuff with matTransform
	}

	// GetAbsoluteTransformFromCurrentTake() returns:
	// pNode->GetScene()->GetAnimationEvaluator()->GetNodeGlobalTransform(pNode, time);

This seems to work well, but on the keys when the flip happens it returns a matrix where the non-animated rotations (Y and Z in this case) have a value of 180, rather than 0.
The Y value also starts "moving" in the opposite direction.

From the Converter we save out the matrix components as T, R, S (R in Euler) and during import in engine the rotation is converted to a quaternion for interpolation.


I'm not sure what else I can share that might help give a clue as to what the issue is, but if you need anything to help me just let me know!

Any help/ideas are very much appreciated! ❤️

    E. Finoli

I answered this here: 

 

 

During export, inject your own keyframes, eliminate redundancies as described, and export a more detailed animation with enough keyframes to ensure this does not happen.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement

Thank you for your response. I have read that post before and while I'm sure your approach is far better than mine, it's not really what I'm looking for at this time.
We do not require a full-featured animation system; we just need to be able to play and switch animation clips on certain models. 
I'm not really sure how what you said in that post is supposed to help fix our current issue either, since you don't really go into how you get the data from FBX (other than mentioning GetNodeGlobalTransform).
I know our approach is not optimal but I also know it should work (partly because our teacher used the same method), and getting it to work is our priority right now.

If you'll tell me that our approach simply wont work for whatever reason that's fine, but I'd need to know why that is.

Best,

E. Finoli

Your problem is that interpolations over a large distance cause incorrect and unpredictable interpolations.  This is normal with Euler and with baked animations.  For example, try interpolating spherically from 0 to 360.  It’s an inherent flaw with the approach, but I didn’t point you to that post to change your approach.  The solution is the same either way.

My algorithm outlined there is the exact fix for this.  If you insert keyframes at every 0.1 seconds you will never have a long-distance interpolation.  If you had keyframes at 0 and 360, and you inserted keyframes at 10, 20, 30, 40, 50, etc. (as per my algorithm in that post), and then removed redundant keyframes using the method described in that post, you will end up with something like 0, 90, 180, 360 as keyframes, and this makes the interpolation non-ambiguous, solving issues that cannot otherwise be solved with baked or Euler systems.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

38 minutes ago, L. Spiro said:

Your problem is that interpolations over a large distance cause incorrect and unpredictable interpolations.  This is normal with Euler and with baked animations.  For example, try interpolating spherically from 0 to 360.  It’s an inherent flaw with the approach, but I didn’t point you to that post to change your approach.  The solution is the same either way.

My algorithm outlined there is the exact fix for this.  If you insert keyframes at every 0.1 seconds you will never have a long-distance interpolation.  If you had keyframes at 0 and 360, and you inserted keyframes at 10, 20, 30, 40, 50, etc. (as per my algorithm in that post), and then removed redundant keyframes using the method described in that post, you will end up with something like 0, 90, 180, 360 as keyframes, and this makes the interpolation non-ambiguous, solving issues that cannot otherwise be solved with baked or Euler systems.


L. Spiro

Ohh now I understand, I didn't quite connect the dots there.
In my code, however, I get the global pose and parent inverse global pose every 1 frame, by incrementing `currentTime` by the time of a frame (1 / 24 ), like so:


float frameTime = 1.0 / 24.0;
int keyCount = anim_curve_rotY->KeyGetCount(); //ghetto approach but works

float endTime = frameTime * keyCount;
float currentTime = 0.0;

while (currentTime < endTime) 
{
	FbxTime takeTime;
	takeTime.SetSecondDouble(currentTime);

	// #calculateLocalTransform
	FbxAMatrix ... = GetAbsoluteTransformFromCurrentTake(skeleton->GetNode(), takeTime);
	FbxAMatrix ...
	...
	...

	currentTime += frameTime;
}



Shouldnt this avoid such a problem? Also, on export from maya I set the option to bake every frame, so again I'm not quite sure how the long-distance interpolation issue can be a thing in my case. Would love it if you could shed some light on that!

Again, thank you for your assistance!

Edit: added code for clarity

The issue has been resolved!

Though I havent confirmed it, I believe the issue was that when we got the key data we used to store it in Euler and then convert to Quaternion. Now we get quaternion data from the start and the issue has gone away. I have fixed some other stuff as well, which is why I can't be certain that was the issue, but I suppose it makes sense.

This topic is closed to new replies.

Advertisement