[Python] 从FBX文件中读取数据

本篇博客是对应于上一篇博客:Python下自定义数据导出为FBX,这篇博客主要介绍如何将FBX模型中的UV数据提取出来。

1. 初始化FBX设置

首先要做的就是是创建FBX SDK管理器,它几乎是所有FBX SDK中的类的对象分配器。

接着,创建FBX IO设置,并为FBX SDK管理器设置IO设置。

然后,创建一个空白的场景,用于读入数据。

lSdkManager = FbxManager.Create()
if not lSdkManager:
    sys.exit(0)

ios = FbxIOSettings.Create(lSdkManager, IOSROOT)
lSdkManager.SetIOSettings(ios)

lScene = FbxScene.Create(lSdkManager, "")

2. 导入模型

导入模型时,先创建FbxImporter并初始化,Initialize 方法的第一个参数是导入文件保存路径,第二个参数表示导入的FBX文件类型,这里1代表为ASCii码格式。然后,调用该对象的Import方法将fbx模型导入到当前的场景,最后使用GetRootNode获取场景的根节点。

def import_scene(fbxManager, fbxScene, fbxFilePath):
    lImporter = FbxImporter.Create(fbxManager, "")
    result = lImporter.Initialize(fbxFilePath, 1, fbxManager.GetIOSettings())
    if not result:
        return False

    result = lImporter.Import(fbxScene)
    lImporter.Destroy()

    fbxIOSettings = FbxIOSettings.Create(fbxManager, IOSROOT)
    fbxManager.SetIOSettings(fbxIOSettings)

    if not result:
        raise IOError("load file [{}] failed".format(fbxFilePath))
    fbxRootNode = fbxScene.GetRootNode()

    return fbxRootNode, fbxIOSettings

3. 读入模型UV数据

3.1 读入mesh数据

首先,通过GetChild(i)遍历获取fbx文件中的每个模型,然后调用GetMesh获取每个模型的mesh数据。

下一步,使用 GetControlPoints() 方法获取网格的控制点。然后,我们遍历控制点,并将每个控制点的坐标存储在 vertices 数组中。

接下来,我们使用 GetPolygonCount() 方法获取网格的多边形数,并使用 GetPolygonSize()GetPolygonVertex() 方法获取每个多边形的顶点索引,并将它们存储在 faces 数组中。

最后,我们使用 GetNormals() 方法获取网格的法线数据,并将它们存储在 normals 数组中。

for i in range(fbxRootNode.GetChildCount()):
    fbxMesh = fbxRootNode.GetChild(i)
    if fbxMesh is None:
        raise RuntimeError("The node is null!")
    fbxMeshName = fbxMesh.GetName()

    mesh = fbxMesh.GetMesh()

    # 提取顶点数据
    vertices = []
    control_points = mesh.GetControlPoints()
    for i in range(mesh.GetControlPointsCount()):
        vertices.append((control_points[i][0], control_points[i][1], control_points[i][2]))

    # 提取面数据
    faces = []
    polygon_count = mesh.GetPolygonCount()
    for i in range(polygon_count):
        polygon_size = mesh.GetPolygonSize(i)
        for j in range(polygon_size):
            faces.append(mesh.GetPolygonVertex(i, j))

    # 提取法线数据
    normals = []
    layer_element_normal = mesh.GetLayer(0).GetNormals()
    if layer_element_normal:
        for i in range(mesh.GetPolygonCount() * 3):
            normal = layer_element_normal.GetDirectArray().GetAt(i)
            normals.append((normal[0], normal[1], normal[2]))

3.2 读入UV数据

对于UV数据,直接调用GetElementUV方法即可获取对应名称的UV数据

然后用UVValue存放UV中的顶点数据,用UVIndex存放面中的顶点序号数组,用UVCount存放每个面的顶点数目。

通过UV数据的GetDirectArray()可以访问其顶点数组,通过GetIndexArray()可以访问面的顶点序号数组。
接着分别调用对应数组的GetAt方法获取对应的数据存入UVValueUVIndex,最后使用GetPolygonSize方法获取每个面的大小存入UVCount中.

    try:
        # init output uv data
        UVValue = []
        UVIndex = []
        UVCount = []
        UVData = mesh.GetElementUV("map01")
    
        numVtx = UVData.GetDirectArray().GetCount()
        numIndex = UVData.GetIndexArray().GetCount()

        # separate uv data: value, index, count
        for i in range(numVtx):
            UVValue.append(UVData.GetDirectArray().GetAt(i)[0])
            UVValue.append(UVData.GetDirectArray().GetAt(i)[1])
        for j in range(numIndex):
            UVIndex.append(UVData.GetIndexArray().GetAt(j))
        for k in range(mesh.GetPolygonCount()):
            UVCount.append(mesh.GetPolygonSize(k))

    except Exception as e:
        runtime_msg.popup_msgbox(info_loader.return_tips_msg(plugin_msg_code.PLUGIN_E_WRITE_UV))
        raise e