c++ – FBX SDK Importing Normals inconsistent with Maya Normals with Skinned and animated geometry

I am currently writing a FBX Importer using FBX SDK 2020, and I’ve been validating that the data I have is correct, by dragging and dropping the FBX into Maya, and viewing what Maya’s Component Editor shows the data to be.

My problem I have currently, is that when I am importing the Normals from FBX SDK, they appear to be slightly off compared to that of Maya for skinned meshes only (aka meshes that are bound to a skeleton) and have animation data.

My process has basically been to drag / drop the FBX into Maya, and put the model into Bind Pose, and view data in Maya’s Component Editor.

Below is a picture of Normals from the fbx file from my Importer using FBX SDK

From FBX SDK

Below is a picture of the same vertex but as seen in Maya’s Component Editor

Data seen in Maya's Component Editor

As seen above there is some error in the Nomal values in the hundreds decimal place, while the Position values are basically identical.


Below is another picture of the same data, but after deleting the Skeleton, and re-exporting from Maya to a new FBX file

enter image description here


UPDATE

I’ve tried with a different model, — a unit sphere with bones, and same problem happens, except this time normals are way off.

Here is the code used to produce issue, again using FBX SDK 2020 (most recent lib)

namespace test
{
  FbxSample::FbxSample()
  {

  }

  void FbxSample::ImportMesh(FbxNode& node, int depth)
  {
    auto log = std::ofstream("C:\\Projects\\FbxSamples\\log.txt");

    auto& globalTransform = node.EvaluateGlobalTransform(FBXSDK_TIME_INFINITE);

    FbxMesh* fbxMesh = (FbxMesh*)node.GetNodeAttribute();

    int triangleCount = fbxMesh->GetPolygonCount();
    auto meshControlPoints = fbxMesh->GetControlPoints();
    int meshControlPointsCount = fbxMesh->GetControlPointsCount();

    auto normals = fbxMesh->GetElementNormal();

    int vertexCount = 0;
    for (int polygonId = 0; polygonId < triangleCount; ++polygonId)
    {
      int polygonSize = fbxMesh->GetPolygonSize(polygonId);

      for (int j = 0; j < polygonSize; ++j, ++vertexCount)
      {
        int vertexIndex = fbxMesh->GetPolygonVertex(polygonId, j);

        auto position = meshControlPoints[vertexIndex];

        FbxVector4 normal;

        switch (normals->GetMappingMode())
        {
        case FbxGeometryElement::eByControlPoint:
          switch (normals->GetReferenceMode())
          {
          case FbxGeometryElement::eDirect:
            normal = normals->GetDirectArray().GetAt(vertexIndex);
            break;
          case FbxGeometryElement::eIndexToDirect:
          {
            int id = normals->GetIndexArray().GetAt(vertexIndex);
            normal = normals->GetDirectArray().GetAt(id);
          }
          break;
          }
          break;
        case FbxGeometryElement::eByPolygonVertex:
          switch (normals->GetReferenceMode())
          {
          case FbxGeometryElement::eDirect:
            normal = normals->GetDirectArray().GetAt(vertexCount);
            break;
          case FbxGeometryElement::eIndexToDirect:
          {
            int id = normals->GetIndexArray().GetAt(vertexCount);
            normal = normals->GetDirectArray().GetAt(id);
          }
          break;
          }
          break;
        }

        FbxVector4 normal2;
        fbxMesh->GetPolygonVertexNormal(polygonId, j, normal2);

        log << "Vertex Count: " << vertexCount << " Vertex Index: " << vertexIndex << std::endl;
        log << "Position: X: " << position[0] << " Y: " << position[1] << " Z: " << position[2] << " W: " << position[3] << std::endl;
        log << "Normal: X: " << normal[0] << " Y: " << normal[1] << " Z: " << normal[2] << " W: " << normal[3] << std::endl;
        log << "Normal2: X: " << normal2[0] << " Y: " << normal2[1] << " Z: " << normal2[2] << " W: " << normal2[3] << std::endl;
        log << std::endl;
      }
    }

    log.flush();
    log.close();
  }

  void FbxSample::Walk(FbxNode& node, int depth)
  {
    auto nodeAttribute = node.GetNodeAttribute();

    if (nodeAttribute)
    {
      FbxNodeAttribute::EType attributeType = nodeAttribute->GetAttributeType();

      switch (attributeType)
      {
      case fbxsdk::FbxNodeAttribute::eMesh:
        ImportMesh(node, depth);
        break;
      }
    }

    for (int i = 0; i < node.GetChildCount(); i++)
    {
      Walk(*node.GetChild(i), depth + 1);
    }
  }

  void FbxSample::Walk(FbxScene& fbxScene)
  {
    FbxNode* node = fbxScene.GetRootNode();

    if (node)
    {
      for (int i = 0; i < node->GetChildCount(); ++i)
      {
        Walk(*node->GetChild(i), 1);
      }
    }
  }

  void FbxSample::Test()
  {
    FbxManager* sdkManager = FbxManager::Create();
    if (!sdkManager)
    {
      FBXSDK_printf("Error: Unable to create FBX Manager!\n");
      exit(1);
    }
    else
    {
      FBXSDK_printf("Autodesk FBX SDK version %s\n", sdkManager->GetVersion());
    }

    FbxIOSettings* ios = FbxIOSettings::Create(sdkManager, IOSROOT);
    ios->SetBoolProp(IMP_FBX_MATERIAL, true);
    ios->SetBoolProp(IMP_FBX_TEXTURE, true);
    ios->SetBoolProp(IMP_GEOMETRY, true);
    ios->SetBoolProp(IMP_FBX_MODEL, true);
    ios->SetBoolProp(IMP_SKINS, true);
    ios->SetBoolProp(IMP_FBX_ANIMATION, true);
    ios->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, true);

    sdkManager->SetIOSettings(ios);

    auto importer = FbxImporter::Create(sdkManager, "");
    importer->Initialize("C:\\Projects\\FbxSamples\\SphereWithBones.fbx", -1, sdkManager->GetIOSettings());
    auto fbxScene = FbxScene::Create(sdkManager, "Scene");

    importer->Import(fbxScene);

    Walk(*fbxScene);
  }
}

Here is output:

Vertex Count: 0 Vertex Index: 20
Position: X: 0.293893 Y: -0.951057 Z: -0.0954916 W: 0
Normal: X: 0.298177 Y: 0.267987 Z: -0.916119 W: 1
Normal2: X: 0.298177 Y: 0.267987 Z: -0.916119 W: 1

Vertex Count: 1 Vertex Index: 0
Position: X: 0.148778 Y: -0.987688 Z: -0.0483409 W: 0
Normal: X: 0.18352 Y: 0.228479 Z: -0.956095 W: 1
Normal2: X: 0.18352 Y: 0.228479 Z: -0.956095 W: 1

Vertex Count: 2 Vertex Index: 21
Position: X: 0.25 Y: -0.951057 Z: -0.181636 W: 0
Normal: X: 0.246448 Y: 0.378525 Z: -0.892179 W: 1
Normal2: X: 0.246448 Y: 0.378525 Z: -0.892179 W: 1

As seen above — a unit sphere in bind pose should have Normals close to that of the Position X/Y/Z — where above normals are just flat out wrong.

Removing bones in the same FBX file, and re-running code above — normals correctly match what is in Maya.


I’ve also posted the same question on autodesk forum that has the sample FBX file that you can download + use code above to reproduce the issue

Read more here: Source link