I have loaded in a model to my C++ OpenGL game. It is a COLLADA file type that I have loaded, and I setup an animation under blender for the file. The problem is I don't know how to animate the model. The Assimp documentation didn't really help me out. Their source code didn't use animations, and I can't seem to find anywhere online that someone explains how to animate your loaded model... I'm sorta wondering if someone could link me to a helpful website, or maybe just help me out, so that maybe I will understand how to do animations with assimp.

  • 1
    \$\begingroup\$ Could you explain a bit better whether you want help extracting the animation from the Assimp graph or actually implementing animation. Assimp animations are stored as transforms for nodes (which can be anything - including bones) in the Animations array (scene/model level). They are straightforward to extract, but its not 100% clear if that is actually what you are asking :) \$\endgroup\$
    – sebf
    Commented Mar 29, 2012 at 14:47
  • \$\begingroup\$ I need help extracting the animation from the file, and I also need to know how to run the animation in the game. \$\endgroup\$ Commented Mar 29, 2012 at 17:03

2 Answers 2


Disclaimer: I use AssimpNet which is a managed wrapper around Assimp, though the object graph used by Assimp remains unchanged so the process is equivalent no matter the platform.
I personally think Assimp is a wonderful idea, and has saved me countless hours, but translating their graph to your engine is writing an importer and learning everything required to do so.

I am assume you are referring to Skeletal animation, rather than blendshapes or just individual nodes (though the latter I'll mention anyway).

As you'll know, Assimp collects entities in your source file in a tree of nodes. These nodes can have things like meshes attached to them, to identify visual geometry, or they can be 'abstract' nodes which exist to identify the world transforms of things like lights, and in your case, bones. Many file formats will 'flatten' the hierarchy so you will have a model with a single level of children, each one with a mesh. COLLADA is not one of those, and therefore its a little more complicated because if you want your imported model to behave correctly, you need to preserve its structure.

enter image description here

Take the above diagram. The dashed boxes are Node objects, some have meshes but all exist within the graph eminating from the Root Node which you find in the scene object.

Importing the Animations

Importing the animation data is the simplest part of the process. The aiScene object will have a member aiAnimations which is a list of aiAnimation objects. Each aiAnimation will have a list of aiNodeAnim* objects, in the mChannels member. Essentially, each channel controls a single node, and contains a list of keyframes, each with a scale, a rotation, and a translation value for this node at a specific point in time. Animation tracks identify nodes to control via their name.

(It is worth nothing at this point, that some exporters (such as 3DSMax) combine animations in odd ways, resulting in a single animation containing tracks for unrelated nodes, so if you are not animating a skinned mesh, make sure you check all the animation objects!)

So, say you are animating "Box". You have already imported the node 'Box' with its geometry and you have an item in your world with a transform. To animate the box, get the animation from aiAnimations and iterate over each aiNodeAnim (channel) until you find one with the mNodeName matching "Box". Then, iterate over each of the Position, Scale, and Rotation keyframes in the mPositionKeys, mScalingKeys, and mRotationKeys members, transforming the value (Vectors or Quaternions, and the time offset) into whatever structure your application stores animation data in.

Then just pick a keyframe/interpolate between them as usual and apply the transform specified by the frame to that nodes local transform and see it move!

Extracting skeletal animation is an identical process, since in Assimp bones are just other nodes. The skinning data is slightly trickier. To animate a skinned model you need three things:

  1. The transforms for each bone from the animation (which we get above)

  2. The hierarchy of the skeleton

  3. The skinning transforms for each bone (a.k.a inverse bind pose transforms) which we get from the mesh objects.

The first thing to do, though, is find the skeleton.

Finding the Skeleton

The example diagram above is what you will find in every COLLADA DOM I have seen so far. That is, the skeleton will exist as a separate tree of nodes, sibling to the 'visual' geometry just under the root node.
Assimp makes no distinction between 'bone nodes' and 'nodes', which means your importer will have to engage in some guesswork to pick the right starting node before reading in the whole tree.

The reason we need to find the skeleton is because, as can be seem from the diagram, each bone is parented to each other bone; the transforms extracted from the animation tracks/channels need to be combined with the transforms from other tracks to move the vertices correctly.

The way I do it is this:

Go over all the meshes and identify all the bones that they use, then for each bone, I look for a node in the object graph under root with the same name. I check how far down in the tree from root this bone is. The bone with the lowest value (degree of separation) is the root of the skeleton.

Once you have this, it is a case of reading in this node and each of its children, storing the transform of the node, and its parent node's name. Using this information, you can look up the parent for each bone.

Skinning Data

This is the trickiest bit, extracting the bones and weights from each mesh. The member you are looking for this time in mBones and it will be a member of aiMesh. It contains two things of interest:

  1. mOffsetMatrix - this is the skinning transform, or Inverse Bind Pose transform - you want to store this for each bone along with the bones original transform and the name of its parent.

  2. mWeights - these are the vertex weights - and they are, well, sort of backwards!

Each aiVertexWeight object contains mVertexId (which identifies the vertex) and mWeight, which is the weighting of the bone. The mVertexId is the index of the vertex in the 'vertex array' that serves the mesh. What that means is that you have to define a 'master list' of bone names, so that you can assign a number to each bone and have it refer consistently to the same bone. Then you need to look up that number for the name of the aiBone object which contains the vertex weight. Now you can update the vertex in your mesh with the bone ids and weights.

(You see what I mean about backwards, the weight contains the vertex id, while its parent contains the bone - although it makes it easier to write importers)

enter image description here


Now you should have, for non-skinned items, a list of keyframes which define transforms of a node (identified by the name) at specific times. Which is all you need to animate a node (just update its local transform).

If you have a skinned mesh then you should have:

a. A list of bones in a specific order
b. A list of bone ids and their weights, for each vertex in the mesh, ready to be packed into your vertex data array.
c. A list of skinning/inversebindpose transforms, one for each bone in the list in (a)
d. The parent for each bone in (a)
e. The bind pose transform for each bone in (a) (same place you got (d), or just take the inverse of the skinning transform)

This is the "standard dataset" for skeletal animation, and if you know about skeletal animation then it's all you need! If not, that best be a different question since it's quite a large area ;)

I hope that has helped, or at the very least not added to the confusion. Good luck!

(*Animations also contain mMeshAnim which allow for animating the mesh directly, I assume you are not talking about this just yet)

  • 1
    \$\begingroup\$ Well this is a huge answer, and it definitely worth my upvote. \$\endgroup\$
    – Valkea
    Commented Mar 29, 2012 at 23:12
  • \$\begingroup\$ This is an excellent write-up of assimp's animation import. Do you mind if I add a link to the assimp docs? \$\endgroup\$ Commented Jun 21, 2012 at 1:18
  • 1
    \$\begingroup\$ @AlexanderGessler, Thanks! Not at all, go ahead - I'm glad after writing all that it is of use :) \$\endgroup\$
    – sebf
    Commented Jun 21, 2012 at 20:15
  • \$\begingroup\$ you said you get "The bind pose transform for each bone in (a) (same place you got (d), or just take the inverse of the skinning transform)". Isn't the matrix you get in the node the local bind transform (the transform relative to the parent bone in the bind pose) what you actually get? If so, you'd need to multiply the transform with the parent's to-root transform to get the bind pose transform for the bone, wouldn't you? \$\endgroup\$
    – Luca
    Commented Mar 13, 2018 at 22:27
  • 1
    \$\begingroup\$ @Luca, if you were computing it yourself from the hierarchy yes, but IIRC Assimp gives you the pre-multiplied inverse bind pose for each node. (By 'skinning transform' I meant world space transform.) \$\endgroup\$
    – sebf
    Commented Mar 14, 2018 at 8:45

I was waiting for a more experienced answer, but as nobody seems to have anything to help you, I will give you the informations I found.

Obviously there is a lack of documentation about Assimp animations and except if you dig into the Assimp code directly it seems that there is no available informations about this precise point.

But I did found this forum thread where you can see some interesting code portions wrote in order to import and use animations.

The other potentially useful information I found is this blog entry where you can find an Open Asset Import Library animation loader with the code which is free for use for anyone for any purpose (as wrote at the end of the entry).

I hope this could help...

  • \$\begingroup\$ It might be worth noting that the "Open Asset Import Library animation loader" is very slow and precalculates every single animation keyframe (high memory usage) instead of computing it on the fly. By "fixing" it I gained a 15-fold speedup. \$\endgroup\$
    – Tara
    Commented Apr 29, 2015 at 14:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .