오늘의 목표 FBX 파일을 읽어와 화면에 띄워보자!
지금까지 표현한 Object들은 하드코딩으로 직접 모양을 만든 Object들이었다.
하지만 실제 실무에서는 그런 경우는 거의 드믈고 미리 만들어진 Object를 읽어와 사용한다.
FBX 파일은 Material, Mesh, Animation, Bone, Skin 등 온갖 모델에 대한 정보가 다 들어가 있다.
FBX 라이브러리를 통해 FBX파일을 읽어온다.
void FBXLoader::LoadFbx(const wstring& path)
{
// 파일 데이터 로드
Import(path);
// Animation
//LoadBones(_scene->GetRootNode());
//LoadAnimationInfo();
// 로드된 데이터 파싱 (Mesh/Material/Skin)
ParseNode(_scene->GetRootNode());
// 우리 구조에 맞게 Texture / Material 생성
CreateTextures();
CreateMaterials();
}
void FBXLoader::Import(const wstring& path)
{
// FBX SDK 관리자 객체 생성
_manager = FbxManager::Create();
// IOSettings 객체 생성 및 설정
FbxIOSettings* settings = FbxIOSettings::Create(_manager, IOSROOT);
_manager->SetIOSettings(settings);
// FbxImporter 객체 생성
_scene = FbxScene::Create(_manager, "");
// 나중에 Texture 경로 계산할 때 쓸 것
_resourceDirectory = fs::path(path).parent_path().wstring() + L"\\" + fs::path(path).filename().stem().wstring() + L".fbm";
_importer = FbxImporter::Create(_manager, "");
string strPath = ws2s(path);
_importer->Initialize(strPath.c_str(), -1, _manager->GetIOSettings());
_importer->Import(_scene);
_scene->GetGlobalSettings().SetAxisSystem(FbxAxisSystem::DirectX);
// 씬 내에서 삼각형화 할 수 있는 모든 노드를 삼각형화 시킨다.
FbxGeometryConverter geometryConverter(_manager);
geometryConverter.Triangulate(_scene, true);
_importer->Destroy();
}
void FBXLoader::ParseNode(FbxNode* node)
{
FbxNodeAttribute* attribute = node->GetNodeAttribute();
if (attribute)
{
switch (attribute->GetAttributeType())
{
case FbxNodeAttribute::eMesh:
LoadMesh(node->GetMesh());
break;
}
}
// Material 로드
const uint32 materialCount = node->GetMaterialCount();
for (uint32 i = 0; i < materialCount; ++i)
{
FbxSurfaceMaterial* surfaceMaterial = node->GetMaterial(i);
LoadMaterial(surfaceMaterial);
}
// Tree 구조 재귀 호출
const int32 childCount = node->GetChildCount();
for (int32 i = 0; i < childCount; ++i)
ParseNode(node->GetChild(i));
}
void FBXLoader::LoadMesh(FbxMesh* mesh)
{
_meshes.push_back(FbxMeshInfo());
FbxMeshInfo& meshInfo = _meshes.back();
meshInfo.name = s2ws(mesh->GetName());
const int32 vertexCount = mesh->GetControlPointsCount();
meshInfo.vertices.resize(vertexCount);
meshInfo.boneWeights.resize(vertexCount);
// Position
FbxVector4* controlPoints = mesh->GetControlPoints();
for (int32 i = 0; i < vertexCount; ++i)
{
meshInfo.vertices[i].pos.x = static_cast<float>(controlPoints[i].mData[0]);
meshInfo.vertices[i].pos.y = static_cast<float>(controlPoints[i].mData[2]);
meshInfo.vertices[i].pos.z = static_cast<float>(controlPoints[i].mData[1]);
}
const int32 materialCount = mesh->GetNode()->GetMaterialCount();
meshInfo.indices.resize(materialCount);
FbxGeometryElementMaterial* geometryElementMaterial = mesh->GetElementMaterial();
const int32 polygonSize = mesh->GetPolygonSize(0);
assert(polygonSize == 3);
uint32 arrIdx[3];
uint32 vertexCounter = 0; // 정점의 개수
const int32 triCount = mesh->GetPolygonCount(); // 메쉬의 삼각형 개수를 가져온다
for (int32 i = 0; i < triCount; i++) // 삼각형의 개수
{
for (int32 j = 0; j < 3; j++) // 삼각형은 세 개의 정점으로 구성
{
int32 controlPointIndex = mesh->GetPolygonVertex(i, j); // 제어점의 인덱스 추출
arrIdx[j] = controlPointIndex;
GetNormal(mesh, &meshInfo, controlPointIndex, vertexCounter);
GetTangent(mesh, &meshInfo, controlPointIndex, vertexCounter);
GetUV(mesh, &meshInfo, controlPointIndex, mesh->GetTextureUVIndex(i, j));
vertexCounter++;
}
const uint32 subsetIdx = geometryElementMaterial->GetIndexArray().GetAt(i);
meshInfo.indices[subsetIdx].push_back(arrIdx[0]);
meshInfo.indices[subsetIdx].push_back(arrIdx[2]);
meshInfo.indices[subsetIdx].push_back(arrIdx[1]);
}
// Animation
LoadAnimationData(mesh, &meshInfo);
}