# Use assimp for skeletal animation HELP!

## Recommended Posts

Guys, I've spent a lot of time to load skeletal animation

but  It is very difficult...

but I didn't get the results I wanted

This is my codes

void LoadAnimation::BoneTransform(float time, vector<XMFLOAT4X4>& transforms)
{
XMMATRIX Identity = XMMatrixIdentity();

float TicksPerSecond = (float)(m_pScene->mAnimations[0]->mTicksPerSecond != 0 ?
m_pScene->mAnimations[0]->mTicksPerSecond : 25.0f);
float TimeInTicks = time*TicksPerSecond;
float AnimationTime = fmod(TimeInTicks, (float)m_pScene->mAnimations[0]->mDuration);

transforms.resize(m_NumBones);

for (int i = 0; i < m_NumBones; ++i) {
XMStoreFloat4x4(&transforms[i], m_Bones[i].second.FinalTransformation);
}
}
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);

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

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

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

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

NodeTransformation = TranslationM * RotationM * ScalingM;
}

XMMATRIX GlobalTransformation = ParentTransform * NodeTransformation;

int tmp = 0;
for (auto& p : m_Bones) {
if (p.first == NodeName) {
p.second.FinalTransformation = XMMatrixTranspose(
m_GlobalInverse *  GlobalTransformation * p.second.BoneOffset);
break;
}
tmp += 1;
}

for (UINT i = 0; i < pNode->mNumChildren; ++i) {
}
}

CalcInterp~ function and Find~ function are like a tutorial
(http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html)

I think that I'm doing the multiplication wrong

but I don't know where it went wrong

If you want, i wall post other codes.

here is my result

(hands are stretched, legs are strange)

and it is ideal result

##### Share on other sites

Implementing skeletal animation can be a hard task the first time around. For easier debugging I suggest you do a very simple animation for a very simple model. Create a test scene with a cylinder animate by two bones or something like this. Also drawing the bone matrices as lines is very helpful.

##### Share on other sites

Agree with turanszkij, start with really simple cases. In addition, it can also be really helpful to 'go by the numbers', create the simple joint, maybe just with a few verts to be skinned, then output all the translates, quaternions for each bone and the transformed vert positions.

If you can also export the baked animation from blender you can know the 'ground truth' positions that the vertices should be in, if everything is going perfect. Watch for the order of your transforms, and also differences between 'polarities' of things that blender is spitting out compared to your code. Sometimes flipping an axis or similar can fix things. I spent ages debugging where blender had output quaternions as wxyz and I was expecting xyzw or vice versa lol!

##### Share on other sites

I was able to get assimp animation to work in DirectX. I see you followed an opengl tutorial but in DX matrix multiplication order is reverse of opengl order. In DX if you want to first rotate a vertex by XMMATRIX R1 and then translate by XMMATRIX T1 you do

XMVector3Transform(v, R1 * T1)

p.second.FinalTransformation = p.second.BoneOffset * GlobalTransformation * m_GlobalInverse;

Also make sure you read correctly from aiMatrix4x4 row-major data structure into XMFLOAT4X4 also row-major before loading into XMMATRIX.

##### Share on other sites
On 2018. 1. 28. at 11:02 AM, Ivan Terziev said:

I was able to get assimp animation to work in DirectX. I see you followed an opengl tutorial but in DX matrix multiplication order is reverse of opengl order. In DX if you want to first rotate a vertex by XMMATRIX R1 and then translate by XMMATRIX T1 you do


XMVector3Transform(v, R1 * T1)


p.second.FinalTransformation = p.second.BoneOffset * GlobalTransformation * m_GlobalInverse;

Also make sure you read correctly from aiMatrix4x4 row-major data structure into XMFLOAT4X4 also row-major before loading into XMMATRIX.

I have some questions for you.

you told me to count 'BoneOffset * GlobalTransformation * m_blobalInverse'

but if I calculate that equation, the model is completyely broken

And I use this function to load XMMATRIX from aiMatirx4x4

XMMATRIX(&pNode->mTransformation.a1);

'pNode->mTransformation' is type aiMatrix4x4

I think it is no ploblem

because XMMATRIX can put the argument values of the array

please, I want you to tell me what method you used

thank you

##### Share on other sites
On 2018. 1. 26. at 12:20 AM, turanszkij said:

Implementing skeletal animation can be a hard task the first time around. For easier debugging I suggest you do a very simple animation for a very simple model. Create a test scene with a cylinder animate by two bones or something like this. Also drawing the bone matrices as lines is very helpful.

I'll take your idea and try to test it wiht a simple model

##### Share on other sites
On 2018. 1. 26. at 1:36 AM, lawnjelly said:

Agree with turanszkij, start with really simple cases. In addition, it can also be really helpful to 'go by the numbers', create the simple joint, maybe just with a few verts to be skinned, then output all the translates, quaternions for each bone and the transformed vert positions.

If you can also export the baked animation from blender you can know the 'ground truth' positions that the vertices should be in, if everything is going perfect. Watch for the order of your transforms, and also differences between 'polarities' of things that blender is spitting out compared to your code. Sometimes flipping an axis or similar can fix things. I spent ages debugging where blender had output quaternions as wxyz and I was expecting xyzw or vice versa lol!

I don't know when quaternion is xyzw or wxyz

Many articles just tell them to be careful

It is very difficult...

##### Share on other sites

Generally there are three types of transformations scaling, rotation and translation and you should be consistent with the order of multiplication. Usually it is scaling, rotation and translation, which is expressed in DX math as multiplication order S * R * T. So your NodeTransformation should be

NodeTransformation = ScalingM * RotationM * TranslationM;

and you should not be transposing your matrices when your read them from assimp (depending on your hlsl setting you should transpose your world, view and projection matrices). Then calculating final transformation remains

p.second.FinalTransformation = p.second.BoneOffset * GlobalTransformation * m_GlobalInverse;

The above equations says the final transformation of a vertex to world space is: transform the vertex to bone space, then transform the vertex to scene space (assuming assimp bone transformations are from bone space to scene space) and finally transform it to world space.

It will be simpler if you test with assimp sceneTransform = identity and without scaling and then add them after the bone hierarchy looks good.

I hope it works!

##### Share on other sites
18 hours ago, Ivan Terziev said:

Generally there are three types of transformations scaling, rotation and translation and you should be consistent with the order of multiplication. Usually it is scaling, rotation and translation, which is expressed in DX math as multiplication order S * R * T. So your NodeTransformation should be


NodeTransformation = ScalingM * RotationM * TranslationM;

and you should not be transposing your matrices when your read them from assimp (depending on your hlsl setting you should transpose your world, view and projection matrices). Then calculating final transformation remains


p.second.FinalTransformation = p.second.BoneOffset * GlobalTransformation * m_GlobalInverse;

The above equations says the final transformation of a vertex to world space is: transform the vertex to bone space, then transform the vertex to scene space (assuming assimp bone transformations are from bone space to scene space) and finally transform it to world space.

It will be simpler if you test with assimp sceneTransform = identity and without scaling and then add them after the bone hierarchy looks good.

I hope it works!

I have some questions

First you say that I shouldn't be transposing matrices when read them from assimp,

but I think that assimp is opengl format

so would i have to do transpose to get the correct col/row ?

Actually, I tried removing transpos function when using assimp to read my model,

but the result is very bad (It was completely disassembled)

second I agree that you told me to do S * R * T

because I saw it in my book when i was studying

but strangely the result of S * R * T and T * R * S were same, so i didn't fix it

hmmm..... assimp is very useful library to load fbx format

but load anmation, it's very hardly...

I don't have enough English skills to read it

so you would be inconvenient to read it

##### Share on other sites

Yeah I understand, the problem with linear transformations is that you have to get both the equations and the data right and it is really hard to debug using the standard debugger and on top of this there are the opengl and directx differences. So in my case I only transpose when I read from aiMatrix4x4. I don't transpose when I read quaternions or vectors.

const aiMatrix4x4& offset = bone.mOffsetMatrix;
XMMATRIX meshToBoneTransform = XMMatrixTranspose(
XMMATRIX(offset.a1, offset.a2, offset.a3, offset.a4,
offset.b1, offset.b2, offset.b3, offset.b4,
offset.c1, offset.c2, offset.c3, offset.c4,
offset.d1, offset.d2, offset.d3, offset.d4));

Do the same for (transpose) XMMATRIX(&pNode->mTransformation.a1);

If you are sending FinalTransformation matrices to hlsl for skinning on the GPU you may have to transpose them depending on how you calculate your final vertex world position.

## Create an account

Register a new account

1. 1
Rutin
22
2. 2
3. 3
4. 4
5. 5

• 9
• 9
• 9
• 14
• 12
• ### Forum Statistics

• Total Topics
633307
• Total Posts
3011289
• ### Who's Online (See full list)

There are no registered users currently online

×