Sign in to follow this  

FbxSdk: Bone rotations differ from source file

This topic is 678 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey guys,

 

I've spent a few days messing about with FBXSDK, here's where I'm at currently:

Got the meshes loaded (sorted into submeshes per material) with bone weights and indexes.

Bone hierarchy is also imported properly.

 

I calculate the inverse bindpose matrix by doing this:

FbxAMatrix transformMatrix;
FbxAMatrix transformLinkMatrix;
FbxAMatrix globalBindPoseInverseMatrix;
lCluster->GetTransformMatrix(transformMatrix);
lCluster->GetTransformLinkMatrix(transformLinkMatrix);
globalBindPoseInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; // *matrixGeo;

When computing bone matrix to send to vertex shader, I do the following:

VOID Model::CalculateCombinedMatrix(Model_Animation *animdata)
{
	for (INT b = 0; b < BoneListCount; b++)
	{
		XMMATRIX transformation = XMLoadFloat4x4(&BoneList[b]->BindPoseInverseMatrix);
		ModelAnim_Bone *pBone = BoneList[b];
		INT boneIndex = b;
		while (pBone)
		{
			transformation = XMMatrixMultiply(transformation, XMLoadFloat4x4(&animdata->TransformationMatrix[boneIndex]));
			boneIndex = pBone->ParentIndex;
			pBone = pBone->Parent;
		}
		XMStoreFloat4x4(&animdata->FinalMatrix[b], transformation);
	}
}

(The "animdata" is a datablock for holding bone transformations and final matrices (I do this seperately from my Model class to allow single models to be used for multiple different "entities").

 

The Transform matrix computed like this:

...

	if (ChannelList[channel]->Type == MDL_ANIMCHANNELTYPE_TRANSLATION)
		vectorT = XMVectorLerp(vector0, vector1, i);
	else if (ChannelList[channel]->Type == MDL_ANIMCHANNELTYPE_ROTATION)
		vectorR = XMQuaternionSlerp(vector0, vector1, i);
//		vectorR = XMQuaternionNormalize(XMVectorLerp(vector0, vector1, i));
	else if (ChannelList[channel]->Type == MDL_ANIMCHANNELTYPE_SCALING)
		vectorS = XMVectorLerp(vector0, vector1, i);
}
return XMMatrixMultiply(XMMatrixMultiply(XMMatrixScalingFromVector(vectorS), XMMatrixRotationQuaternion(vectorR)), XMMatrixTranslationFromVector(vectorT));

My animation channel vector (vector0, vector1 is for the two keyframes to interpolate in between) is where I think it all goes wrong.

I'm doing it by resampling the FBX bone node using EvaluateLocalTransform(), and multiple animations (stacks) I handle by calling lScene->SetCurrentAnimationStack(pAnimStack) before evaluating the FbxAnimCurveNode.

INT numStacks = lScene->GetSrcObjectCount<FbxAnimStack>();
INT animationindex = 0;
for (INT stack = 0; stack < numStacks; stack++)
{
	FbxAnimStack* pAnimStack = lScene->GetSrcObject<FbxAnimStack>(stack);
	lScene->SetCurrentAnimationStack(pAnimStack);
	FbxAnimCurveNode *pCurveNode = lBoneNode->LclRotation.GetCurveNode(pAnimStack);
	if (pCurveNode)		// Rotation
	{
		FbxTimeSpan timespan;
		pCurveNode->GetAnimationInterval(timespan);
		FbxTime fbxduration = timespan.GetStop() - timespan.GetStart();
		FLOAT duration = fbxduration.GetMilliSeconds() / 1000.0f;

...

// Sample the curve every 1/60th second
INT nkeys = (INT)(duration * 1000.0f / 60.0f);

...

FbxQuaternion lastlocalQ;
for (INT key = 0; key < nkeys; key++)
{
	FLOAT keytime = ((FLOAT)key / (FLOAT)(nkeys - 1)) * duration;
	FbxTime fbxkeytime;
	fbxkeytime.SetMilliSeconds((FbxLongLong)(keytime * 1000.0f));
// IS THIS WRONG?
	FbxAMatrix localtransform = lBoneNode->EvaluateLocalTransform(fbxkeytime);
	FbxQuaternion localQ = localtransform.GetQ();
	if (key > 0)
	{
		if (localQ.DotProduct(lastlocalQ) < 0)
			localQ *= -1.0;
	}
	lastlocalQ = localQ;
	pKeyList[key].Time = keytime;
	pKeyList[key].Vector.x = (FLOAT)localQ.GetAt(0);
	pKeyList[key].Vector.y = (FLOAT)localQ.GetAt(1);
	pKeyList[key].Vector.z = (FLOAT)localQ.GetAt(2);
	pKeyList[key].Vector.w = (FLOAT)localQ.GetAt(3);
}

The end result seems to look allright, except that my model (a simple snowman waving his arm) exaggerates the rotation so the arm bends all the way into his head. When I test my model in FBXViewer it's displayed properly, but the author's code is spaghetti code mixed with a grenade (totally unreadable for me, can't find where he does what as he's got many weirdly named functions etc.) so I cannot figure out where it goes wrong in my code. Plus FbxViewer and most other examples uses the FBXSDK functions and homemade quaternion functions etc. for alot of things, and I'm doing things manually using XMMath.

 

Snowman model if it's needed: https://dl.dropboxusercontent.com/u/9361906/snowman.7z

 

Here's how it looks (disregard half of the snowmen, they play a "bow" animation which seems to correspond to what I see in Blender when I'm modelling), you can see the arm intersecting the head:

vp95ll.jpg

 

 

Share this post


Link to post
Share on other sites

Ok I think I got it working as far as the rotation is concerned, but now the root bone offset translation is the problem...

 

How it looks in Blender (notice the offset stippled line going from the (0, 0, 0) origin to bone root):

vno96p.jpg

 

And my result:

29mx34k.jpg

 

 

How can I move my root bone to reflect that?

 

Edit: Here's the matrix I use for the whole model as worldmatrix input to my vertex shader:

FbxNode *lNode = lMesh->GetNode();
FbxAMatrix globalMatrix = lNode->EvaluateGlobalTransform();
Edited by vinterberg

Share this post


Link to post
Share on other sites

This topic is 678 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this