Windows環境配置
FBX SDK安裝后,目錄下有三個文件夾:
- include 頭文件
- lib 編譯的二進制庫,根據你項目的配置去包含相應的庫
- samples 官方使用案列
動態鏈接
libfbxsdk.dll, libfbxsdk.lib
是動態庫,需要在配置屬性->C/C++->預處理器->預處理器定義
中添加FBXSDK_SHARED
;
靜態鏈接
libfbxsdk-md.lib, libfbxsdk-mt.lib
是兩種靜態庫,不同的是運行庫選項,在配置屬性->C/C++->代碼生成->運行庫
中設置/MD
或/MT
,/MDd
和/MTd
時debug模式。
mt
編譯時,將LIBCMT.lib
編譯到obj
文件中,連接器通過它處理外部符號,會將引用的外部庫集成到生成的庫里面;md
編譯時,將MSVCRT.lib
編譯到obj
文件中,鏈接器會鏈接到MSVCRT.dll
,因此生成的庫會比mt
的小;
如果時debug模式的靜態鏈接,還需要在配置屬性->鏈接器->輸入->忽略特定默認庫
項添加LIBCMT
,配置屬性->鏈接器->輸入->附加依賴項
項添加wininet.lib
注意:FBX SDK可以使用多線程,但是不保證線程安全。
FBX SDK基礎知識
內存管理
FbxManager
類用來管理FBX SDK對象的創建和銷毀,且每一個程序里面只能有一個FbxManager
的實例。
// 創建FbxManager的實例
FbxManager* lSdkManager = FbxManager::Create();
// 使用FbxManager來創建對象
FbxScene* lScene = FbxScene::Create(lSdkManager, "Scene Name");
// 釋放所有FbxManager分配的內存
lSdkManager->Destroy();
導入導出設置
FbxIOSettings ioSettings = FbxIOSettings::Create(lSdkManager, IOSROOT); // 創建FbxIOSettings
mFbxSettings->SetBoolProp(IMP_FBX_MATERIAL, true); // 設置材質導入
lSdkManager->SetIOSettings(ioSettings); // 應用設置
設置導入導出對象,所有設置的默認值為true
mFbxSettings->SetBoolProp(IMP_FBX_MATERIAL, true) // 輸入設置,以“IMP_”為前綴
mFbxSettings->SetBoolProp(EXP_FBX_MATERIAL, true) // 輸出設置,以“EXP_”為前綴
導出文件格式設置
EXP_FBX
導出二進制FBX文件,能嵌入媒體文件
EXP_ASCIIFBX
導出ascii FBX文件
EXP_DXF
導出DXF文件
EXP_COLLADA
導出collada文件
EXP_OBJ
導出OBJ文件
嵌入媒體文件
只有binary fbx文件才能嵌入媒體文件。當導入有媒體文件嵌入的fbx文件(myExportFile.fbx)時,將提取嵌入的媒體文件到當前目錄下的myExportFile.fbm/subdirectory文件夾中。
mFbxSettings->SetBoolProp(EXP_FBX_EMBEDDED, true); // 導出Fbx binary文件時,媒體文件嵌入到導出文件中
設置密碼保護
導出時設置密碼
FbxString lString;// 將密碼設置給lStringmFbxSettings->SetStringProp(EXP_FBX_PASSWORD, lString);
mFbxSettings->SetBoolProp(EXP_FBX_PASSWORD_ENABLE, true);
導入時填寫密碼
FbxString lString;// 將密碼設置給lStringmFbxSettings->SetStringProp(IMP_FBX_PASSWORD, lString);
mFbxSettings->SetBoolProp(IMP_FBX_PASSWORD_ENABLE, true);
打印支持的寫入和讀取文件格式
void PrintWriterFormatAndReaderFormat(FbxManager* fbxManager)
{FbxIOPluginRegistry* ioPlugin = fbxManager->GetIOPluginRegistry();int readerFormatCnt = ioPlugin->GetReaderFormatCount();printf("read format count = %d\n", readerFormatCnt);for (int i = 0; i < readerFormatCnt; i++){const char* extension = ioPlugin->GetReaderFormatExtension(i);const char* desc = ioPlugin->GetReaderFormatDescription(i);printf("extension = %s description = %s\n", extension, desc);}int writerFormatCnt = ioPlugin->GetWriterFormatCount();printf("writer format count = %d\n", writerFormatCnt);for (int i = 0; i < writerFormatCnt; i++){const char* extension = ioPlugin->GetWriterFormatExtension(i);const char* desc = ioPlugin->GetWriterFormatDescription(i);printf("extension = %s description = %s\n", extension, desc);}
}
導入導出場景
導入場景
FbxImporter* lImporter = FbxImporter::Create(lSdkManager, "");
bool lImportStatus = lImporter->Initialize(lFileName, -1, mFbxSettings); // -1 表示讓fbxsdk根據文件的擴展名去判斷文件的格式
if(!lImportStatus)
{printf("Call to FbxImporter::Initialize() failed.\n");printf("Error returned: %s\n\n", lImporter->GetStatus().GetErrorString());exit(-1);
}// 將文件中的內容導入到scene對象中
FbxScene* scene = FbxScene::Create(lSdkManager, "");
lImporter->Import(scene);// 導入完成后就可以安全銷毀,減少內存占用
lImporter->Destroy();
索引非嵌入的多媒體文件
當ASCII FBX或其他不包含嵌入媒體的文件格式定位引用的媒體文件時,遵循下面兩個步驟:
-
使用絕對路徑來檢查引用的多媒體文件是否存在,這個絕對路徑是導出場景到Fbx文件時指定的
FbxFileTexture::SetFileName()
// The resource file is in the application's directory. FbxString lTexPath = gAppPath + "\\Crate.jpg";// Create a texture object. FbxFileTexture* lTexture = FbxFileTexture::Create(pScene,"Crate Texture");// Set the texture's absolute file path. lTexture->SetFileName(lTexPath.Buffer())
-
如果絕對路徑不存在多媒體文件,則使用相對路徑,相對路徑在導出場景到FBX文件時會自動保存
獲取導入文件的版本
int lFileMajor, lFileMinor, lFileRevision;
lImporter->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);
導出場景
FbxExporter* lExporter = FbxExporter::Create(lSdkmanager, "");
const char* lFilename = "file.fbx";
bool lExportStatus = lExporter->Initialize(lFilename, -1, lSdkManager->GetIOSettings());
if(!lExportStatus) {printf("Call to FbxExporter::Initialize() failed.\n");printf("Error returned: %s\n\n", lExporter->GetStatus().GetErrorString());return false;
}FbxScene* scene = FbxScene::Create(lSdkManager, "myScene");
lExporter->Export(lScene);
lExporter->Destroy();
坐標系和單位轉換
我們讀取FBX文件的數據到自己的應用中,需要的坐標系和單位可能和FBX文件的不一樣,這時需要去修改這些設置。使用FBX SDK創建的對象是右手坐標系,Y軸向上。
// 轉換為OpenGL坐標系
FbxGlobalSettings& globalSettings = mScene->GetGlobalSettings();
if (globalSettings.GetAxisSystem() != FbxAxisSystem::OpenGL)
{FbxAxisSystem::OpenGL.ConvertScene(mScene);
}// 轉換為m作為單位
FbxSystemUnit systemUnit = globalSettings.GetSystemUnit();
if (globalSettings.GetSystemUnit() != FbxSystemUnit::m)
{const FbxSystemUnit::ConversionOptions options = {false, // mConvertRrsNodestrue, // mConvertLimitstrue, // mConvertClusterstrue, // mConvertLightIntensitytrue, // mConvertPhotometricLPropertiestrue, // mConvertCameraClipPlanes};FbxSystemUnit::m.ConvertScene(mScene, options);
}
注意:坐標系轉換只會作用到節點Transform的pre-rotaion和動畫,單位轉換只會作用到節點Transform的scale和動畫,并不會作用到頂點的值,比如位置,法線和UV。因此頂點值的轉換需要自己去處理。
OpenGL和DirectX坐標系的轉換
OpenGL坐標系是Y軸向上,X軸向右,Z軸向屏幕外,DirectX坐標系是Y軸向上,X軸向右,Z軸向屏幕內。如果將DirectX坐標系的頂點轉換為OpenGL坐標系的頂點,我們以OpenGL的坐標系為世界坐標系,三個軸的基向量為 x ? 0 ( 1 , 0 , 0 ) , y ? 0 ( 0 , 1 , 0 ) , z ? 0 ( 0 , 0 , 1 ) \vec x_0(1,0,0),\vec y_0(0,1,0),\vec z_0(0,0,1) x0?(1,0,0),y?0?(0,1,0),z0?(0,0,1),則DirectX坐標系的三個軸的基向量為 x ? d ( 1 , 0 , 0 ) , y ? d ( 0 , 1 , 0 ) , z ? d ( 0 , 0 , ? 1 ) \vec x_d(1,0,0),\vec y_d(0,1,0),\vec z_d(0,0,-1) xd?(1,0,0),y?d?(0,1,0),zd?(0,0,?1)。若點 P ( x p , y p , z p ) P(x_p,y_p,z_p) P(xp?,yp?,zp?)為DirectX坐標系中的一點,將其轉換為OpenGL坐標系為 ( ( x p , 0 , 0 ) ? x ? d , ( 0 , y p , 0 ) ? y ? d , ( 0 , 0 , z p ) ? z ? d ) = ( x p , y p , ? z p ) ((x_p,0,0) \cdot \vec x_d, (0,y_p,0) \cdot \vec y_d, (0,0,z_p) \cdot \vec z_d)=(x_p,y_p,-z_p) ((xp?,0,0)?xd?,(0,yp?,0)?y?d?,(0,0,zp?)?zd?)=(xp?,yp?,?zp?),所以x,y坐標保持不變,z坐標取反。頂點的位置,法線可以按照這個規則處理。
因為OpenGL是右手坐標系,DirectX是左手坐標系,它們三角面的頂點的順序是相反,所以 ( v 1 , v 2 , v 3 ) (v_1,v_2,v_3) (v1?,v2?,v3?)要變成 ( v 1 , v 3 , v 2 ) (v_1,v_3,v_2) (v1?,v3?,v2?)。
頂點的UV, V n e w = 1 ? V V_{new}=1-V Vnew?=1?V,怎么得出來的,目前不清楚。
FBX場景
每個FBX文件是一個FbxScene
,FbxScene
由FbxNode
以樹狀層級結構組成。
一個場景由許多元素組成,包含:meshes, lights, cameras, skeletons, NURBS等,這些元素繼承FbxNodeAttribute
,FbxNode
是一個或多個場景元素的容器。
FbxNode* lRootNode = lScene->GetRootNode();
if(lRootNode) {for(int i = 0; i < lRootNode->GetChildCount(); i++)PrintNode(lRootNode->GetChild(i));
}
// Print a node, its attributes, and all its children recursively.
void PrintNode(FbxNode* pNode) {const char* nodeName = pNode->GetName();FbxDouble3 translation = pNode->LclTranslation.Get();FbxDouble3 rotation = pNode->LclRotation.Get();FbxDouble3 scaling = pNode->LclScaling.Get();// Print the node's attributes.for(int i = 0; i < pNode->GetNodeAttributeCount(); i++)PrintAttribute(pNode->GetNodeAttributeByIndex(i));// Recursively print the children.for(int j = 0; j < pNode->GetChildCount(); j++)PrintNode(pNode->GetChild(j));
}
FBX對象,屬性,特性
FbxObject
FBX對象都繼承于FbxObject
,比如FbxScene
,FbxNode
,FbxImporter
, FbxExporter
, FbxCollection
等。
FbxCollection
是FBX object的容器,FbxAnimLayer
, FbxAnimStack
, FbxScene
都是繼承于它。
遍歷場景的動畫
int numStacks = mScene->GetSrcObjectCount<FbxAnimStack>();
for (int i = 0; i < numStacks; i++)
{FbxAnimStack* animStack = FbxCast<FbxAnimStack>(mScene->GetSrcObject(i));int numAnimLayers = animStack->GetMemberCount<FbxAnimLayer>();for (int layerIdx = 0; layerIdx < numAnimLayers; layerIdx++){FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(layerIdx);···}
}
FbxProperty
FbxProperty目前我知道的就是獲取FbxNode的變換數據。
FbxDouble* lTranslation = lNode->LclTranslation.Get().mData;
FbxDouble* lRotation = lNode->LclRotation.Get().mData;
FbxDouble* lScaling = lNode->LclScaling.Get().mData;
屬性的創建和銷毀
// 創建FBX Property
FbxProperty p = FbxProperty::Create(pScene, DTDouble3, "Vector3Property");
FbxSet<FbxDouble3>(p, FbxDouble3(1.1, 2.2, 3.3);// ... initialize FbxNode* lNode ...
FbxDouble3 translation = lNode->LclTranslation.Get();
FbxDouble3 rotation = lNode->LclRotation.Get();
FbxDouble3 scaling = lNode->LclScaling.Get();
FbxNodeAttribute
FbxNodeAttribute定義了一個場景元素,比如FbxMesh,FbxSkeleton。
遍歷FbxNode的FbxNodeAttribute
FbxNode* pNode;
int attrCount = pNode->GetNodeAttributeCount();
for (int i = 0; i < attrCount; i++)
{FbxNodeAttribute* attribute = pNode->GetNodeAttributeByIndex(i);FbxNodeAttribute::EType type = attribute->GetAttributeType();switch (type){case fbxsdk::FbxNodeAttribute::eSkeleton:mSkeletonNodes.push_back(pNode);break;case fbxsdk::FbxNodeAttribute::eMesh:mFbxMeshes.push_back((FbxMesh*)attribute);break;default:break;}
}
對象和屬性的關系
FbxObjec和FbxObject,FbxProperty和FbxProperty,FbxObject和FbxProperty直接可以建立父子關系,FBX SDK里面叫Connection。
GetSrcXXX
可以理解為獲取子對象,GetDstXX
可以理解為獲取父對象
- object-property
FbxObject::GetSrcProperty()
獲取節點的屬性,FbxProperty::GetDstObject()
獲取屬性綁定的節點 - object-object
FbxObject::GetSrcObject()
獲取子節點,FbxProperty::GetDstObject()
獲取父節點 - property-property
FbxProperty::GetSrcProperty()
獲取子屬性,FbxProperty::GetDstObject()
獲取父屬性
參考:
- FBX SDK文檔
- 官方案列