[DirectX11] 16. FBX File로부터 모델 데이터 Import하기.



fbx파일을 로드해서 모델을 렌더링하려고 한다. fbx파일을 로드하려면 FBX SDK관련 셋팅이 필요하다. 관련 내용들은 FBX SDK Help | Getting Started | Autodesk 에서 참고하였다.



FBX SDK 다운로드 및 설치


FBX | Adaptable File Formats for 3D Animation Software | Autodesk 우선 이곳에서 SDK 다운로드를 한다. 다운로드 이후 설치까지 한다.



설치된 내용이다. 필요한 파일들을 복사해서 사용하기 때문에 복사 이후에는 uninstall을 통해서 설치를 해제해도 무방하다.



프로젝트에 필요한 파일들 복사


프로젝트에 필요한 내용은 다음과 같다.

  • include/*.*
  • lib/x64/debug/libfbxsdk.dll
  • lib/x64/debug/libfbxsdk.lib
  • lib/x64/debug/libfbxsdk-md.lib
  • lib/x64/debug/libfbxsdk-mt.lib
  • lib/x64/release/libfbxsdk.dll
  • lib/x64/release/libfbxsdk.lib
  • lib/x64/release/libfbxsdk-md.lib
  • lib/x64/release/libfbxsdk-mt.lib

include는 전체 복사가 필요하고, lib는 필요한 파일들만 복사하면 된다.



위와 같이 파일들을 프로젝트 폴더에 복사해주었다. 이 파일들을 github에 올리기 위해 Git LFS를 사용했다. 2.0gb용량 제한이 있는데 벌써 800메가를 사용해버렸다. fbx파일을 독자 포맷으로 컨버팅해서 사용해야할 듯 하고, 컨버터는 별도 프로젝트로 분리하는것을 심각하게 고민해봐야겠다.



프로젝트에서 사용전 설정



추가 포함 경로 추가




위와 같이 fbx sdkinclude경로를 추가해주었다. sdk안에서의 헤더파일들의 경로 참조 때문에 꼭 해주어야 한다.


lib 포함하기


C++
#include <fbxsdk.h>  
  
  
#ifdef _DEBUG  
  
    #pragma comment ( lib, "Fbx/debug/libfbxsdk.lib" )  
  
#else  
  
    #pragma comment ( lib, "Fbx/release/libfbxsdk.lib" )  
  
#endif

debug, release모드를 구분해서 lib파일을 포함하도록 추가해주었다.


dll 파일 복사 이벤트 추가




xcopy /Y /I "$(SolutionDir)\Engine\Extras\Fbx\debug\libfbxsdk.dll" "$(TargetDir)"를 빌드전 이벤트로 추가해주었다. 런타임 라이브러리 포함이 필요하다. 실행 파일 경로로 복사해주며 debug, release모드를 구분해서 원본 파일의 위치가 다르도록 따로 이벤트를 추가해주었다.

여기까지 하면 fbx파일을 로드하는 준비는 모두 마쳤다.



FBX 파일 로드하기


fbx파일을 로드하기 위해 필요한 인스턴스는 다음과 같다.

C++
FbxManager*    FbxManagerPtr    = nullptr;  
FbxIOSettings* FbxIOSettingsPtr = nullptr;  
FbxImporter*   FbxImporterPtr   = nullptr;  
FbxScene*      FbxScenePtr      = nullptr;

이 인스턴스들을 생성하고 초기화 하는 과정이 필요하다.

C++
FbxManagerPtr    = FbxManager::Create();
FbxIOSettingsPtr = FbxIOSettings::Create( FbxManagerPtr, IOSROOT );  
FbxImporterPtr   = FbxImporter::Create( FbxManagerPtr, "" ); 
FbxScenePtr      = FbxScene::Create( FbxManagerPtr, "" );

if ( !FbxManagerPtr || !FbxIOSettingsPtr || !FbxImporterPtr || !FbxScenePtr ) return;

FbxManagerPtr->SetIOSettings( FbxIOSettingsPtr );  
  
FbxImporterPtr->Initialize( FbxFilePath, -1, FbxIOSettingsPtr );
FbxImporterPtr->Import( FbxScenePtr );  
FbxImporterPtr->Destroy();

여기까지 하면 일단 fbx파일 로드까지 완료되었다. 이제부터 fbx파일을 내가 사용하기 위한 데이터로 컨버팅 하는 과정이 필요하다.



DirectX 기준 좌표계와 삼각형의 형태로 변환하기


fbx는 맥스나 마야등 저장한 프로그램에 따른 좌표계 기준으로 메쉬 데이터가 저장된다. 또한 삼각형이 아니라 사각형 등 Primitive또한 다양한 형태로 저장되어 있을 수 있다. 따라서 사용하는 목적에 맞게 컨버팅해서 데이터를 가지고 와야 한다.


좌표계 컨버팅


C++
FbxImporterPtr->Import( FbxScenePtr );  
  
FbxAxisSystem directXAxisSystem( FbxAxisSystem::eDirectX );  
directXAxisSystem.ConvertScene( FbxScenePtr );

위 코드처럼 Import이후 directX기준의 FbxAxisSystem을 가져와서 FbxScene을 변경해준다. 이 컨버팅이 끝나면 내부 데이터의 좌표값들을 directX기준으로 갖게 된다.



삼각형 형태로 변환하기


C++
FbxGeometryConverter fbxGeometryConverter( FbxManagerPtr );  
fbxGeometryConverter.Triangulate( FbxScenePtr, true );

위 코드는 FbxScene에서 Primitive의 기본 모양을 삼각형으로 변경해준다.




FBX Node 로드하기


fbx에서의 node는 트리 구조로 구성되어 있다. 그리고 각 노드는 transform 정보를 가지고 있다.




트리를 순회하며 Mesh데이터를 가지는 FBX Node 찾기


C++
void _LoadNode( FbxNode* Node )  
{  
    if ( !Node ) return;  
  
    for ( int i = 0; i < Node->GetChildCount(); ++i )  
    {  
        _LoadNode( Node->GetChild( i ) );  
    }  
      
    FbxNodeAttribute* nodeAttribute = Node->GetNodeAttribute();  
    if ( !nodeAttribute ) return;  
  
    switch ( nodeAttribute->GetAttributeType() )  
    {  
        case FbxNodeAttribute::eMesh: _LoadMeshNode( Node ); break;  
        default: break;  
    }  
}

위와 같이 재귀 탐색을 하며 NodeType에 따라 Node데이터의 임포트를 처리할 수 있다. 모든 타입의 노드를 처리하는 것은 너무 방대하므로 일단 모델 데이터만 가져올 수 있도록 FbxMesh타입의 Node만 처리하도록 한다. 나머지는 필요할 때 차차 하나씩 다룰 수 있도록 할 것이다.



FBX Node의 글로벌 변환 매트릭스 가져오기


C++
const FbxAMatrix& transformMatrix = Node->EvaluateGlobalTransform();

위 함수를 통해 해당 노드의 글로벌 변환 매트릭스를 가져올 수 있다.



FBX Node의 Vertex 정보 가져오기 ( Position )


C++
FbxVector4* fbxVertices = mesh->GetControlPoints();

for ( int polygonIndex = 0; polygonIndex < mesh->GetPolygonCount(); ++polygonIndex )  
{  
    for ( int t = 0; t < 3; ++t )  
    {  
        int index = mesh->GetPolygonVertex( polygonIndex, t );  
  
        const FbxVector4& fbxPosition = transformMatrix.MultT( fbxVertices[ index ] );  

        CRVector position;
        position.x = fbxPosition.mData[ 0 ];  
        position.y = fbxPosition.mData[ 1 ];  
        position.z = fbxPosition.mData[ 2 ];
  
        ++vertexIndex;  
    }  
}

위와같이 Primitive를 이루는 Vertex Position을 가져올 수 있다. 데이터에 미리 전역 변환을 적용해두었다. 물론 이 변환 매트릭스를 따로 저장해두었다가 이 모델을 렌더링할 때 변환 매트릭스를 통해 변환을 해주어도 된다. 실제 샘플 프로젝트는 그렇게 렌더링하고 있다. 하지만 나는 이 데이터를 내 포맷으로 전환 후 사용하고, 일단은 여러 노드로 나누어진 메쉬 데이터를 하나의 Primitve로 통합해서 임포트할 예정이므로 위와같이 VertexPosition데이터를 임포트할 때부터 전역 변환을 미리 해두는 것이다.


C++
for ( int polygonIndex = 0; polygonIndex < mesh->GetPolygonCount(); ++polygonIndex )  
{  
    for ( int t = 0; t < 3; ++t )  
    {  
        FbxVector4 fbxNormal;  
        mesh->GetPolygonVertexNormal( polygonIndex, t, fbxNormal );  
    
        CRVector normal;
        normal.x = fbxNormal.mData[ 0 ];  
        normal.y = fbxNormal.mData[ 1 ];  
        normal.z = fbxNormal.mData[ 2 ];
    }
}

VertexNormal은 위와 같이 얻어올 수 있다. 이 외 머티리얼 데이터나 애니메이션 데이터등 임포트해야할 데이터가 아직 많이 남아있으나 이건 나중에 필요할 때 천천히 하나씩 하기로 하고 일단은 PositionNormal만 우선 임포트하도록 한다.

무료 FBX Asset파일을 다운로드 받아 임포트 후 렌더링 해본 결과 아래와 같은 결과물을 볼 수 있었다.



윈도우 3D 뷰어로 보면 다음과 같은 이미지이다.




뭔가 많이 다른것 같다. 일단 나는 아직 Depth Test도 하지 않고, Node의 렌더링 순서도 엉망이라 다르게 보인다. Depth Test만 해도 정상적인 이미지에 가까운 결과물을 얻을 수 있으리라 생각된다.

yunei0313/CRY at FBX-Load-(-Position,-Normal-)에서 코드 확인이 가능하다.



이전글 : [DirectX11] 15. 카메라( View & Projection ) – Constant Buffer 활용하기 2편.
다음글 : [DirectX11] 17. Depth-Stencil Buffer 사용하기.



Leave a Comment