Recently, trying to use FBX sdk to import a 3d model made with 3dmax, i almost immediately got in trouble with transformations. A very simple mesh(a sphere split in two halves) consisting of two nodes has one of it's nodes offset no matter what. I tried several(quite ambiguous) ways of calculating transform latest SDK documentation provides... But result is the same. I'll provide the code and the mesh in case anyone can point out any mistakes.
Helper Functions:
FbxAMatrix MeshManager::GetGlobalPosition(FbxNode* pNode, const FbxTime& pTime, FbxPose* pPose, FbxAMatrix* pParentGlobalPosition)
{
FbxAMatrix lGlobalPosition;
bool lPositionFound = false;
if (pPose)
{
int lNodeIndex = pPose->Find(pNode);
if (lNodeIndex > -1)
{
// The bind pose is always a global matrix.
// If we have a rest pose, we need to check if it is
// stored in global or local space.
if (pPose->IsBindPose() || !pPose->IsLocalMatrix(lNodeIndex))
{
lGlobalPosition = GetPoseMatrix(pPose, lNodeIndex);
}
else
{
// We have a local matrix, we need to convert it to
// a global space matrix.
FbxAMatrix lParentGlobalPosition;
if (pParentGlobalPosition)
{
lParentGlobalPosition = *pParentGlobalPosition;
}
else
{
if (pNode->GetParent())
{
lParentGlobalPosition = GetGlobalPosition(pNode->GetParent(), pTime, pPose);
}
}
FbxAMatrix lLocalPosition = GetPoseMatrix(pPose, lNodeIndex);
lGlobalPosition = lParentGlobalPosition * lLocalPosition;
}
lPositionFound = true;
}
}
if (!lPositionFound)
{
// There is no pose entry for that node, get the current global position instead.
// Ideally this would use parent global position and local position to compute the global position.
// Unfortunately the equation
// lGlobalPosition = pParentGlobalPosition * lLocalPosition
// does not hold when inheritance type is other than "Parent" (RSrs).
// To compute the parent rotation and scaling is tricky in the RrSs and Rrs cases.
lGlobalPosition = pNode->EvaluateGlobalTransform(pTime);
}
return lGlobalPosition;
}
// Get the matrix of the given pose
FbxAMatrix MeshManager::GetPoseMatrix(FbxPose* pPose, int pNodeIndex)
{
FbxAMatrix lPoseMatrix;
FbxMatrix lMatrix = pPose->GetMatrix(pNodeIndex);
memcpy((double*)lPoseMatrix, (double*)lMatrix, sizeof(lMatrix.mData));
return lPoseMatrix;
}
// Get the geometry offset to a node. It is never inherited by the children.
FbxAMatrix MeshManager::GetGeometry(FbxNode* pNode)
{
const FbxVector4 lT = pNode->GetGeometricTranslation(FbxNode::eSourcePivot);
const FbxVector4 lR = pNode->GetGeometricRotation(FbxNode::eSourcePivot);
const FbxVector4 lS = pNode->GetGeometricScaling(FbxNode::eSourcePivot);
return FbxAMatrix(lT, lR, lS);
}
mat4 FbxMatToGlm(const FbxAMatrix& mat) {
dvec4 c0 = glm::make_vec4((double*)mat.GetColumn(0).Buffer());
dvec4 c1 = glm::make_vec4((double*)mat.GetColumn(1).Buffer());
dvec4 c2 = glm::make_vec4((double*)mat.GetColumn(2).Buffer());
dvec4 c3 = glm::make_vec4((double*)mat.GetColumn(3).Buffer());
glm::mat4 convertMatr = mat4(c0, c1, c2, c3);
return inverse(convertMatr);
}
Mesh Extraction:
void MeshManager::extractMeshRecursive(FbxScene* mScene, FbxNode* pNode, FbxAMatrix& pParentGlobalPosition, shared_ptr<Mesh> mesh, unsigned ¤tNode) {
// Find out what type of node this is
FbxNodeAttribute* lNodeAttribute = pNode->GetNodeAttribute();
FbxAMatrix lGlobalPosition = GetGlobalPosition(pNode, 1, mScene->GetPose(-1) , &pParentGlobalPosition);
FbxAMatrix lGeometryOffset = GetGeometry(pNode);
FbxAMatrix lGlobalOffsetPosition = lGlobalPosition * lGeometryOffset;
if (lNodeAttribute)
{
// Get the actual node mesh data if it is a mesh this time
// (You could use this like the sample where they draw other nodes like cameras)
if (lNodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh)
{
// Draw the actual mesh data
FbxMesh* lMesh = pNode->GetMesh();
if (lMesh->IsTriangleMesh() == false) {
FbxGeometryConverter conv(mFbxManager);
conv.Triangulate(lNodeAttribute, true);
}
const uint lVertexCount = lMesh->GetControlPointsCount();
const uint lTriangleCount = lMesh->GetPolygonCount();
// May not have any vertex data
if (lVertexCount == 0) return;
mesh->nodes.push_back(MeshNode());
FbxVector4* pControlPoints = lMesh->GetControlPoints();
for (uint i = 0; i < lVertexCount; i++)
{
mesh->nodes[currentNode].vertices.push_back(vec3((float)pControlPoints[i].mData[0], (float)pControlPoints[i].mData[1], (float)pControlPoints[i].mData[2]));
}
mesh->nodes[currentNode].localTransform = FbxMatToGlm(lGlobalOffsetPosition);
}
currentNode++;
}
... Extracting other vertex attributes and materials ...
// Now check if this node has any children attached
const int lChildCount = pNode->GetChildCount();
for (int lChildIndex = 0; lChildIndex < lChildCount; ++lChildIndex)
{
// Draw this child
extractMeshRecursive(mScene, pNode->GetChild(lChildIndex), lGlobalPosition, mesh, currentNode);
}
}