대표적인 3D Object 표현방식
잘 알려진 Format으로 순수한 FBX를 쓸 경우 외부의 제 3자가 게임의 Resource에 쉽게 접근할 수 있음
또한, Game에 필요없는 부가적인 정보도 가지고 있을 수 있어서 Game이나 Engine에 Import시 최적화 작업 필요
Custom Exporter, Importer를 이용하여 최적화 진행
Exporter를 통해 3D Object를 출력할 경우 Viewer를 추가로 제작하여 외부에서 쉽게 확인할 수 있도록 제작
void Load()
{
// FbxManager : System같은 역할
m_pFbxManager = FbxManager::Create();
// FbxImporter : 파일을 읽어오는 역할
m_pFbxImporter = FbxImporter::Create(m_pFbxManager, "");
// FbxScene : 3D OBject를 구성하는 1개의 Scene.
// Scene에 있는 모든 정보를 읽어오면 3D Max의 화면을 구현할 수 있음
// Tree Graph의 구조를 가지고 있으며, Tree의 Node마다 Texture, Vertex, Normal등 component 정보가 저장되어 있음
m_pFbxScene = FbxScene::Create(m_pFbxManager, "");
m_pFbxImporter->Initialize(filename.c_str());
m_pFbxImporter->Import(m_pFbxScene);
// FbxNode : Tree를 구성하는 Node
m_pRootNode = m_pFbxScene->GetRootNode();
PreProcess(m_pRootNode);
for (auto mesh : m_pFbxMeshList)
{
ParseMesh(mesh);
}
}
// Tree 순회
void TFbxLoader::PreProcess(FbxNode* pFbxNode)
{
if (pFbxNode == nullptr) return;
// Mesh정보를 가지고 있는 Node 검색
// Mesh = Rendering가능한 정보
FbxMesh* pFbxMesh = pFbxNode->GetMesh();
if (pFbxMesh)
{
m_pFbxMeshList.push_back(pFbxMesh);
}
int iNumChild = pFbxNode->GetChildCount();
for( int iChild=0; iChild < iNumChild; iChild++)
{
FbxNode* pChild = pFbxNode->GetChild(iChild);
PreProcess(pChild);
}
}
// Mesh 정보를 내 Engine에 맞도록 수정
void TFbxLoader::ParseMesh(FbxMesh* pFbxMesh)
{
TFbxObject* pObject = new TFbxObject;
FbxLayerElementUV* VertexUVSet=nullptr;
// Layer 개념
// 1개의 Mesh는 다수의 Layer로 이루어져있을 수 있음
FbxLayer* pFbxLayer = pFbxMesh->GetLayer(0);
if (pFbxLayer->GetUVs() != nullptr)
{
// Layer 단위로 정보를 읽어와야 한다
VertexUVSet=pFbxLayer->GetUVs();
}
int iNumPolyCount = pFbxMesh->GetPolygonCount();
int iNumFace = 0;
// 제어점. 정점 위치 원본인듯
FbxVector4* pVertexPositions = pFbxMesh->GetControlPoints();
for (int iPoly = 0; iPoly < iNumPolyCount; iPoly++)
{
int iPolySize = pFbxMesh->GetPolygonSize(iPoly);
// 3D Object는 1개의 폴리곤을 이루고있는 정점의 개수가 다를 수 있음
// 3 정점 -> 1폴리곤( 삼각형)
// 4 정점 -> 1폴리곤( 쿼드 )
iNumFace = iPolySize - 2;
for (int iFace = 0; iFace < iNumFace; iFace++)
{
int iCornerIndex[3];
// FBX와 Direct3D는 사용하는 좌표계가 다름
iCornerIndex[0] = pFbxMesh->GetPolygonVertex(iPoly, 0);
// 4개 정점 폴리곤도 정상적으로 작동하는가?
iCornerIndex[1] = pFbxMesh->GetPolygonVertex(iPoly, iFace+2);
iCornerIndex[2] = pFbxMesh->GetPolygonVertex(iPoly, iFace+1);
for (int iIndex = 0; iIndex < 3; iIndex++)
{
int vertexID = iCornerIndex[iIndex];
FbxVector4 v = pVertexPositions[vertexID];
SimpleVertex tVertex;
// FBX와 Direct3D는 사용하는 좌표계가 다름
tVertex.p.x = v.mData[0];
tVertex.p.y = v.mData[2];
tVertex.p.z = v.mData[1];
tVertex.c = { 1,1,1,1 };
FbxVector2 t = ReadTextureCoord(pFbxMesh, VertexUVSet);
tVertex.t.x = t.mData[0];
tVertex.t.y = 1.0f-t.mData[1];
pObject->m_VertexList.push_back(tVertex);
}
}
}
m_pDrawObjList.push_back(pObject);
}
FBX 파일은 오른손 Y-up 방식을 사용하기 때문에 왼손 Z-up을 사용하는 현 Engine에서는 읽어올 때 좌표축에 변환이 필요하다
또한 FBX의 UV좌표는 좌하단을 원점으로 잡는다
1개의 3D Object는 다수의 Mesh와 다수의 Texture로 이루어져 있을 수 있고 1개의 Mesh역시 다수의 Texture를 통해 출력될 수 있음
1개의 Mesh가 다수의 Texture를 1개씩 여러번 출력하면 Multi Pass Rendering
다수의 Texture를 합쳐서 1번 Rendering하면 Multi Texture Rendering
MD : MultiThreadDLL
MT : MultiThread
DLL 여부에 따른 차이인 듯
뒤에 소문자 d는 debug유무
재배포 패키지가 무엇일까?
Device Shader에서 처리되는 정보를 확인할 수 있음
마무리 3문장이 상당히 인상깊음