3

I'm having trouble figuring what to use to get indices from a FBX file. Recovering data is perfectly fine (at least for positions anyway), but I can't seem to find how to get indices.

Disclaimer: sorry about the long pieces of code, feel free to edit if you think some parts are really unnecessary.

I'm currently getting data like this:

// Recovering geometry
for (int meshIndex = 0; meshIndex < scene->GetGeometryCount(); ++meshIndex)
{
  const auto mesh = static_cast<FbxMesh*>(scene->GetGeometry(meshIndex));

  // Recovering positions
  int currentVertIndex = points_coords.size();
  points_coords.resize(currentVertIndex + mesh->GetControlPointsCount(), std::vector<coord_type>(3));

  for (int vertIndex = 0; vertIndex < mesh->GetControlPointsCount(); ++vertIndex, ++currentVertIndex)
  {
    const auto& vertPos = mesh->GetControlPointAt(vertIndex);

    points_coords[currentVertIndex][0] = vertPos[0]; // X position
    points_coords[currentVertIndex][1] = vertPos[1]; // Y position
    points_coords[currentVertIndex][2] = vertPos[2]; // Z position
  }

  // Iterate over normals with mesh->GetElementNormal()->GetDirectArray().GetCount()

  // Iterate over texcoords with mesh->GetElementUV()->GetDirectArray().GetCount()

As for indices, I'm iterating over faces (polygons) and getting their vertices' indices as such:

// Fetching positions' indices
int currentPosPolyIndex = face_indices.size();
face_indices.resize(currentPosPolyIndex + mesh->GetPolygonCount());

for (int polyIndex = 0; polyIndex < mesh->GetPolygonCount(); ++polyIndex, ++currentPosPolyIndex)
{
  const auto polySize = mesh->GetPolygonSize(polyIndex);
  face_indices[currentPosPolyIndex].resize(polySize);

  for (int polyVertIndex = 0; polyVertIndex < polySize; ++polyVertIndex)
    face_indices[currentPosPolyIndex][polyVertIndex] = mesh->GetPolygonVertex(polyIndex, polyVertIndex);
}

Doing this works fine for FBXs containing only one mesh, but when there are several it seems it can't seam the faces. I will thoroughly investigate further if this comes from the data structures I'm using, but if anyone finds a problem in this, please let me know.

The problem lies within normals & texcoords' indices, which I can't figure how to fetch. Currently I'm checking the mapping mode for both, and trying to fill data accordingly:

  const auto texMapping = mesh->GetElementUV()->GetMappingMode();

  if (texMapping == FbxLayerElement::EMappingMode::eByControlPoint)
  {
    std::cout << "[FbxFileReader] Mapping mesh's texture coordinates by vertex." << std::endl;

    texture_face_indices.resize(texture_face_indices.size() + mesh->GetPolygonCount());

    std::copy(face_indices.cend() - mesh->GetPolygonCount(), face_indices.cend(), texture_face_indices.end() - mesh->GetPolygonCount());
  }
  else if (texMapping == FbxLayerElement::EMappingMode::eByEdge)
  {
    std::cout << "[FbxFileReader] Mapping mesh's texture coordinates by halfedge." << std::endl;
  }
  else if (texMapping == FbxLayerElement::EMappingMode::eByPolygon || texMapping == FbxLayerElement::EMappingMode::eByPolygonVertex)
  {
    std::cout << "[FbxFileReader] Mapping mesh's texture coordinates by face" << (texMapping == FbxLayerElement::EMappingMode::eByPolygonVertex ? " vertices" : "") << '.' << std::endl;

    int currentTexPolyIndex = texture_face_indices.size();
    texture_face_indices.resize(currentTexPolyIndex + mesh->GetPolygonCount());

    for (int polyIndex = 0; polyIndex < mesh->GetPolygonCount(); ++polyIndex, ++currentTexPolyIndex)
    {
      if (texMapping == FbxLayerElement::EMappingMode::eByPolygonVertex)
      {
        const auto polySize = mesh->GetPolygonSize(polyIndex);
        texture_face_indices[currentTexPolyIndex].resize(polySize);

        for (int polyVertIndex = 0; polyVertIndex < polySize; ++polyVertIndex)
          texture_face_indices[currentTexPolyIndex][polyVertIndex] = mesh->GetTextureUVIndex(polyIndex, polyVertIndex);
      }
      else
      {
        // Fetch face's texcoords & add it
        //texture_face_indices[currentTexPolyIndex].emplace_back(...);
      }
    }
  }
  else if (texMapping == FbxLayerElement::EMappingMode::eAllSame)
  {
    std::cout << "[FbxFileReader] Mapping mesh's texture coordinates by mesh." << std::endl;
  }
  else
  {
    std::cerr << "[FbxFileReader] Couldn't handle mesh's texture coordinates' mapping mode." << std::endl;
  }

I'm only handling face [vertices] cases for now, and I think it is correct for texcoords (I'm not getting materials at the moment so I can't check). But for normals, I can't find anything about getting their indices; this is why I'm only copying the same indices as positions', which I assume is plain wrong:

  // Same checks for normals
  else if (normMapping == FbxLayerElement::EMappingMode::eByPolygon || normMapping == FbxLayerElement::EMappingMode::eByPolygonVertex)
  {
    std::cout << "[FbxFileReader] Mapping mesh's normals by face" << (normMapping == FbxLayerElement::EMappingMode::eByPolygonVertex ? " vertices" : "") << "." << std::endl;

    int currentNormPolyIndex = normal_face_indices.size();
    normal_face_indices.resize(currentNormPolyIndex + mesh->GetPolygonCount());

    for (int polyIndex = 0; polyIndex < mesh->GetPolygonCount(); ++polyIndex, ++currentNormPolyIndex)
    {
      if (normMapping == FbxLayerElement::EMappingMode::eByPolygonVertex)
      {
        const auto polySize = mesh->GetPolygonSize(polyIndex);
        normal_face_indices[currentNormPolyIndex].resize(polySize);

        for (int polyVertIndex = 0; polyVertIndex < polySize; ++polyVertIndex)
          normal_face_indices[currentNormPolyIndex][polyVertIndex] = face_indices[currentNormPolyIndex][polyVertIndex];
      }
      else { /*...*/ }
    }
  }

Is this the proper way to recover the indices? Did I miss something?

Razakhel
  • 732
  • 13
  • 34

1 Answers1

3

First of all you should not put all meshes' data to same array. Every mesh can have different material, so should be drawn with separate drawcall. But if it's not your choice, error is in that line:

face_indices[currentPosPolyIndex][polyVertIndex] = mesh->GetPolygonVertex(polyIndex, polyVertIndex);

You put vertices in single array, like this

[(1-st mesh) V1, V2, V3, ..., (2-nd mesh) V52, V53, V54, ...]

But mesh->GetPolygonVertex(polyIndex, polyVertIndex) returns index relatively current mesh, so second mesh will start not from V52, but from V1. So you should calculate some offset.

int currentVertIndex = points_coords.size();
int meshVertIndexStart = currentVertIndex;
...
for (int polyVertIndex = 0; polyVertIndex < polySize; ++polyVertIndex)
    face_indices[currentPosPolyIndex][polyVertIndex] = meshVertIndexStart + mesh->GetPolygonVertex(polyIndex, polyVertIndex);

Normals and UV better to get in the same loop were you get indices. Here is some code

FbxVector4 getNormal(FbxGeometryElementNormal* normalElement, int polyIndex, int posIndex) {
    if (normalElement->GetMappingMode() == FbxGeometryElement::eByControlPoint) {
        if (normalElement->GetReferenceMode() == FbxGeometryElement::eDirect)
            return normalElement->GetDirectArray().GetAt(posIndex);
        int i = normalElement->GetIndexArray().GetAt(posIndex);
        return normalElement->GetDirectArray().GetAt(i);
    }
    else if (normalElement->GetMappingMode() == FbxGeometryElement::eByPolygonVertex) {
        if (normalElement->GetReferenceMode() == FbxGeometryElement::eDirect)
            return normalElement->GetDirectArray().GetAt(polyIndex);
        int i = normalElement->GetIndexArray().GetAt(polyIndex);
        return normalElement->GetDirectArray().GetAt(i);
    }
    return FbxVector4();
}

FbxVector2 getUV(FbxGeometryElementUV* uvElement, int polyIndex, int posIndex) {
    if (uvElement->GetMappingMode() == FbxGeometryElement::eByControlPoint) {
        if (uvElement->GetReferenceMode() == FbxGeometryElement::eDirect)
            return uvElement->GetDirectArray().GetAt(posIndex);
        int i = uvElement->GetIndexArray().GetAt(posIndex);
        return uvElement->GetDirectArray().GetAt(i);
    }
    else if (uvElement->GetMappingMode() == FbxGeometryElement::eByPolygonVertex) {
        if (uvElement->GetReferenceMode() == FbxGeometryElement::eDirect)
            return uvElement->GetDirectArray().GetAt(polyIndex);
        int i = uvElement->GetIndexArray().GetAt(polyIndex);
        return uvElement->GetDirectArray().GetAt(i);
    }
    return FbxVector2();
}

...

int vertNum = 0;

for (int polyIndex = 0; polyIndex < mesh->GetPolygonCount(); ++polyIndex, ++currentPosPolyIndex) {
    const auto polySize = mesh->GetPolygonSize(polyIndex);
    face_indices[currentPosPolyIndex].resize(polySize);

    for (int polyVertIndex = 0; polyVertIndex < polySize; ++polyVertIndex) {
        int vertIndex = mesh->GetPolygonVertex(polyIndex, polyVertIndex);
        face_indices[currentPosPolyIndex][polyVertIndex] = meshVertIndexStart + vertIndex;

        FbxVector4 normal = getNormal(mesh->GetElementNormal(), vertNum, vertIndex);
        FbxVector2 uv = getUV(mesh->GetElementUV(), vertNum, vertIndex);
        vertNum++;
    }
}
Guinzoo
  • 164
  • 1
  • 5