FBX SDK and the 3DS Max skin modifier

Started by
3 comments, last by Gumgo 13 years, 9 months ago
I'm writing a converter from FBX to my own format. I've figured out how to export basic mesh data (vertices, normals, tangents, binormals, tex coords, etc.) but I'm not sure of how to access the vertex weights or bone indices in skinned meshes and the documentation seems pretty sparse on that topic.

Would someone who has gotten this working mind explaining how the FBX format manages this?

Previously I have exported objects using MAXScript, but I'd like to move to FBX because it seems more powerful and I can code it in C++, and because it supports tangents/binormals. My format is as follows. There are 3 objects involved in skeletal animation: skeletons, animations, and rigged meshes, which are all separate files:

Skeletons store a list of bones, the order determining the bone's index. For each bone, the index of the parent bone, along with the "inverse bind pose" transformation of that bone is stored.

Animations refer to the skeleton files. In an animation, there is a list of indices, where each index corresponds to a bone in the skeleton file (not all bones must be listed, in cases of animating only part of the skeleton). Each index in the list includes a list of keyframes consisting of a time and the bone's local transformation at that time.

Rigged meshes simply store a list of vertices (and triangles). Each vertex has the basic vertex data (position, normal, etc.) and also has 4 indices and 4 weights. Each index corresponds to a weight. The index represents the bone in the skeleton file influencing that vertex, and the weight represents how much that bone influences the vertex (weights sum to 1).

Basically, what I need to do is find some way to get this index and weight data. However, it looks like I'll have to work in reverse. From what I can tell, FBX uses "deformers" (which I'm guessing correspond to bones?) and each deformer has a list of vertices it influences, along with weights. Is this how it works? If so, the process is simple: associate each deformer (bone) with an index upon exporting the skeleton file, and then when exporting animations and rigged meshes, refer to the skeleton to figure out which deformer corresponds to which index. For rigged meshes, go through all deformers, determine their indices, and add the index/weight to each vertex it influences.

Anyway, hopefully I've explained this clearly... could anyone explain how weight data is accessed from these deformers (assuming I'm getting this right), or if I'm not right about it, how to go about doing this? Thanks.
Advertisement
I just got through writing a converter like this of my own. Your understanding of the how the blend weights are stored is pretty much correct, although the objects that correspond to bones and the set of vertices they influence are what FBX calls "clusters" not deformers. From the files I've tested, you usually have just one deformer which should have a type of KFbxDeformer::eSKIN. Then you can get a number of clusters from the deformer which each correspond to a bone. Each cluster provides you with a list of vertices it influences and the corresponding weights.

Anyway, let me know if you have any other questions and I can tell you what I know. The documentation for the FBX SDK is pretty lacking so I had to figure a lot of stuff out by trial and error.

EDIT: Here's my code that extracts all the info from the clusters. The part that deals with the "transform link" matrices is related to the bind pose, but I'm not actually using the information extracted there (there's another way to get it, if the file stores a KFbxPose with this information).

static void process_clusters(KFbxMesh *mesh){		if (!mesh->GetDeformerCount())		return;	KFbxDeformer *def = mesh->GetDeformer(0);	KFbxDeformer::EDeformerType defType = def->GetDeformerType();	assert(defType == KFbxDeformer::eSKIN);				KFbxSkin *skin = KFbxCast<KFbxSkin>(def);	int clusterCount = skin->GetClusterCount();			for (int i = 0 ; i < clusterCount ; i++)	{		KFbxCluster *cluster = skin->GetCluster(i);		KFbxCluster::ELinkMode lClusterMode = cluster->GetLinkMode();		const char *boneName = cluster->GetLink()->GetName();					KFbxXMatrix kLinkMatrix;		cluster->GetTransformLinkMatrix(kLinkMatrix);		KFbxXMatrix kTM;		cluster->GetTransformMatrix(kTM);		KFbxXMatrix kInvLinkMatrix(kLinkMatrix.Inverse());		KFbxXMatrix kM(kInvLinkMatrix * kTM);				invBindPose[boneName] = kM;		bindPose[boneName] = kM.Inverse();		int indexCount = cluster->GetControlPointIndicesCount();		int *indices = cluster->GetControlPointIndices();		double *weights = cluster->GetControlPointWeights();		int bone = boneMap[boneName];		for (int j = 0 ; j < indexCount ; j++)		{			int vertex = indices[j];			float weight = (float)weights[j];			BoneWeightPair pair;							pair.bone = bone;			pair.weight = weight;			boneWeights[vertex].push_back(pair);		}	}	}
Awesome, thanks, that is super helpful! So far what I've done for skeletons and animations is searched the scene for a node labeled "root" and then used the hierarchy from that point on as the skeleton. Assuming that I can figure out how clusters correspond to nodes (that is, are clusters nodes/do they refer to nodes in some way? If so I could use cluster->GetLink()->GetName(), as you have), finding the correct indices for the bones should be easy.
I started my code from one of the FBX sample programs that just walks the scene and prints out the name of every node it finds. It starts from the node returned by KFbxScene::GetRootNode and just walks recursively, handling every node it encounters in that order.

As you can see in that code, the clusters are stored on the skin deformer, which is in turn stored on the mesh (actually any KFbxGeometry node looks like it can have deformers). At that point in the code, I'm storing a list of bone-index + blend-weight pairs for each vertex. I'm determining the bone index by getting the name from cluster->GetLink()->GetName() and looking it up in a map that was previously populated while traversing the skeleton nodes. The docs for GetLink say:

"The link node is the node which influences the displacement of the control points. Typically, the link node is the bone a skin is attached to."

It just seems to have worked out that by the time I process a mesh, I've already visited all the skeleton nodes (nodes with KFbxNodeAttribute::eSKELETON) which is how I built up the mapping of bone names to bone index that I'm using in that process_clusters function.
That sounds pretty similar to what I'm doing. In my setup, the skeleton's root must be called "root". First I try to find the node by name called "root" in the scene and recursively build the list of skeleton bone names/indices. Then I do the same thing as you regarding clusters/weights/finding the correct bone indices.

This topic is closed to new replies.

Advertisement