FBX是Autodesk公司开发的一种通用3D文件格式,可以在不同的3D软件之间进行交换和共享。FBX文件包含了3D模型的几何形状、材质、动画、灯光、相机和其他相关信息。
如果想在当前流行的 3D 编辑器(如 Maya 和 3DS Max)与 3D 体验引擎(如 Unity、Unreal Engine)之间传输数据,则应使用 FBX 文件格式。这是这个文件格式主要的设计目的。
关于FBX模型的更多详细信息,可以看我的另一篇博客:FBX文件格式:3D建模和游戏开发的通用语言----FBX模型详解
本篇博客是基于上一篇博客:从Max中读取模型数据,介绍如何将获取到的数据转换为fbx模型导出。
1. 初始化FBX设置
首先要做的就是是创建FBX SDK管理器,它几乎是所有FBX SDK中的类的对象分配器。
接着,创建FBX IO设置,并为FBX SDK管理器设置IO设置。
然后,创建一个空白的场景,用于写入数据。
最后需要注意的是,由于FBX SDK默认的是Y轴向上,而3ds Max默认的是Z轴向上,因此需要进行利用FbxAxisSystem
的ConvertScene
进行轴转换。
lSdkManager = FbxManager.Create()
if not lSdkManager:
sys.exit(0)
ios = FbxIOSettings.Create(lSdkManager, IOSROOT)
lSdkManager.SetIOSettings(ios)
lScene = FbxScene.Create(lSdkManager, "")
if lScene .GetGlobalSettings().GetSystemUnit() != fbx.FbxAxisSystem.eMayaZUp:
fbx.FbxAxisSystem.MayaZUp.ConvertScene(lScene)
2. 写入模型数据
我之前获取到的数据主要就是模型的mesh数据,UV数据以及变换属性。下面依次介绍如何将这些数据写入。
2.1 写入mesh数据
首先,创建一个新的FbxMesh
用于写入。接着,调用InitControlPoints
初始化控制点,然后将顶点数据写入控制点。
下一步,先构造模型的材料属性并设置材料映射。接着,将面数据写入多边形中。BeginPolygon
表示开始多边形构造, AddPolygon
向多边形中添加顶点的ID, EndPolygon
表示多边形构造结束。
最后是写入法线数据。先构造模型的法线属性并设置法线的映射类型。接着,
通过GetDirectArray
方法获取法线数组,接着调用Add
写入法线数据。
lMesh = fbx.FbxMesh.Create(fbxScene, "")
lMesh.InitControlPoints(len(vertices))
for i, vertex in enumerate(vertices):
lMesh.SetControlPointAt(vertex, i)
materialElement = lMesh.CreateElementMaterial()
materialElement.SetMappingMode(fbx.FbxLayerElement.eByPolygon)
materialElement.SetReferenceMode(fbx.FbxLayerElement.eIndexToDirect)
for i in range(len(faces)):
face = faces[i]
lMesh.BeginPolygon(material_ids[i])
for index in face:
Mesh.AddPolygon(index)
Mesh.EndPolygon()
normalElement = lMesh.CreateElementNormal()
normalElement.SetMappingMode(fbx.FbxLayerElement.eByPolygonVertex)
normalElement.SetReferenceMode(fbx.FbxLayerElement.eDirect)
for normal in normals:
normalElement.GetDirectArray().Add(normal)
2.2 写入UV
先构造模型的UV属性并设置UV映射,接着通过GetDirectArray
方法获取UV顶点数组,然后调用Add
写入UV的顶点数据。
接下来,调用GetIndexArray
获取面索引数组,然后调用Add
写入UV的面的顶点索引数据。
UVElement = lMesh.CreateElementUV("")
UVElement.SetMappingMode(fbx.FbxLayerElement.eByPolygonVertex)
UVElement.SetReferenceMode(fbx.FbxLayerElement.eIndexToDirect)
for i in range(numVtx):
UVValue = fbx.FbxVector2(verts[i][0], verts[i][1])
UVElement.GetDirectArray().Add(UVValue)
for i in range(numIndex):
UVIndex = faces[i]
for j in range(len(UVIndex)):
UVElement.GetIndexArray().Add(UVIndex[j] - 1)
2.3 写入变换属性
在写入变换属性之前,需要先在场景中构造一个节点FbxNode
代表模型,并将刚刚写入的lMesh通过SetNodeAttribute
方法赋予给这个节点。
lNode = fbx.FbxNode.Create(lScene, "")
lNode.SetNodeAttribute(lMesh)
接着,就可以向这个节点设置变换属性了。直接使用节点的对应属性的set
方法就可以完成。
lNode.LclTranslation.Set(LclTranslation)
lNode.LclRotation.Set(LclRotation)
lNode.LclScaling.Set(LclScaling)
lNode.GeometricTranslation.Set(GeometricTranslation)
lNode.GeometricRotation.Set(GeometricRotation)
lNode.GeometricScaling.Set(GeometricScaling)
3. 导出模型
导出模型时,需要将刚刚的那个节点放入场景中。先通过GetRootNode
获取场景的根节点,然后使用AddChild
将其加入根节点的孩子。
最后,创建FbxExporter
并初始化,Initialize
方法的第一个参数是导出文件保存路径,第二个参数表示导出的FBX文件类型,这里1代表为ASCii码格式。然后,调用该对象的Export
方法将当前的场景导出为fbx模型。
lRootNode = fbxScene.GetRootNode()
lRootNode.AddChild(lNode)
exporter = FbxExporter.Create(fbxManager, "")
exporter.Initialize(output_path, 1, fbxIOSettings)
exporter.Export(fbxOutputScene)
exporter.Destroy()