들어가며
Fbx파일은 Autodesk에서 개발한 파일 형식으로 3D모델, 애니메이션, Material, 카메라, 조명등 다양한 정보를 저장할 수 있는 파일입니다.
Autodesk에서는 Fbx파일을 다루는 Fbx sdk라는 c++ 라이브러리를 제공합니다.
Fbx sdk를 사용하면 Fbx파일을 읽고, 쓰고, 수정할 수 있습니다.
최근 튜토리얼에서 텍스트 파일로 이루어진 정점 데이터를 가져오는 기능을 만든 김에, 자주쓰는 fbx파일에서 정점 데이터를 추출해 볼 생각입니다.
이번 포스팅에서는 정점의 위치만 가져와, 단색으로 렌더링 하겠습니다.
(라이브러리 적용은 건너뜁니다.)
FbxTool.h
이 FbxTool의 흐름은 간단합니다.
FindMesh로 Mesh데이터를 찾아 SaveVertexData로 Mesh의 데이터를 분석하여 저장합니다.
#include <fbxsdk.h>
class FbxTool
{
public:
FbxTool();
~FbxTool() = default;
bool Initialize();
// 파일을 불러옵니다.
bool Load(const char* fileName);
// VertexBuffer를 만들때 호출 됩니다.
XMFLOAT3* GetVertexPos() { return m_pos; };
unsigned int* GetVertexIdx() { return m_idx; };
private:
// 재귀형식으로 트리형 노드를 탐색해 Mesh데이터를 찾아 저장
bool FindMesh(FbxNode* node);
// 찾은 Mesh로부터 정점 데이터 저장
bool SaveVertexData();
void Shotdown();
private:
FbxManager* m_manager;
FbxImporter* m_importer;
FbxScene* m_scene;
FbxMesh* m_mesh;
// 가져온 Fbx자료형을 DX자료형으로 변환하여 저장해둘 변수
XMFLOAT3* m_pos;
unsigned int* m_idx;
};
FbxTool.h
1. Intialize
Fbx sdk는 싱글톤패턴으로 개체가 관리되기에, 프로그램당 한개의 개체만 이용됩니다.
bool FbxTool::Initialize()
{
// 관리자 객체 생성
m_manager = FbxManager::Create();
Fbx 파일의 내용을 가져오려면 FbxIoSettings와 FbxScene, FbxImporter개체를 생성해야 합니다.
앞서 말했듯이 Fbx에는 여러 데이터를 가지고 있습니다.(3D모델, Material, 카메라, ...) 그래서 가져오거나, 내보내고 싶은 데이터를 설정할 수 있는데 이 설정을 FbxIoSettings으로 지정할 수 있습니다.
이번 시간은 설정필요가 없어 기본 값을 사용하겠습니다.
// 가져올 씬의 요소(camera, light, mesh, texture, ...) 세팅
FbxIOSettings* ios = FbxIOSettings::Create(m_manager, IOSROOT);
m_manager->SetIOSettings(ios);
FbxScene은 Fbx파일의 전체 3D 장면을 나타내며 이 장면 속에 속한 카메라, 메시, 텍스쳐 모든 데이터를 포함합니다.
Fbx파일의 본체 격인것 같습니다.
m_scene = FbxScene::Create(m_manager, "My Scene");
다음으로 FbxImporter의 생성 차례인데, FbxImporter는 Fbx의 Scene데이터를 FbxScene으로 가져와주는 개체입니다.
일단 생성만 해둡니다
m_importer = FbxImporter::Create(m_manager, "");
return true;
}
2. Load
Load함수의 목적은 Fbx파일명을 받아와 해당 파일에서 정점 데이터를 찾아 저장합니다.
파일을 찾아 해당 파일에서 씬 데이터를 m_scene에 저장합니다.
Fbx파일마다 기본 폴리곤 형태가 다릅니다.(삼각형 or 사각형) 제가 사용하는 DX에서는 삼각형을 통해 렌더링이 이루어지기 때문에 삼각형으로 만들어줍니다.
그리고 메시 데이터를 저장한는데 매개변수에 GetRootNode라는 매개변수가 있습니다.
Scene은 Node라는 트리 구조로 구성되어 있습니다. 즉, 이 Node 하나하나가 Scene의 각 개별 데이터를 의미합니다.(메쉬 노드, 조명 노드, 카메라 노드)
우린는 최 상단 노드인 Root Node로부터 메시 데이터를 찾아 저장할 것 입니다.
bool FbxTool::Load(const char* fileName)
{
bool ret;
// 1. 임포터 초기화
ret = m_importer->Initialize(fileName, -1, m_manager->GetIOSettings());
m_importer->Import(m_scene);
// 임포트 후 임포터는 해제하여 메모리 사용량을 줄입니다.
m_importer->Destroy();
// 2. 삼각형화할 수 있는 노드를 삼각형화 시키기
FbxGeometryConverter converter(m_manager);
converter.Triangulate(m_scene, true);
// 3. 메시 데이터 저장
ret = FindMesh(m_scene->GetRootNode());
if (ret == false) return false;
// 4. 메시의 정점 데이터(위치, 인덱스) 저장
SaveVertexData();
return true;
}
3. FindMesh
트리 구조를 메시 노드가 나올때까지 재귀형으로 탐색합니다. 노드가 가진 데이터는 FbxNodeAttribute로 알 수 있습니다.
발견했다면 m_mesh에 저장합니다 .
bool FbxTool::FindMesh(FbxNode* node)
{
// 찾은 노드가 메시타입이면 메시에 저장 후 반환합니다.
FbxNodeAttribute* attribute = node->GetNodeAttribute();
if (node->GetNodeAttribute() != nullptr)
{
if (node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eMesh)
{
m_mesh = node->GetMesh();
return true;
}
}
// 노드의 자식의 수만큼 반복
int childCnt = node->GetChildCount();
for (int i = 0; i < childCnt; ++i)
{
if (FindMesh(node->GetChild(i)) == true)
{
return true;
}
}
// 다 순회해도 메시 데이터가 없으면 false반환합니다.
return false;
}
4. SaveVertexData
찾은 메시 노드로 정점 데이터중 위치 데이터를 추출하여 저장합니다.
특별히 어려운 부분은 없으나 중간에 위치 데이터를 0, 1, 2순이 아니라 0, 2, 1순으로 된걸 볼 수 있는데, 그 이유는 backface의 방향 때문입니다.
Dx11같은 경우 정점이 시계 방향으로 그려진 면이 front, 반대 방향으로 그려진 면이 back 입니다
반면에, Fbx sdk가 사용하는 면은 그 반대입니다.
그렇기 때문에 Fbx sdk에서 Dx11으로 정점 데이터를 가져오는 순은 0, 2, 1순으로 가져와야 합니다.
이렇게 두 번째, 세 번째 만 바꿔주어도 그리는 방향을 반대로 만들수 있습니다.
bool FbxTool::SaveVertexData()
{
// 정점의 개수
int vertexCnt = m_mesh->GetControlPointsCount();
// 정점 데이터 생성
m_pos = new XMFLOAT3[vertexCnt];
// 정점배열의 포인터 얻기
FbxVector4* position = m_mesh->GetControlPoints();
// 정점 위치 구하여 저장
for (int i = 0; i < vertexCnt; ++i)
{
float x = static_cast<float>(position[i][0]);
float y = static_cast<float>(position[i][2]);
float z = static_cast<float>(position[i][1]);
m_pos[i] = XMFLOAT3(x, y, z);
}
ModelClass.h
VertexBuffer를 생성하는 클래스에 FbxTool 포인터를 생성합니다.
class ModelClass
{
// ....
private:
FbxTool* m_fbx;
}
ModelClass.cpp
Vertex Buffer를 만들때 이런식으로 사용합니다.
///////////////////////////////////////////////////////////////////////////////
// Vertex Buffer 생성
{
m_vertexCount = _msize(m_fbx->GetVertexPos()) / sizeof(XMFLOAT3);
m_vertices = new VertexColor[m_vertexCount];
// Load the vertex array and index array with data.
for (int i = 0; i < m_vertexCount; i++)
{
m_vertices[i].position = m_fbx->GetVertexPos()[i];
m_vertices[i].color = XMFLOAT4(1, 0, 0, 1);
}
Index Buffer 같은 경우는 바로 받아오기만 하면 됩니다.
///////////////////////////////////////////////////////////////////////////////
// Index Buffer 생성
{
m_indices = m_fbx->GetVertexIdx();
m_indexCount = _msize(m_indices) / sizeof(unsigned int);
요약
Fbx sdk를 사용하여 정점의 위치 데이터를 가져와 단색으로 렌더링 했습니다.
다음은 다른 정점 데이터까지 불러와 렌더링 할 예정입니다.