Open Asset Import Library animation loader

I noticed that on the ASSIMP forums there were many requests for an animation loader– even more specially, one that pre calculated the animation transforms. There are huge benefits to pre calculating anything, especially animation data like this. I had a test mesh with 56 bones (yeah, its alot i know, but the models animations are great) and a single texture for the entire model. So the whole model took a single draw call.

All the numbers below were taken from using NVIDIA PerfHUD, which is a great profiling tool!

To Traverse the model and recalculate the transforms each frame took about .083 ms CPU time (this is includes the total model setup time as well), GPU draw time was .083ms. You see the problem?
Afterwards, my CPU time went to .013ms, and of course my gpu time was the same. This might not seem like big savings, but if you have 20-30 animated characters on the screen the total cpu time is 25* .083 = 2.075 ms, and 25* .013 = .325ms. As you can see, this is a huge savings if I plan on having multiple animations running in the scene.

So, to use it, you must be using the ASSIMP library of course! The code also has a save to disks and load from disk that works as well. So, if you want to save the animation, use

void Save(std::ofstream& file);
void Load(std::ifstream& file);

The functions expect the file stream to be opened somewhere else in the program, then passed a reference to the stream. In this way, animations can be saved where ever you like within a custom model file, or to a separate file.

Before any Animations can be played,

void Init(const aiScene* pScene);

has to be called, which will extract the skeleton and all animations from the pScene object.

After that, to get the matrix transforms, simply call

std::vector& GetTransforms(float dt)

Now, this function will wrap your dt, so dont worry about going out of bounds. If the time is 5656565, it will wrap it correctly. So just keep a running total of the dt and pass it to the function. The return will be the array with the transforms, which you then pass to the shader.

Note: I did not comment the code completely, and there is a small reminent of my dual quaternion implementation that will work if you uncomment it and use it. You would need to check out the NVIDIA SDK10, Quaternion Skinning example for the shader code and their implementation. It is pretty straight forward. All you would need to do is change the shader. Also, I use my mat4 class, but it is complely compatible with the D3DXMATRIX class, so if you replace mat4 with D3DXMATRIX and their equivalent calls, there will be no problems. One last thing, this builds transforms for a LEFT HANDED coordinate system. ASSIMP’s matrix4x4 is a right handed matrix, which is why I do a couple of transposes in the code when converting to my mat4.

The code is free for use for anyone for any purpose.

Leave a comment ?

65 Comments.

  1. You’re right, that function should be removed. It was left in by accident.

  2. I am trying to use your animation class but it does not work for me, although there is no problem with a static mesh, tiny model. Later, I use your LoadAssimp() function for loading a static mesh, but it does not work either. I uploaded the image here: http://img542.imageshack.us/img542/3506/0k6r.jpg

  3. That looks like a problem with your bones/indices in your shader
    Verify that all the bones have a valid index
    Verify that all of your weights add up to 1 Per vertex
    You can test that your bones/indicies are good in your shader by passing identity matrix’ to your shader and use that.
    If your model appears correct using the identity matrix, then try using the animation matrix from the animator class.

  4. Psudo code just an example below
    To test your weights and bone index, fill bone_matrices with Identity matrix.
    If your bones and weights are correct, the model will stand still.

    struct PSIn {
    float4 position : SV_Position;
    float2 texcoord : TexCoord0;
    };
    cbuffer CBuffer0{
    float4x4 ObjectToWorld, ViewProjection;
    };

    cbuffer CBuffer2{
    float4x4 bone_matrices[50];//matrix passed from c++ code
    };

    PSIn VS(float3 vPos : POSITION, float2 vTexCoord0 : TEXCOORD0, float3 vNormal : NORMAL, float3 vTangent : TANGENT, float4 vBones : BONE, float4 vWeights : BLENDWEIGHT){
    PSPTNTIn output;
    float3 vNormalWorldSpace;

    float4x4 finalMatrix;
    finalMatrix = vWeights.x * bone_matrices[vBones.x];
    finalMatrix += vWeights.y * bone_matrices[vBones.y];
    finalMatrix += vWeights.z * bone_matrices[vBones.z];
    finalMatrix += vWeights.w * bone_matrices[vBones.w];

    float4 vAnimatedPos = mul(float4(vPos.xyz,1),finalMatrix);
    float4 vAnimatedNormal = mul(float4(vNormal.xyz,0),finalMatrix);
    float4 vAnimatedTangent = mul(float4(vTangent.xyz,0),finalMatrix);

    // Transform the position from object space to homogeneous projection space
    output.position = mul(mul(vAnimatedPos,ObjectToWorld), ViewProjection);

    output.texcoord.xy = vTexCoord0;
    return output;
    }

  5. Thank Scott, after testing as you said that fill the bone matrices with the identity matrices, the result is http://img407.imageshack.us/img407/8826/h63s.jpg

  6. Result of rendering the static mesh is http://imageshack.us/a/img10/3883/z44a.jpg

  7. I modified the source code as creating the input layout, then I filled the bone matrices with identity matrices. As you said, the model is stand still.

    But when I try to use animation matrix, the model is distorted.http://img6.imageshack.us/img6/2593/k4wo.jpg

  8. the layout format is
    { “POSITION”, 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { “NORMAL”, 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { “TEXCOORD”, 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { “WEIGHTS”, 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { “BONEINDICES”, 0, DXGI_FORMAT_R8G8B8A8_UINT, 0, 48, D3D11_INPUT_PER_VERTEX_DATA, 0 },

    In shader

    float3 PositionL : POSITION; // vertex position
    float3 Normal : NORMAL;
    float2 TextureUV : TEXCOORD0;// vertex texture coords
    float4 Weights : WEIGHTS;
    uint4 BoneIndices : BONEINDICES;

  9. This is the animated model when I use your animation class:
    http://img513.imageshack.us/img513/5554/quh8.jpg

  10. Thank you so much, Scott. I got it. I just need to change the input layout format for “BoneIndices”, and it is working.

  11. Glad to hear! I know, I found that the input layout is sometimes difficult to figure out. There many times when I couldnt figure out why something was not working correct, and it was because the input layout needed to be tweaked.

  12. Note to readers: the header’s filenames have been changed since this article was written.

    assimp.h becomes cimport.h
    aiScene.h becomes scene.h
    aiPostProcess.h becomes postprocess.h

  13. Thanks, I will update the files eventually. . .

  14. Your code does not work with dae animation format. I have a model in dae format. Its animation is fine with Assimp viewer program, but it seems have no bones information as debugging .Please check it.

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackbacks and Pingbacks: