使用 FBX SDK 获取索引
Posted
技术标签:
【中文标题】使用 FBX SDK 获取索引【英文标题】:Fetching indices with FBX SDK 【发布时间】:2018-11-28 07:45:34 【问题描述】:我无法确定使用什么来从 FBX 文件中获取索引。恢复数据非常好(至少对于位置而言),但我似乎无法找到如何获取索引。
免责声明:对于长段代码感到抱歉,如果您认为某些部分确实不必要,请随时编辑。
我目前正在获取这样的数据:
// 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()
至于索引,我正在迭代面(多边形)并获取它们的顶点索引:
// 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);
这样做对于只包含一个网格的 FBX 效果很好,但是当有多个网格时,它似乎无法缝合面。如果这来自我正在使用的数据结构,我将进一步彻底调查,但如果有人发现这有问题,请告诉我。
问题在于法线和 texcoords 的索引,我不知道如何获取。目前我正在检查两者的映射模式,并尝试相应地填充数据:
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;
我现在只处理面部 [顶点] 案例,我认为这对于 texcoords 是正确的(我目前没有获得材料,所以我无法检查)。但是对于法线,我找不到任何有关获取索引的信息;这就是为什么我只复制与位置相同的索引,我认为这是完全错误的:
// 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 /*...*/
这是恢复索引的正确方法吗?我错过了什么吗?
【问题讨论】:
【参考方案1】:首先你不应该把所有网格的数据放到同一个数组中。每个网格可以有不同的材质,所以应该用单独的drawcall来绘制。但如果这不是你的选择,那么错误就在那一行:
face_indices[currentPosPolyIndex][polyVertIndex] = mesh->GetPolygonVertex(polyIndex, polyVertIndex);
你把顶点放在一个数组中,像这样
[(1-st mesh) V1, V2, V3, ..., (2-nd mesh) V52, V53, V54, ...]
但是mesh->GetPolygonVertex(polyIndex, polyVertIndex)
返回的索引相对于当前网格,所以第二个网格不是从V52
开始,而是从V1
开始。所以你应该计算一些偏移量。
int currentVertIndex = points_coords.size();
int meshVertIndexStart = currentVertIndex;
...
for (int polyVertIndex = 0; polyVertIndex < polySize; ++polyVertIndex)
face_indices[currentPosPolyIndex][polyVertIndex] = meshVertIndexStart + mesh->GetPolygonVertex(polyIndex, polyVertIndex);
如果您获得索引,则法线和 UV 更好地进入同一个循环。这是一些代码
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++;
【讨论】:
以上是关于使用 FBX SDK 获取索引的主要内容,如果未能解决你的问题,请参考以下文章