Advertisement

Use assimp for skeletal animation HELP!

Started by January 23, 2018 10:31 AM
16 comments, last by emil0 4 years, 10 months ago

I just noticed you should also reverse the multilcation of GlobalTransformation like


XMMATRIX GlobalTransformation = NodeTransformation * ParentTransform;

 

I accept the idea of testin with a simple model

so got simple models of two bone

 

first It is ideal result

image.png.c14b9ca27eb80f86301c2695539926a5.png

It has a blue circle with its aixs of rotation

(com up and down around the axis)

but my result ( fix transpos ploblem and GlobalTransformation and etc...) is like this

image.thumb.png.08dba4511fb8bc2c9782f9c2fdb72999.png

the axis is center of the cube

it also come up and down but different axis give completely different results

 

I attach my modified codes

 

1. load bone data


void LoadModel::InitBones(UINT index, const aiMesh* pMesh)
{
	for (UINT i = 0; i < pMesh->mNumBones; ++i) {
		int BoneIndex = -1;
		string BoneName(pMesh->mBones[i]->mName.data);

		int tmpIndex = 0;
		for (const auto& p : m_Bones) { 
			if (p.first == BoneName) {
				BoneIndex = tmpIndex;
				break;
			}
			tmpIndex++;
		}

		if (BoneIndex < 0) { 
			BoneIndex = (int)m_Bones.size();

			Bone bone;
			bone.BoneOffset = aiMatrixToXMMatrix(pMesh->mBones[BoneIndex]->mOffsetMatrix);
			m_Bones.emplace_back(make_pair(BoneName, bone));
		}

		const aiBone* pBone = pMesh->mBones[BoneIndex];
		for (UINT b = 0; b < pBone->mNumWeights; ++b) {
			UINT vertexID = pBone->mWeights[b].mVertexId;
			float weight = pBone->mWeights[b].mWeight;
			m_meshes[index].m_vertices[vertexID].AddBoneData(BoneIndex, weight);
		}
	}
}

 

2.  ReadNodeHeirarchy


void LoadAnimation::ReadNodeHeirarchy(float AnimationTime, const aiNode * pNode, const XMMATRIX& ParentTransform)
{
	string NodeName(pNode->mName.data);

	const aiAnimation* pAnim = m_pScene->mAnimations[0];

	XMMATRIX NodeTransformation = aiMatrixToXMMatrix(pNode->mTransformation);

	const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnim, NodeName);

	if (pNodeAnim) {
		aiVector3D s;
		CalcInterpolatedScaling(s, AnimationTime, pNodeAnim);
		XMMATRIX ScalingM = XMMatrixScaling(s.x, s.y, s.z);


		aiQuaternion q;
		CalcInterpolatedRotation(q, AnimationTime, pNodeAnim);
		XMMATRIX RotationM = XMMatrixRotationQuaternion(XMVectorSet(q.x, q.y, q.z, q.w));


		aiVector3D t;
		CalcInterpolatedPosition(t, AnimationTime, pNodeAnim);
		XMMATRIX TranslationM = XMMatrixTranslation(t.x, t.y, t.z);



		NodeTransformation = ScalingM * RotationM  * TranslationM;

	}

	XMMATRIX GlobalTransformation = NodeTransformation * ParentTransform;

	for (auto& p : m_Bones) {
		if (p.first == NodeName) {
			p.second.FinalTransformation = 
				m_GlobalInverse *  GlobalTransformation * p.second.BoneOffset;
			break;
		}
	}

	for (UINT i = 0; i < pNode->mNumChildren; ++i) {
		ReadNodeHeirarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);
	}
}

aiMatrixToXMMatrix function is like your(Ivan Terziev) function

 

i want you to take a look at it

very thanks!

Advertisement

Make it even simpler I think, why is the root bone (which is the root?) moving at all? Put the root bone start from the origin pointing along one axis, and only move the second bone, perhaps at 0 degrees (straight) (should be just a translate, no rotate component), 45 and 90. It kind of looks like the order of translate / rotate might be wrong, but it will be easier to see with more simplified. (I'm not even looking at the code, there's too many permutations for my tiny brain lol). :)

If it helps this is what I do:


// local bone combined matrix
matBoneLocal = matBoneLocalTrans * matBoneLocalRot;

// global bone matrix is combined with parent matrix
matBone = matParent * matBoneLocal;

// the skinning matrix also combines back transform to get vertices into 'bone space'
matSkin = matBone * matRestInverse;

That last step probably isn't necessary if you store your skin verts in 'bonespace', i.e. pretransform them back to be rooted from the origin and pointing along the default axis. You can find out whether you need this by just drawing the skin with identity matrix and seeing what it looks like, whether it looks like the character in rest pose, or all the bones on top of each other.

On 2018. 2. 1. at 6:00 PM, lawnjelly said:

Make it even simpler I think, why is the root bone (which is the root?) moving at all? Put the root bone start from the origin pointing along one axis, and only move the second bone, perhaps at 0 degrees (straight) (should be just a translate, no rotate component), 45 and 90. It kind of looks like the order of translate / rotate might be wrong, but it will be easier to see with more simplified. (I'm not even looking at the code, there's too many permutations for my tiny brain lol). :)

You inspired me! thanks

I ignored the animation imported from assimp and just tried rotation the bones

and As a result, I found the axis of rotaion to be strange.

 

As I predicted above, the axis was applied differently from the original (the center of the second mesh)

So many of the advice and functions we've got from above seem to have been inadequately applied

 

And i have one question 

Maybe i misunderstood, Are you telling me not to apply all animations from root node?

 

I think that read all child nodes from the root node and multiply the transformations( rot, scaling, shifting)

and if current node is bone, apply the multiplied transformation value up to now

 

It is wrong???

Hey Guys

I've completely fixed everything!

 

It's matter about transpos matrix

 

Previously, I applied transpos function to the bone offset and mTranslation of the node
(that is, i applied transpos when I read aiMatrix4x4)

 

but after trying many different things, I found it to be wrong

 

Exactly, there is an aiMatrix4x4 which should apply transpos and aiMatrix4x4 should not apply

 

In my case, I applied transpos only result of transformation

This is my codes

 


void LoadAnimation::ReadNodeHeirarchy(float AnimationTime, const aiNode * pNode, const XMMATRIX& ParentTransform)
{
	string NodeName(pNode->mName.data);
	const aiAnimation* pAnim = m_pScene->mAnimations[0];

	XMMATRIX NodeTransformation = XMMATRIX(&pNode->mTransformation.a1);
	//I just read aiMatrix4x4 (aiMatrix to XMMATRIX format)
	const aiNodeAnim* pNodeAnim = FindNodeAnim(pAnim, NodeName);

	XMMATRIX anim = XMMatrixIdentity();
	if (pNodeAnim) {
		aiVector3D s;
		CalcInterpolatedScaling(s, AnimationTime, pNodeAnim);
		XMMATRIX ScalingM = XMMatrixScaling(s.x, s.y, s.z);


		aiQuaternion q;
		CalcInterpolatedRotation(q, AnimationTime, pNodeAnim);
		XMMATRIX RotationM = XMMatrixRotationQuaternion(XMVectorSet(q.x, q.y, q.z, q.w));


		aiVector3D t;
		CalcInterpolatedPosition(t, AnimationTime, pNodeAnim);
		XMMATRIX TranslationM = XMMatrixTranslation(t.x, t.y, t.z);



		NodeTransformation = ScalingM * RotationM * TranslationM;
		NodeTransformation = XMMatrixTranspose(NodeTransformation);
     	 	//I applied transpos
	}


	XMMATRIX GlobalTransformation = ParentTransform  *  NodeTransformation;

	for (auto& p : m_Bones) {
		if (p.first == NodeName) {
			p.second.FinalTransformation = 
				m_GlobalInverse *  GlobalTransformation * p.second.BoneOffset;
			break;
		}
	}

	for (UINT i = 0; i < pNode->mNumChildren; ++i) {
		
		ReadNodeHeirarchy(AnimationTime, pNode->mChildren[i], GlobalTransformation);
	}
}

 

If I read bone offset and mTransformation about node(aiMatrix4x4),

I simply transformed it into XMMATRIX format
// XMMATRIX(&aiMatrix4x4.a1)

 

second, If current node is a node belonging to the animation channel, and S R T transformation is performed,
transpos is applied to the transformation

 

The important this is that we don't apply the transpos to the parent nodes matrix
(if parent node conduct transpos function, there is a risk that it will be performed twice)

 

This is the result

image.thumb.png.461a1f2e9e01c9d835311d8d97d222d7.png

 

PS. I hope this post helps people using directX and assimp

assimp library has some bugs that some models using fbx format are broken
I know that the assimp team  is fixing it

I was able to observe that, in general, if the viewer provided by assimp show broken results,
my result are also broken.

Advertisement

How did you solve the time?

Like how do you get the correct time in seconds in Direct X?

How do you translate this?::float RunningTime = (float)((double)GetCurrentTimeMillis() - (double)m_startTime) / 1000.0f;

This topic is closed to new replies.

Advertisement