配置FBX_SDK
1.介绍
FBX格式是现在最主流的用于游戏的3D模型格式,要使用DirectX12绘制模型,当然需要先用这个库来解析模型数据。它的版权协议如下:
不是开源的并且是非商用的,假设你完成了一个引擎到时候还需要联系他们讨论版权的问题,不过前提是你想盈利。出于研究技术的目的,所以不用考虑这么多,大不了到时候再换一个格式即可。
先在官网下载对应的安装文件:
https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2020-2
我现在用上了vs2019,所以下载的是2020.2 vs2019的版本。
2.复制文件
安装之后就有include和lib文件了,同时也要拷贝上License文件到我们的项目里。如下:
所以附加包含目录如下:
$(SolutionDir)\FBX_SDK\include
而库目录如下:
$(SolutionDir)FBX_SDK\lib\vs2019\$(PlatformName)\$(Configuration)
由于$(PlatformName)返回的平台名是Win32,而不是x86,所以需要改一下文件夹的名字,如下:
最后附加依赖项添加上:
libfbxsdk.lib
官网的教程说还需要加上预处理器定义FBXSDK_SHARED,因为我们是以动态链接的方式使用的。所以dll文件还需要拷贝一下,是在exe目录:
3.准备一个茶壶
用3ds max创建一个茶壶并导出fbx格式的文件,当然也可以用网上找别人的fbx文件,不过我想从最简单的一步一步做起这样不容易出错,我用的3ds max 2021。
导出fbx拷贝到对应的资源目录(这里还包含上一章的shader文件,和生成的log文件):
4.示例代码
我先快速的测试官方给出的示例代码,这样更方便理解并测试有没有配置成功:
DNDFBX.h
/**
* @file DNDFBX.h
* @brief 用于封装FBX SDK解析FBX文件
*
*
* @version 1.0
* @author lveyou
* @date 21_03_27
* 修订说明: 1
*/
#pragma once
#include <fbxsdk.h>
#include "DNDDebug.h"
namespace DND
{
/**
* Print the required number of tabs.
*/
void PrintTabs();
/**
* Return a string-based representation based on the attribute type.
*/
FbxString GetAttributeTypeName(FbxNodeAttribute::EType type);
/**
* Print an attribute.
*/
void PrintAttribute(FbxNodeAttribute* pAttribute);
/**
* Print a node, its attributes, and all its children recursively.
*/
void PrintNode(FbxNode* pNode);
class DNDFBX
{
public:
void _init();
private:
FbxManager* _lSdkManager;
};
extern DNDFBX g_fbx;
}
DNDFBX.cpp
#include "DNDFBX.h"
#include "DNDString.h"
namespace DND
{
DNDFBX g_fbx;
/* Tab character ("\t") counter */
int numTabs = 0;
void PrintTabs()
{
for (int i = 0; i < numTabs; i++)
{
g_debug.Write(L"\t");
}
}
FbxString GetAttributeTypeName(FbxNodeAttribute::EType type)
{
switch (type) {
case FbxNodeAttribute::eUnknown: return "unidentified";
case FbxNodeAttribute::eNull: return "null";
case FbxNodeAttribute::eMarker: return "marker";
case FbxNodeAttribute::eSkeleton: return "skeleton";
case FbxNodeAttribute::eMesh: return "mesh";
case FbxNodeAttribute::eNurbs: return "nurbs";
case FbxNodeAttribute::ePatch: return "patch";
case FbxNodeAttribute::eCamera: return "camera";
case FbxNodeAttribute::eCameraStereo: return "stereo";
case FbxNodeAttribute::eCameraSwitcher: return "camera switcher";
case FbxNodeAttribute::eLight: return "light";
case FbxNodeAttribute::eOpticalReference: return "optical reference";
case FbxNodeAttribute::eOpticalMarker: return "marker";
case FbxNodeAttribute::eNurbsCurve: return "nurbs curve";
case FbxNodeAttribute::eTrimNurbsSurface: return "trim nurbs surface";
case FbxNodeAttribute::eBoundary: return "boundary";
case FbxNodeAttribute::eNurbsSurface: return "nurbs surface";
case FbxNodeAttribute::eShape: return "shape";
case FbxNodeAttribute::eLODGroup: return "lodgroup";
case FbxNodeAttribute::eSubDiv: return "subdiv";
default: return "unknown";
}
}
void PrintAttribute(FbxNodeAttribute* pAttribute)
{
if (!pAttribute) return;
FbxString typeName = GetAttributeTypeName(pAttribute->GetAttributeType());
FbxString attrName = pAttribute->GetName();
PrintTabs();
// Note: to retrieve the character array of a FbxString, use its Buffer() method.
//printf(, );
//
g_debug.Line(String::Format(L"<attribute type='%s' name='%s'/>", String::Mbtowc(typeName.Buffer()).c_str(), String::Mbtowc(attrName.Buffer()).c_str()));
}
void PrintNode(FbxNode* pNode)
{
PrintTabs();
const char* nodeName = pNode->GetName();
FbxDouble3 translation = pNode->LclTranslation.Get();
FbxDouble3 rotation = pNode->LclRotation.Get();
FbxDouble3 scaling = pNode->LclScaling.Get();
// Print the contents of the node.
g_debug.Line(String::Format(L"<node name='%s' translation='(%f, %f, %f)' rotation='(%f, %f, %f)' scaling='(%f, %f, %f)'>",
String::Mbtowc(nodeName).c_str(),
translation[0], translation[1], translation[2],
rotation[0], rotation[1], rotation[2],
scaling[0], scaling[1], scaling[2]
));
numTabs++;
// 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));
numTabs--;
PrintTabs();
g_debug.Line(L"</node>");
}
void DNDFBX::_init()
{
// Change the following filename to a suitable filename value.
const char* lFilename = "teapot.fbx";
// Initialize the SDK manager. This object handles all our memory management.
_lSdkManager = FbxManager::Create();
// Create the IO settings object.
FbxIOSettings* ios = FbxIOSettings::Create(_lSdkManager, IOSROOT);
_lSdkManager->SetIOSettings(ios);
// Create an importer using the SDK manager.
FbxImporter* lImporter = FbxImporter::Create(_lSdkManager, "");
// Use the first argument as the filename for the importer.
if (!lImporter->Initialize(lFilename, -1, _lSdkManager->GetIOSettings())) {
g_debug.Line(L"Call to FbxImporter::Initialize() failed.");
g_debug.Line(String::Mbtowc(
(string("Error returned: ") + string(lImporter->GetStatus().GetErrorString())).c_str()
, false));
auto v = lImporter->GetStatus().GetCode();
exit(-1);
}
// Create a new scene so that it can be populated by the imported file.
FbxScene* lScene = FbxScene::Create(_lSdkManager, "myScene");
// Import the contents of the file into the scene.
lImporter->Import(lScene);
// The file is imported; so get rid of the importer.
lImporter->Destroy();
// Print the nodes of the scene and their attributes recursively.
// Note that we are not printing the root node because it should
// not contain any attributes.
FbxNode* lRootNode = lScene->GetRootNode();
if (lRootNode) {
for (int i = 0; i < lRootNode->GetChildCount(); i++)
PrintNode(lRootNode->GetChild(i));
}
// Destroy the SDK manager and all the other objects it was handling.
_lSdkManager->Destroy();
return;
}
}
前面几个函数均是读取数据并打印,而_init函数是最基础的使用方式,其中lImporter->Initialize总是失败,我一直以为是我导出的fbx文件设置不对,实际上只是文件放错位置了,没有读取到文件。但是它错误提示竟然是Unexpected file type,很坑人啊。最后成功的打印出了一些数据。
下一步我的目标就是用DirectX12绘制出这个茶壶!