수업


✅ 주제

이번 강의의 주제는 Assimp 라이브러리를 활용하여 FBX 파일 내의 Material 정보를 추출하고, 이를 구조화된 커스텀 데이터(XML)로 저장하는 전체 과정을 구현하는 것입니다.

Assimp를 통해 로딩한 FBX 파일에는 다양한 머티리얼 정보가 포함되어 있으며, 이 정보들은 aiMaterial이라는 구조를 통해 접근할 수 있습니다. 본 강의에서는 이 머티리얼 정보를 우리만의 자료구조(asMaterial)로 변환하고, 이후 tinyxml2 라이브러리를 사용해 XML 형식으로 저장하는 기능을 구현합니다.

또한, FBX에 내장 텍스처가 포함된 경우 메모리에서 직접 추출하여 저장하고, 외부 텍스처가 존재할 경우에는 원본 파일 경로를 따라 지정된 폴더에 복사되도록 자동화하는 파이프라인도 포함됩니다.

이 모든 과정은 단순히 로딩에 그치지 않고, 파싱 → 변환 → 저장이라는 일련의 자동화된 구조를 갖추는 데 초점을 맞추고 있으며, 결과적으로 우리가 게임 엔진이나 툴에서 활용 가능한 형태로 데이터를 가공하는 데 목적이 있습니다.


✅ 개념

FBX 파일을 다룰 때 가장 핵심이 되는 건 바로 구조를 이해하고, 그 구조를 어떻게 파싱해서 우리 방식으로 변환하느냐입니다.
이번 강의에서는 Assimp 라이브러리를 기반으로 머티리얼 데이터를 파싱하고, 우리가 원하는 XML 파일로 저장하는 전체적인 흐름을 배웁니다.


1. Assimp 구조 이해

Assimp에서는 FBX 파일을 aiScene이라는 하나의 루트 객체로 표현합니다.
이 객체는 FBX에 포함된 모든 구성 요소—노드, 메쉬, 머티리얼, 애니메이션, 텍스처 등을 계층적으로 담고 있으며, 그 내부 구조는 다음과 같습니다:

  • aiScene: FBX 전체를 표현하는 루트 컨테이너입니다.
  • aiNode: 계층 구조를 구성하며, 각 노드는 자식 노드를 갖고 있고 Transform 정보(SRT)를 포함합니다.
  • aiMesh: 정점(Vertex), UV, 노멀, 머티리얼 인덱스를 포함한 메시 데이터입니다.
  • aiMaterial: 이름, 색상 정보(Ambient, Diffuse, Specular, Emissive), 텍스처 파일 경로 등을 담고 있는 머티리얼입니다.
  • aiTexture: 텍스처가 FBX 파일 안에 내장된 경우, 해당 데이터를 메모리에서 바로 접근할 수 있게 해주는 구조입니다.

2. 파싱 전략

머티리얼 파싱 과정은 크게 3가지 요소를 분리하여 설계합니다:

  • Material, Mesh, Node서로 독립적으로 파싱하고,
  • Material과 Mesh는 순차적으로 읽으면 되지만,
  • Node는 계층 구조로 되어 있기 때문에 재귀적으로 순회해야 합니다.

즉, 머티리얼(Material)은 단독으로 읽을 수 있지만, 메쉬나 노드는 계층을 고려한 설계가 필요합니다.


3. 전체 흐름 (파이프라인)

이번 강의에서 구현할 흐름은 다음과 같습니다:

FBX 파일 → Assimp 메모리 로딩 → Material 추출 → 우리만의 구조(asMaterial)로 변환 → XML 저장

보다 구체적으로 보면 다음과 같은 단계로 이루어집니다:

  1. ReadAssetFile()
    • FBX 파일을 로딩하여 _scene 객체에 메모리로 로드합니다.
  2. ReadMaterialData()
    • _scene->mMaterials[] 배열을 순회하면서 aiMaterial 정보를 파싱하고, 이를 asMaterial이라는 커스텀 구조체로 변환합니다.
  3. WriteMaterialData()
    • _materials 벡터에 저장된 asMaterial들을 tinyxml2를 사용하여 XML 포맷으로 저장합니다.
  4. WriteTexture()
    • 텍스처가 내장되어 있으면 메모리에서 추출하여 저장하고, 외부 파일이라면 원본 위치에서 지정된 경로로 복사합니다.

이 개념을 바탕으로 본격적인 코드 구현에 들어가게 되며, 이후 구조적으로 완성된 Material 시스템을 통해 텍스처 관리와 데이터 저장을 자동화할 수 있게 됩니다.


✅ 용어정리

용어설명
aiSceneFBX 파일 전체를 대표하는 루트 구조체로, 메쉬, 머티리얼, 노드, 애니메이션 등 모든 데이터를 포함합니다.
aiNode계층 구조를 표현하는 단위로, 부모-자식 관계를 구성하며 각 노드는 메쉬 인덱스를 참조할 수 있습니다.
aiMesh정점(Vertex), 노멀(Normal), UV, 머티리얼 인덱스 등의 정보를 담고 있는 메쉬 데이터 구조입니다.
aiMaterial머티리얼의 이름, 색상 정보(Ambient, Diffuse, Specular 등), 텍스처 파일 경로 등의 정보를 담고 있는 구조체입니다.
aiTextureFBX 파일 내부에 포함된(임베디드된) 텍스처 데이터를 메모리로 읽어올 수 있게 해주는 구조체입니다.
Assimp::ImporterAssimp에서 FBX 파일을 메모리로 로딩할 때 사용하는 주요 진입점 객체입니다.
asMaterialAssimp로부터 파싱한 머티리얼 정보를 우리가 정의한 커스텀 구조로 담기 위한 자료구조입니다.
tinyxml2C++에서 사용 가능한 경량 XML 처리 라이브러리로, XML 파일 생성, 태그 추가, 속성 설정 등을 지원합니다.
XMLElementtinyxml2에서 사용되는 XML 태그 객체로, <Material>이나 <Diffuse>와 같은 요소를 표현합니다.
SetAttribute()XMLElement에 속성(R, G, B, A 등)을 설정할 때 사용하는 함수입니다.
SetText()XMLElement 태그 내의 텍스트 내용을 설정할 때 사용하는 함수입니다.
WriteTexture()텍스처 파일을 내장 또는 외부 경로에서 읽어 지정된 위치로 저장하거나 복사해주는 함수입니다.

좋습니다! 두 블로그의 코드를 기반으로 작성된 통합 코드 분석 내용을 강의 스타일로 완전하게 정리해드리겠습니다.
이 내용은 단순 요약이 아닌, 실제 강의 현장에서 코드와 함께 설명하기 적합한 형식으로 구성했습니다.


✅ 코드 분석

이번 시간에는 FBX의 Material 데이터를 로딩하고, XML로 저장하는 전체 로직을 코드 단위로 살펴보겠습니다.
우리가 구현한 파이프라인은 다음과 같은 구성요소들로 이뤄져 있습니다:

  • FBX → 메모리 로딩
  • Material 정보 파싱
  • XML 문서로 저장
  • 텍스처 파일 복사 또는 추출

이제 하나씩 코드를 보며 설명하겠습니다.


1. Converter 클래스 설계

class Converter {
public:
    void ReadAssetFile(wstring file);
    void ExportMaterialData(wstring savePath);
    void ExportModelData(wstring savePath);

private:
    void ReadMaterialData();
    void WriteMaterialData(wstring finalPath);
    string WriteTexture(string saveFolder, string file);

    ...
    vector<shared_ptr<asMaterial>> _materials;
};
  • ReadAssetFile() : FBX 파일을 메모리로 로딩
  • ReadMaterialData() : Assimp에서 머티리얼 정보 추출
  • WriteMaterialData() : XML 파일로 저장
  • WriteTexture() : 텍스처를 복사하거나 추출해서 저장
  • _materials : 우리 방식으로 변환한 머티리얼 리스트

2. FBX 파일 로딩 – ReadAssetFile

void Converter::ReadAssetFile(wstring file) {
    ...
    _scene = _importer->ReadFile(
        Utils::ToString(fileStr),
        aiProcess_ConvertToLeftHanded |
        aiProcess_Triangulate |
        aiProcess_GenUVCoords |
        aiProcess_GenNormals |
        aiProcess_CalcTangentSpace
    );
    assert(_scene != nullptr);
}
  • aiProcess_* 플래그는 FBX를 변환 및 정리할 때 사용하는 옵션들입니다.
  • 로드가 성공되면 _scene 객체 안에 모든 FBX 정보가 들어옵니다.

3. 머티리얼 파싱 – ReadMaterialData

void Converter::ReadMaterialData() {
    for (uint32 i = 0; i < _scene->mNumMaterials; i++) {
        aiMaterial* srcMaterial = _scene->mMaterials[i];
        shared_ptr<asMaterial> material = make_shared<asMaterial>();

        material->name = srcMaterial->GetName().C_Str();
        ...
        srcMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color);
        material->diffuse = Color(color.r, color.g, color.b, 1.f);
        ...
        srcMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &file);
        material->diffuseFile = file.C_Str();

        _materials.push_back(material);
    }
}
  • FBX에서 머티리얼 정보를 가져올 때, 이름, 색상, 텍스처 파일 경로를 하나씩 꺼내서 asMaterial 구조에 저장합니다.
  • 텍스처 정보는 나중에 실제 복사 또는 추출을 위해 WriteTexture()로 넘깁니다.

4. XML 생성 – WriteMaterialData

void Converter::WriteMaterialData(wstring finalPath) {
    auto path = filesystem::path(finalPath);
    filesystem::create_directory(path.parent_path());

    shared_ptr<tinyxml2::XMLDocument> document = make_shared<tinyxml2::XMLDocument>();
    document->LinkEndChild(document->NewDeclaration());
    tinyxml2::XMLElement* root = document->NewElement("Materials");
    document->LinkEndChild(root);

    for (shared_ptr<asMaterial> material : _materials) {
        tinyxml2::XMLElement* node = document->NewElement("Material");
        root->LinkEndChild(node);

        auto addTextNode = [&](const char* name, const string& value) {
            auto element = document->NewElement(name);
            element->SetText(value.c_str());
            node->LinkEndChild(element);
        };

        addTextNode("Name", material->name);
        addTextNode("DiffuseFile", WriteTexture(folder, material->diffuseFile));
        addTextNode("SpecularFile", WriteTexture(folder, material->specularFile));
        addTextNode("NormalFile", WriteTexture(folder, material->normalFile));

        auto addColorNode = [&](const char* tag, const Color& c) {
            auto element = document->NewElement(tag);
            element->SetAttribute("R", c.x);
            element->SetAttribute("G", c.y);
            element->SetAttribute("B", c.z);
            element->SetAttribute("A", c.w);
            node->LinkEndChild(element);
        };

        addColorNode("Ambient", material->ambient);
        addColorNode("Diffuse", material->diffuse);
        addColorNode("Specular", material->specular);
        addColorNode("Emissive", material->emissive);
    }

    document->SaveFile(Utils::ToString(finalPath).c_str());
}
  • XML 문서의 루트는 <Materials>
  • 각 머티리얼마다 <Material> 태그를 생성하고 내부에 텍스트 정보 및 색상 정보를 삽입
  • 색상은 SetAttribute()로 RGBA 속성으로 저장됩니다.

5. 텍스처 복사 및 저장 – WriteTexture

string Converter::WriteTexture(string saveFolder, string file) {
    string fileName = filesystem::path(file).filename().string();
    const aiTexture* srcTexture = _scene->GetEmbeddedTexture(file.c_str());

    if (srcTexture) {
        // 내장 텍스처 저장 로직
        if (srcTexture->mHeight == 0) {
            // 바이너리 포맷 저장 구현 예정
        } else {
            // DX11 Texture → DDS 변환 및 저장
            ...
        }
    } else {
        // 외부 텍스처 복사
        string originStr = (_assetPath / folderName / file).string();
        string pathStr = (saveFolder / fileName).string();
        ::CopyFileA(originStr.c_str(), pathStr.c_str(), false);
    }

    return fileName;
}
  • FBX에 내장된 텍스처가 있을 경우 메모리에서 바로 추출하여 DDS 파일로 저장합니다.
  • 외부 텍스처라면 기존 위치에서 복사하여 저장합니다.

6. 통합 사용 예 – AssimpTool::Init()

void AssimpTool::Init() {
    shared_ptr<Converter> converter = make_shared<Converter>();
    converter->ReadAssetFile(L"House/House.fbx");
    converter->ExportMaterialData(L"House/House");
    converter->ExportModelData(L"House/House");
}
  • 초기화 단계에서 FBX를 메모리로 로드하고,
  • Material 정보를 XML로 저장
  • Model 데이터도 함께 저장할 수 있도록 구조화

좋습니다! 두 블로그의 내용을 결합한 핵심 내용을 기반으로, 전체 강의의 마무리를 짓는 ‘핵심 정리’ 파트를 강의하는 듯한 말투로 완성해드리겠습니다.


✅ 핵심

이번 강의의 핵심은, FBX 파일에서 Material 정보를 자동으로 추출하고, 이를 사람이 편하게 읽고 수정할 수 있는 XML 형태로 변환·저장하는 자동화 파이프라인을 구축하는 데 있습니다.


🔸 핵심 흐름

  1. FBX 파일을 Assimp로 로딩하면 모든 정보가 _scene이라는 루트 구조체 안에 정리됩니다.
  2. 우리는 그중에서도 Material 정보만 별도로 추출asMaterial이라는 커스텀 구조체에 담습니다.
  3. 이 정보를 기반으로 WriteMaterialData() 함수를 호출하면, 우리가 정의한 XML 포맷으로 머티리얼 데이터를 자동 저장할 수 있습니다.
  4. 텍스처가 내장돼 있든 외부 파일로 존재하든, WriteTexture() 함수가 자동으로 경로를 파악해 해당 폴더로 저장하거나 복사해줍니다.
  5. 결과적으로, FBX 한 개를 로드하면 .xml 머티리얼 문서와 .dds 또는 복사된 텍스처 파일까지 완성되는 자동화 구조가 만들어집니다.

🔸 구조적으로 보면

  • Assimp에서 가져온 aiMaterial을 → asMaterial로 변환
  • 이 데이터를 → tinyxml2<Material> 구조로 XML로 출력
  • 텍스처 파일도 자동으로 처리되므로, 최종적으로 다음과 같은 구조의 문서가 생성됩니다:
<Materials>
    <Material>
        <Name>cottage_texture</Name>
        <DiffuseFile></DiffuseFile>
        <SpecularFile></SpecularFile>
        <NormalFile></NormalFile>
        <Ambient R="0.2" G="0.2" B="0.2" A="1"/>
        <Diffuse R="0.8" G="0.8" B="0.8" A="1"/>
        <Specular R="0.2" G="0.2" B="0.2" A="20"/>
        <Emissive R="0" G="0" B="0" A="1"/>
    </Material>
</Materials>
  • 이 XML은 인간이 직접 수정하거나 외부 툴에서 로딩하기 적합한 구조이며,
  • 텍스처 경로가 비어있어도 나중에 수동으로 채워넣을 수 있게 설계되어 있어 유연합니다.

profile
李家네_공부방

0개의 댓글