FBX SDK的使用:基礎知識

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或其他不包含嵌入媒體的文件格式定位引用的媒體文件時,遵循下面兩個步驟:

  1. 使用絕對路徑來檢查引用的多媒體文件是否存在,這個絕對路徑是導出場景到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())
    
  2. 如果絕對路徑不存在多媒體文件,則使用相對路徑,相對路徑在導出場景到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) x 0?(1,0,0)y ?0?(0,1,0)z 0?(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) x d?(1,0,0)y ?d?(0,1,0)z d?(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)?x d?,(0,yp?,0)?y ?d?,(0,0,zp?)?z d?)=(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文件是一個FbxSceneFbxSceneFbxNode以樹狀層級結構組成。

一個場景由許多元素組成,包含:meshes, lights, cameras, skeletons, NURBS等,這些元素繼承FbxNodeAttributeFbxNode是一個或多個場景元素的容器。

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,比如FbxSceneFbxNodeFbxImporter, 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文檔
  • 官方案列

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67774.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67774.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67774.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【單層神經網絡】基于MXNet庫簡化實現線性回歸

寫在前面 同最開始的兩篇文章 完整程序及注釋 導入使用的庫# 基本 from mxnet import autograd, nd, gluon # 模型、網絡 from mxnet.gluon import nn from mxnet import init # 學習 from mxnet.gluon import loss as gloss # 數據集 from mxnet.gluon…

【爬蟲】JS逆向解決某藥的商品價格加密

??????????歡迎來到我的博客?????????? ??作者:秋無之地 ??簡介:CSDN爬蟲、后端、大數據領域創作者。目前從事python爬蟲、后端和大數據等相關工作,主要擅長領域有:爬蟲、后端、大數據開發、數據分析等。 ??歡迎小伙伴們點贊????、收藏??、…

OpenAI開源戰略反思:中國力量推動AI產業變革

在周五的Reddit問答會上&#xff0c;OpenAI首席執行官Sam Altman罕見承認公司正面臨來自中國科技企業的強勁挑戰。這位向來強硬的硅谷領軍者坦言&#xff0c;以深度求索&#xff08;DeepSeek&#xff09;為代表的中國AI公司正在改寫行業游戲規則。 這場歷時三小時的對話揭示了…

一文講解HashMap線程安全相關問題(上)

HashMap不是線程安全的&#xff0c;主要有以下幾個問題&#xff1a; ①、多線程下擴容會死循環。JDK1.7 中的 HashMap 使用的是頭插法插入元素&#xff0c;在多線程的環境下&#xff0c;擴容的時候就有可能導致出現環形鏈表&#xff0c;造成死循環。 JDK 8 時已經修復了這個問…

android java系統彈窗的基礎模板

1、資源文件 app\src\main\res\layout下增加custom_pop_layout.xml 定義彈窗的控件資源。 <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/…

python學習——常用的內置函數匯總

文章目錄 類型轉換函數數學函數常用的迭代器操作函數常用的其他內置函數 類型轉換函數 數學函數 常用的迭代器操作函數 實操&#xff1a; from cv2.gapi import descr_oflst [55, 42, 37, 2, 66, 23, 18, 99]# (1) 排序操作 asc_lst sorted(lst) # 升序 desc_lst sorted(l…

《解鎖AI黑科技:數據分類聚類與可視化》

在當今數字化時代&#xff0c;數據如潮水般涌來&#xff0c;如何從海量數據中提取有價值的信息&#xff0c;成為了眾多領域面臨的關鍵挑戰。人工智能&#xff08;AI&#xff09;技術的崛起&#xff0c;為解決這一難題提供了強大的工具。其中&#xff0c;能夠實現數據分類與聚類…

MySQL數據庫環境搭建

下載MySQL 官網&#xff1a;https://downloads.mysql.com/archives/installer/ 下載社區版就行了。 安裝流程 看b站大佬的視頻吧&#xff1a;https://www.bilibili.com/video/BV12q4y1477i/?spm_id_from333.337.search-card.all.click&vd_source37dfd298d2133f3e1f3e3c…

AI學習指南HuggingFace篇-Tokenizers 與文本處理

一、引言 在自然語言處理(NLP)中,文本數據的預處理是至關重要的一步。分詞器(Tokenizers)是將文本分割成單詞、短語或其他單元的工具,是文本處理的基礎。Hugging Face的Tokenizers庫提供了高效且靈活的分詞工具,支持多種預訓練模型的分詞需求。本文將深入講解Tokenizer…

如何用微信小程序寫春聯

? 生活沒有模板,只需心燈一盞。 如果笑能讓你釋然,那就開懷一笑;如果哭能讓你減壓,那就讓淚水流下來。如果沉默是金,那就不用解釋;如果放下能更好地前行,就別再扛著。 一、引入 Vant UI 1、通過 npm 安裝 npm i @vant/weapp -S --production?? 2、修改 app.json …

[SAP ABAP] 靜態斷點的使用

在 ABAP 編程環境中&#xff0c;靜態斷點通過關鍵字BREAK-POINT實現&#xff0c;當程序執行到這一語句時&#xff0c;會觸發調試器中斷程序的運行&#xff0c;允許開發人員檢查當前狀態并逐步跟蹤后續代碼邏輯 通常情況下&#xff0c;在代碼的關鍵位置插入靜態斷點可以幫助開發…

96,【4】 buuctf web [BJDCTF2020]EzPHP

進入靶場 查看源代碼 GFXEIM3YFZYGQ4A 一看就是編碼后的 1nD3x.php 訪問 得到源代碼 <?php // 高亮顯示當前 PHP 文件的源代碼&#xff0c;用于調試或展示代碼結構 highlight_file(__FILE__); // 關閉所有 PHP 錯誤報告&#xff0c;防止錯誤信息泄露可能的安全漏洞 erro…

基于深度學習的輸電線路缺陷檢測算法研究(論文+源碼)

輸電線路關鍵部件的缺陷檢測對于電網安全運行至關重要&#xff0c;傳統方法存在效率低、準確性不高等問題。本研究探討了利用深度學習技術進行輸電線路關鍵組件的缺陷檢測&#xff0c;目的是提升檢測的效率與準確度。選用了YOLOv8模型作為基礎&#xff0c;并通過加入CA注意力機…

3、從langchain到rag

文章目錄 本文介紹向量和向量數據庫向量向量數據庫 索引開始動手實現rag加載文檔數據并建立索引將向量存放到向量數據庫中檢索生成構成一條鏈 本文介紹 從本節開始&#xff0c;有了上一節的langchain基礎學習&#xff0c;接下來使用langchain實現一個rag應用&#xff0c;并稍微…

DeepSeek-R1大模型本地化部署

前言 Ollama作為一個輕量級、易上手的工具&#xff0c;可以幫助你在自己的電腦上快速部署和運行大型語言模型&#xff0c;無需依賴云端服務。通過加載各種開源模型&#xff0c;比如LLaMA、GPT-J等&#xff0c;并通過簡單的命令行操作進行模型推理和測試。 此小結主要介紹使用…

【小白學AI系列】NLP 核心知識點(五)Transformer介紹

Transformer Transformer 是一種基于自注意力機制&#xff08;Self-Attention Mechanism&#xff09;的深度學習模型&#xff0c;首次由 Vaswani 等人于 2017 年在論文《Attention is All You Need》中提出。與 RNN 和 LSTM 不同&#xff0c;Transformer 不需要依靠序列順序進…

【高級篇 / IPv6】(7.6) ? 03. 寬帶IPv6 - ADSL撥號寬帶上網配置 ? FortiGate 防火墻

【簡介】大部分ADSL撥號寬帶都支持IPv6&#xff0c;這里以ADSL撥號寬帶為例&#xff0c;演示在FortiGate防火墻上的配置方法。 準備工作 同上篇文章一樣&#xff0c;為了兼顧不熟悉FortiGate防火墻的朋友&#xff0c;我們從基礎操作進行演示&#xff0c;熟練的朋友可以跳過這一…

【Elasticsearch】_all 查詢

在 Elasticsearch 中&#xff0c;_all 查詢是一種特殊的查詢方式&#xff0c;用于在多個索引或數據流中執行搜索操作&#xff0c;而無需顯式指定每個目標索引或數據流的名稱。以下是關于 _all 查詢的詳細說明&#xff1a; _all 查詢概述 用途&#xff1a;_all 查詢允許您在多個…

Linux第104步_基于AP3216C之I2C實驗

Linux之I2C實驗是在AP3216C的基礎上實現的&#xff0c;進一步熟悉修改設備樹和編譯設備樹&#xff0c;以及學習如何編寫I2C驅動和APP測試程序。 1、AP3216C的原理圖 AP3216C集成了一個光強傳感器ALS&#xff0c;一個接近傳感器PS和一個紅外LED&#xff0c;為三合一的環境傳感…

基于單片機的盲人智能水杯系統(論文+源碼)

1 總體方案設計 本次基于單片機的盲人智能水杯設計&#xff0c;采用的是DS18B20實現杯中水溫的檢測&#xff0c;采用HX711及應力片實現杯中水里的檢測&#xff0c;采用DS1302實現時鐘計時功能&#xff0c;采用TTS語音模塊實現語音播報的功能&#xff0c;并結合STC89C52單片機作…