Skeletal Animation

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:

  1. The difference in matrix layouts and in memory of Assimp’s matrices and the matrices in my library
  2. 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:

  1. generating the blend weights and indices
  2. 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

Advertisements

2 responses to “Skeletal Animation

  1. 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s