After nearly two weeks of hitting my head against the wall I finally got it working!!

Words cant express how happy I am. The reason I couldn’t working was because of a two issues:

- The difference in matrix layouts and in memory of Assimp’s matrices and the matrices in my library
- The order in which the matrix multiplication occurs

This blog post should give you a better understanding of the issues I had. After all of that struggling I cant help but to feel my linear algebra skills leveling up; I now have a much better understanding of matrix-matrix multiplication, matrix-vector multiplication and the layout of matrices in memory. Terms like *row-major* and *column-major* don’t scare me any more and I have no issue using either since they’re one transpose away from each other. I should probably explain process of doing skeletal animation, but I’ll do it in a tutorial when I have the time. Aside from the issues stated above, here are a few other noteworthy things about the process:

- generating the blend weights and indices
- floating point indices

**Blend Weights and Blend Indices**

I found it odd at first that the blend weights and indices were kept in the bones and not in the mesh. After digging though open3mod i realize it was done that way to allow for easy CPU skinning. I want to the skinning on the GPU so Assimp storing the blend indices and weights inside the bones meant that I had to extract and lay them out in an array that’s in line with the other attributes in the other buffers. Here’s how I did it:

**step1 1: Create and initialize arrays to 0**

//4 weights and indices per vertex #define BONE_WEIGHTS_SIZE 4 //create arrays and initialize them to 0 int boneIdArraySize = mesh->mNumVertices*BONE_WEIGHTS_SIZE;//same size as bone arrays float* boneIds = new float[boneIdArraySize];//has to be float, GLES 2.0 doesnt support ivec4 😦 float* boneWeights = new float[boneIdArraySize]; for(int i=0;i<boneIdArraySize;i++) { boneIds[i]=0; boneWeights[i]=0; }

A blend weight of 0 means the bone’s transformation at the associating blend index does not affect the final deformation transformation.

**step2: Fill in the weights and indices data from the aiVertexWeights stored in the bones**

for(int i=0;i<mesh->mNumBones;i++) { aiBone* aiBone = mesh->mBones[i]; //find associating Bone Bone* bone = skeleton->FindBone(aiBone->mName.data); //get the id unsigned int boneId = bone->id; //find each vertex for the weight and assign it for(int j=0;j<aiBone->mNumWeights;j++) { aiVertexWeight w = aiBone->mWeights[j]; //the following gets us at the start index of the vertex's weights unsigned int vertexStart = w.mVertexId *BONE_WEIGHTS_SIZE; //each vertex has a maximum of 4 weights, so we find the next empty one and use it //we know a slot is empty when the weight is 0 // "k < BONE_WEIGHTS_SIZE" prevents writing weights in another vertex's slot for(int k=0;k<BONE_WEIGHTS_SIZE;k++) { if(boneWeights[vertexStart+k]==0) { //empty slot! boneWeights[vertexStart+k] = w.mWeight; boneIds[vertexStart+k] = boneId; //IMPORTANT! this is to ensure we dont write on any other slots break; } } } }

**An Important Note:**

The *id* of the bone is it’s index in the array it’s being stored in. This is important because the a blend index of 0 will search for the bone matrix at index 0 in bone matrices array in the shader. Because of the way I was generating the skeletons hierarchy, the bones in my skeleton system may be in a different order from that of the Assimp’s which is why both were using in generating the weights.

**Floating Point Blend Indices**

The *ivec4* type which isn’t supported in OpenGL ES ( WebGL) so I had to use *vec4*. While it is rumored that on some cards indexing an array with a float is ok, mine had issues with it which is understandable. I simply had to do this convert each component of the vec4 to an int before using it as an index: **int**(a_boneIds.x).

That’s pretty much it for this post. Sorry I have no demo web demo. Before I can do so I’d have to add skeletal animation to my mesh file format since I don’t want to compile assimp’s bloat of a library with emscripten. So i’ll upload a demo in time. I’ll also make a tutorial about doing skeletal animation purely with assimp’s library and SDL (or some other windowing library); I’m currently using another math library and my prototyping tools. The internet is seriously lacking a good assimp skeletal animation tutorial.

Since I dont have a demo, the least i can do is show my skinning shader.

uniform mat4 world; uniform mat4 view; uniform mat4 proj; attribute vec3 a_pos; attribute vec3 a_normal; attribute vec4 a_boneIds; attribute vec4 a_boneWeights; #define MAX_BONES 60 uniform mat4 bones[MAX_BONES]; varying float light; void main() { //creation of the deformation matrix mat4 skinMatrix = bones[int(a_boneIds.x)]*a_boneWeights.x; skinMatrix += bones[int(a_boneIds.y)]*a_boneWeights.y; skinMatrix += bones[int(a_boneIds.z)]*a_boneWeights.z; skinMatrix += bones[int(a_boneIds.w)]*a_boneWeights.w; //nb: my matrices are column major gl_Position = proj*view*world*skinMatrix*vec4(a_pos,1); vec3 normal = (world*skinMatrix*vec4(a_normal,0)).xyz; //quick and dirty per-vertex diffuse lighting light = 0.5; light += dot(normalize(vec3(0.0,1.0,1.0)),normal)*0.3; light += dot(normalize(vec3(1.0,1.0,0.0)),normal)*0.1; }

The model used in the post’s thumbnail was downloaded from blendswap and was designed by 61stylo.

References

http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html

http://ephenationopengl.blogspot.com/2012/06/doing-animations-in-opengl.html

https://github.com/ccxvii/asstools/blob/master/assview.c

https://code.google.com/p/grandpa-animation/

http://www.catalinzima.com/2012/12/a-word-on-matrices/

http://fgiesen.wordpress.com/2012/02/12/row-major-vs-column-major-row-vectors-vs-column-vectors/

https://github.com/acgessler/open3mod/tree/master/open3mod

Great blog Nick. I’ve been similarly bashing my head against the perils of skeletal animation. I’m fairly sure I’ve got my weights and bone transforms correct – but something in my logic isn’t right.. using identity transforms produces the correct bind pose, anything else is a mess.

Don’t suppose you’ll get around to posting your skeletal update? As you mentioned… “The internet is seriously lacking a good assimp skeletal animation tutorial”.. true words indeed.

Thanks for the reminder. I havent been blogging at all lately, but I’ll try to find some time in the near future to write a decent tutorial on the topic.