[D3D] Assimp - Material

vector·2025년 11월 24일

1. Material 로딩

  • scene을 통해서 material을 순회하면서 파일내부의 모든것을 저장
  • 이때 xml을 만들기 위해 tinyxml라이브러리 사용

Convert.h

#pragma once
#include "AsTypes.h"

class Converter
{
public:
	Converter();
	~Converter();

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

private:
	void ReadModelData(aiNode* node, int32 index, int32 parent);	// Material과 관련된 부분 이외의 정보를 로드하는 함수
	void ReadMeshData(aiNode* node, int32 bone);
	void WriteModelFile(wstring finalPaht);


private:
	void ReadMaterialData();	// Material과 관련된 정보를 로드하는 함수
	void WriteMaterialData(wstring finalPath);
	string WriteTexture(string saveFolder, string file);

public:
	wstring _assetPath = L"../Resources/Assets/";
	wstring _modelPath = L"../Resources/Models/";
	wstring _texturePath = L"../Resources/Textures/";
private:
	shared_ptr<Assimp::Importer> _importer;

	const aiScene* _scene;

private:
	vector<shared_ptr<asBone>> _bones;
	vector<shared_ptr<asMesh>> _meshes;
	vector<shared_ptr<asMaterial>> _materials;
};
#include "pch.h"
#include "Converter.h"
#include <filesystem> // c++17부터 지원
#include "Utils.h"
#include "tinyxml2.h"

Converter::Converter()
{
	_importer = make_shared<Assimp::Importer>();
}

Converter::~Converter()
{
}

void Converter::ReadAssetFile(wstring file)
{
	wstring fileStr = _assetPath + file;

	auto p = filesystem::path(fileStr);
	assert(filesystem::exists(p));

	_scene = _importer->ReadFile(
		Utils::ToString(fileStr),
		aiProcess_ConvertToLeftHanded | 
		aiProcess_Triangulate | 
		aiProcess_GenUVCoords | 
		aiProcess_GenNormals | 
		aiProcess_CalcTangentSpace
	);

	assert(_scene != nullptr);
}

void Converter::ExportModelData(wstring savePath)
{ // 모델 데이터를 추출해서 그 정보를 가지고 와서 별도의 파일을 만드는
	wstring finalPath = _modelPath + savePath + L".mesh";

	ReadModelData(_scene->mRootNode, -1, -1); // 모델의 모든 노드를 재귀를 통해서 로드하는
	WriteModelFile(finalPath); // 메모리에 들고 있던 것들을 최종 타일 형태로 만드는
}

void Converter::ExportMaterialData(wstring savePath)
{
	wstring finalPath = _texturePath + savePath + L".xml";
	ReadMaterialData();
	WriteMaterialData(finalPath);
}

void Converter::ReadModelData(aiNode* node, int32 index, int32 parent)
{
	
}

void Converter::ReadMeshData(aiNode* node, int32 bone)
{
}

void Converter::WriteModelFile(wstring finalPaht)
{
}

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();

		// Material의 Ambient, Diffuse, Specular, Emissive값 가져오기
		aiColor3D color;
		// Ambient
		srcMaterial->Get(AI_MATKEY_COLOR_AMBIENT, color);
		material->ambient = Color(color.r, color.g, color.b, 1.f);
		// Diffuse
		srcMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color);
		material->diffuse = Color(color.r, color.g, color.b, 1.f);
		// Specular
		srcMaterial->Get(AI_MATKEY_COLOR_SPECULAR, color);
		material->specular = Color(color.r, color.g, color.b, 1.f);
		srcMaterial->Get(AI_MATKEY_SHININESS, material->specular.w);
		// Emissive
		srcMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, color);
		material->emissive = Color(color.r, color.g, color.b, 1.f);

		aiString file;

		// DiffuseFile
		srcMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &file);
		material->diffuseFile = file.C_Str();

		// SpecularFile
		srcMaterial->GetTexture(aiTextureType_SPECULAR, 0, &file);
		material->specularFile = file.C_Str();

		// NormalFile
		srcMaterial->GetTexture(aiTextureType_NORMALS, 0, &file);
		material->normalFile = file.C_Str();

		_materials.push_back(material);
	}
}

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

	// 폴더가 없으면 폴더 생성
	filesystem::create_directory(path.parent_path());
	
	string folder = path.parent_path().string();

	shared_ptr<tinyxml2::XMLDocument> document = make_shared<tinyxml2::XMLDocument>();

	tinyxml2::XMLDeclaration* decl = document->NewDeclaration();
	document->LinkEndChild(decl);

	tinyxml2::XMLElement* root = document->NewElement("Materials");
	document->LinkEndChild(root);

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

		tinyxml2::XMLElement* element = nullptr;

		element = document->NewElement("Name");
		element->SetText(material->name.c_str());
		node->LinkEndChild(element);

		element = document->NewElement("DiffuseFile");
		element->SetText(WriteTexture(folder, material->diffuseFile).c_str());
		node->LinkEndChild(element);

		element = document->NewElement("SpecularFile");
		element->SetText(WriteTexture(folder, material->specularFile).c_str());
		node->LinkEndChild(element);

		element = document->NewElement("NormalFile");
		element->SetText(WriteTexture(folder, material->normalFile).c_str());
		node->LinkEndChild(element);

		element = document->NewElement("Ambient");
		element->SetAttribute("R", material->ambient.x);
		element->SetAttribute("G", material->ambient.y);
		element->SetAttribute("B", material->ambient.z);
		element->SetAttribute("A", material->ambient.w);
		node->LinkEndChild(element);

		element = document->NewElement("Diffuse");
		element->SetAttribute("R", material->diffuse.x);
		element->SetAttribute("G", material->diffuse.y);
		element->SetAttribute("B", material->diffuse.z);
		element->SetAttribute("A", material->diffuse.w);
		node->LinkEndChild(element);

		element = document->NewElement("Specular");
		element->SetAttribute("R", material->specular.x);
		element->SetAttribute("G", material->specular.y);
		element->SetAttribute("B", material->specular.z);
		element->SetAttribute("A", material->specular.w);
		node->LinkEndChild(element);

		element = document->NewElement("Emissive");
		element->SetAttribute("R", material->emissive.x);
		element->SetAttribute("G", material->emissive.y);
		element->SetAttribute("B", material->emissive.z);
		element->SetAttribute("A", material->emissive.w);
		node->LinkEndChild(element);
	}

	document->SaveFile(Utils::ToString(finalPath).c_str());
}

string Converter::WriteTexture(string saveFolder, string file)
{
	string fileName = filesystem::path(file).filename().string();
	string folderName = filesystem::path(saveFolder).filename().string();

	// embeddedTexture 가져오는 방법, fbx에 texture가 내장되어 있는 경우
	// 웬만하면 텍스처 파일이 따로있는 버전을 사용하는 걸 권장. -> 관리 차원에서 조금 더 편하다
	const aiTexture* srcTexture = _scene->GetEmbeddedTexture(file.c_str()); 
	// 사용 권장x
	if (srcTexture)
	{
		string pathStr = saveFolder + fileName;

		// 바이너리모드로
		if (srcTexture->mHeight == 0)
		{
			//shared_ptr<FileUtils> file = make_shared<FileUtils>();
			//file->Open(Utils::ToWString(pathStr), FileMode::Write);
			//file->Write(srcTexture->pcData, srcTexture->mWidth);
		}
		else
		{
			D3D11_TEXTURE2D_DESC desc;
			ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
			desc.Width = srcTexture->mWidth;
			desc.Height = srcTexture->mHeight;
			desc.MipLevels = 1;
			desc.ArraySize = 1;
			desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			desc.SampleDesc.Count = 1;
			desc.SampleDesc.Quality = 0;
			desc.Usage = D3D11_USAGE_IMMUTABLE;

			D3D11_SUBRESOURCE_DATA subResource = { 0 };
			subResource.pSysMem = srcTexture->pcData;

			ComPtr<ID3D11Texture2D> texture;
			HRESULT hr = DEVICE->CreateTexture2D(&desc, &subResource, texture.GetAddressOf());
			CHECK(hr);

			DirectX::ScratchImage img;
			::CaptureTexture(DEVICE.Get(), DC.Get(), texture.Get(), img);

			// Save To File, 최신SaveToDDS라는 텍스 라이브러리
			hr = DirectX::SaveToDDSFile(*img.GetImages(), DirectX::DDS_FLAGS_NONE, Utils::ToWString(fileName).c_str());
			CHECK(hr);
		}
	}
	else // 텍스처 따로 있을 경우
	{
		// 기존에 있던 Resources/Asset/House/내부에 있는 texture 폴더내부의 texture를 복사해서
		string originStr = (filesystem::path(_assetPath) / folderName / file).string(); // 원본 경로
		Utils::Replace(originStr, "\\", "/");

		// Resources/Textures 에 House폴더를 생성해서 그 안에 texture를 붙여 넣는다.
		string pathStr = (filesystem::path(saveFolder) / fileName).string(); // 최종 경로
		Utils::Replace(pathStr, "\\", "/");

		// WinAPI
		::CopyFileA(originStr.c_str(), pathStr.c_str(), false);
	}


	return fileName;
}

AssimpTool

  • Memory를 CustomFile로 저장한다.
void AssimpTool::Init()
{
	{
		shared_ptr<Converter> converter = make_shared<Converter>();

		// FBX -> Memory
		converter->ReadAssetFile(L"House/House.fbx");

		// Memory -> CustomData(file)
		converter->ExportMaterialData(L"House/House");		// Material 추출
		converter->ExportModelData(L"House/House");			// Model 추출

		// CUstumData(file) -> Memory
	}
    
	{ 	// Tower 추가
		shared_ptr<Converter> converter = make_shared<Converter>();

		// FBX -> Memory
		converter->ReadAssetFile(L"Tower/Tower.fbx");

		// Memory -> CustomData(file)
		converter->ExportMaterialData(L"Tower/Tower");		// Material 추출
		converter->ExportModelData(L"Tower/Tower");			// Model 추출

		// CUstumData(file) -> Memory
	}

}

결과

  • 아래와 같이 Resources/TextureHouse, Tower라는 폴더가 생성
  • 폴더내부에 House폴더에는 House.xml파일
  • Tower폴더에는 Tower.xml,Wood_Tower_Col.jpg, Wood_Tower_Nor.jpg 파일 생성
  • xml파일 내부는 아래 사진과 같다

참고 강의

https://www.inflearn.com/courses/lecture?courseId=329791&type=LECTURE&unitId=161139&tab=curriculum&subtitleLanguage=ko

첨부자료 출처

https://free3d.com/ko/3d-model/abandoned-cottage-house-825251.html

profile
게임 클라이언트 프로그래머 준비중 (공부 및 기록용)

0개의 댓글