Obtaining FBX node's global to local space transform correctly

49 views Asked by At

I'm trying to convert legacy models in proprietary format to FBX. I seem to be getting an FbxNode's global to local space transform matrix incorrectly in some cases. Here's the situation.

Skeleton bones (transforms) in the old format are specified as local to global space transforms unlike most modern formats where it's local to parent space i.e. a node's frame/space is described, not in terms of its parent but, in terms of the global frame.

I've to work with global space origins of bones to arrive at the closest skeleton/armature possible. Specifying just node positions with FbxNode::LclTranslation led to child bones not connecting to their parents physically (no grey bar between them), orienting its parent bone (FbxNode::LclRotation) such that parent's X axis points towards child's origin and setting the child's position only along X fixed the issue. Most of the bone connections are working as expected except for a few. Here's the code

  FbxVector4 childGlobal = getChildOriginInGlobalSpace();
  FbxAMatrix globalToParentLocal = GetLocalToGlobalXform(pParent).Inverse();
  // Map child origin in global space to parent local space; the obtained
  // position is incorrect for some nodes.
  FbxVector4 childParentLocal = globalToParentLocal.MultT(childGlobal);
  const auto distToChildParentLocal = childParentLocal.Length();
  FbxVector4 eulerXYZ;
  FbxVector4::AxisAlignmentInEulerAngle(
    /*pAB*/ {0.0, 0.0, 0.0, 1.0},
    /*pA*/ {1.0, 0.0, 0.0, 1.0},
    /*pB*/ childParentLocal,
    eulerXYZ);
  // Orient parent
  pParent->LclRotation.Set(eulerXYZ);
  // Specify child's origin in parent local space
  pNodeChild->LclTranslation.Set({ distToChildParentLocal, 0.0, 0.0});


// Problematic function which doesn't return the correct global to parent transform
FbxAMatrix GetLocalToGlobalXform(FbxNode* pNode)
{
  FbxAMatrix lLocalPosition;
  FbxAMatrix lGlobalPosition;
  FbxAMatrix lParentGlobalPosition;

  lLocalPosition.SetTRS(pNode->LclTranslation.Get(), pNode->LclRotation.Get(), pNode->LclScaling.Get());

  if (pNode->GetParent())
  {
    lParentGlobalPosition = GetGlobalDefaultPosition(pNode->GetParent());
    lGlobalPosition = lParentGlobalPosition * lLocalPosition;
  }
  else
  {
    lGlobalPosition = lLocalPosition;
  }

  return lGlobalPosition;
}

Notice below that most bones are sitting at the right place with their parent correctly oriented. However, the ones crossed red are incorrect because their origin in parent space is coming out to be incorrect; GetLocalToGlobalXform() returns incorrect transforms for some nodes. What am I doing incorrectly in obtaining a node's global transform? Am I missing things like pivots, pre-/post-rotation, geometric transforms of nodes, etc.? I used FbxNode::EvaluateGlobalTransform() to no avail. The FBX documentation is very thin around this. Hence this question.

I've verified that origin for these nodes in global space is correct (the white dummies created with nodes' global positions), while their origin in parent-local space is incorrect leads to these off bones.

enter image description here


For those wondering why not convert local to global transforms into local to parent transforms; I did try it and it's a futile attempt.

I converted the transforms to local space with

  FbxMatrix mapLocalToParent = mapLocalToGlobal * mapParentLocalToGlobal.Inverse();

only to realize there're unwanted shears in some of these transforms. Building a node tree/skeleton using Fbx's FbxNode::LclTranslation, FbxNode::LclRotation and FbxNode::LclScale doesn't help; I get a deformed skeleton as shears are unaccounted.

0

There are 0 answers