基于FBX SDK的FBX模型解析与加载 -(一)
http://blog.csdn.net/bugrunner/article/details/7210511
1. 简介
FBX是Autodesk的一个用于跨平台的免费三维数据交换的格式(最早不是由Autodesk开发,但后来被其收购),目前被 众多的标准建模软件所支持,在游戏开发领域也常用来作为各种建模工具的标准导出格式。Autodesk提供了基于C++(还有Python)的SDK来实现对FBX格式的各种读写、修改以及转换等操作,之所以如此是因为FBX的格式不是公开的,这也是FBX的诟病之一。与FBX相对的则是格式开源的Collada,它的应用也很广泛。总体来说这两种格式还是各有优劣不相上下,关于两种格式在游戏开发中使用的对比与讨论也比较多,可见GameDev中的帖子:http://www.gamedev.net/topic/467753-collada-vs-autodesk-fbx , 这里就不再论述了。大多数情况下我们是需要解析模型在程序中渲染使用,因此这里主要讨论使用FBX SDK来对FBX模型进行解析与加载(主要包括几何网格、材质、Light与Camera、Skeleton动画等),而对于在建模工具中可能涉及到的FBX写出等则没有涉及。
2. FBX SDK的配置
首先,在使用前需要下载安装FBX的SDK,可以从Autodesk的网站上进行获得最新的版本http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=7478532(需要填些基本信息注册下)。安装之后在VS里边的配置就跟D3D类似。其中的Samples基本上涵盖了FBX相关的应用,可以在使用之前好好研究一下。最新的SDK版本(2012版)与之前的版本会在细节上有所不同(有些较大的改动是实现某些功能 的API接口的修改,具体细节可以用2012的Programmer's guide中找到),而且支持最新的FBX格式,因此最好就直接用最新的版本。
3. FBX模型的组织结构
FBX是以scene graph的结构来存储模型的所有信息(也可以认为是一个多叉树),类似于OSG中的组织方式,这一点可以从SDK所带的Sample里边很清楚的看出来。一个较为典型的模型的组织结构与下图所示:
整个Scene是从一个空属性的根结点开始,其中每个结点均是一个KFbxNode的对象,所有对象之间的关联均是双向的,比如从子结点可以索引到父结点,从父结点也可以索引到子结点;从单个结点可以索引到整个Scene,从Scene也可以索引到该结点。每个结点都会有一个标记属性的Enum值,比如eMesh、eLight、eCamera或eSkeleton等,分别用来标记当前结点是Mesh、Light、Camera或Skeleton。在整个结构的遍历过程中可以通过判断不同的结点属性而进行不同的处理操作。
在进行使用SDK进行FBX的处理操作之前需要先初始化两个必须的FBX对象:KFbxSdkManager和KFbxScene。前者用来对所有的FBX对象进行内在管理,所有使用SDK加载的资源均在此对象的管控之下,最终的资源释放也由其来完成。有了内存管理器之后再在其上创建一个相关的KFbxScene对象之后即可以进行模型的加截与处理了。KFbxScene其实相当于Manager提供的整个场景对象的一个入口。两个对象的初始化与配置代码如下所述:
初始化SDKManager
- bool FBXImporter::Initialize()
- {
- // Create the FBX SDK Manager, destroy the old manager at first
- if(mpFBXSDKManager)
- {
- mpFBXSDKManager->Destroy();
- }
- mpFBXSDKManager = KFbxSdkManager::Create();
- if(mpFBXSDKManager == NULL)
- {
- return false;
- }
- // Create an IOSettings object
- KFbxIOSettings* ios = KFbxIOSettings::Create(mpFBXSDKManager , IOSROOT);
- mpFBXSDKManager->SetIOSettings(ios);
- // Load plug-ins from the executable directory
- KString lExtension = "dll";
- KString lPath = KFbxGetApplicationDirectory();
- mpFBXSDKManager->LoadPluginsDirectory(lPath.Buffer() , lExtension.Buffer());
- // Create the entity that hold the whole Scene
- mpFBXSDKScene = KFbxScene::Create(mpFBXSDKManager , "");
- return true;
- }
FbxScene的初始化
- bool FBXImporter::LoadScene(const char* pSeneName)
- {
- if(mpFBXSDKManager == NULL)
- {
- return false;
- }
- // Get the file version number generate by the FBX SDK.
- KFbxSdkManager::GetFileFormatVersion(mSDKVersion.mMajor , mSDKVersion.mMinor , mSDKVersion.mRevision);
- // Create an importer.
- KFbxImporter* pKFBXImporter = KFbxImporter::Create(mpFBXSDKManager , "");
- // Initialize the importer by providing a filename
- FBXFileVersion fileVersion;
- bool importStatus = pKFBXImporter->Initialize(fileName , -1 , mpFBXSDKManager->GetIOSettings());
- lImporter->GetFileVersion(fileVersion.mMajor , fileVersion.mMinor , fileVersion.mRevision);
- if(!importStatus)
- {
- return false;
- }
- // Import the scene
- mpFBXScene->Clear();
- importStatus = pKFBXImporter->Import(m_mpFBXScene);
- // Destroy the importer.
- pKFBXImporter->Destroy();
- return importStatus;
- }
在完成了对KFbxScene的初始化操作之后即可以从其中得到整个模型的所有信息。由于FBX的是采用了类似于树的结构来进行存储,因而就很容易使用类似于树的递归方法来遍历其中的每个结点,并根据结点的属性选择合适的处理操作,下述代码就完成了从根结点开始的全局遍历:
- void ProcessNode(KFbxNode* pNode)
- {
- KFbxNodeAttribute::EAttributeType attributeType;