DirectX/DirectX 3D_(구)

18_ModelEditor [1/2]

컴맹학자 2021. 7. 13. 22:48
728x90

시작전에 Model 파일을 추출 저장 하기위한 솔루션 하나를 생성

그럼 아래와 같이 3개의 솔루션이 나오고 시작 프로그램을 ModelEditor로 걸고 종속성도 수정


ModelEditor(솔루션) -> stdafx.h

 

더보기
#pragma once

#include "Framework.h"
#pragma comment(lib, "../Debug/Framework.lib")

//모델 변환 하는 파일들
//디자인이 만든 모델 파일을 불러옴 (fbx, obj)
#include "Assimp/Importer.hpp" 
//소스로 불러올때 다양한 옵션을 줄 수 있음 (좌표계, UV 읽어오는 순서)
#include "Assimp/postprocess.h"
//postprocess를 통해 불러온 값 (RootNode -> (Bone, Tree형태) , Meshes, Material, Animations 등 -> 배열 형태로 들어옴) 
#include "Assimp/scene.h"      


#pragma comment(lib, "Assimp/assimp-vc140-mt.lib")

ModelEditor(솔루션) -> Types, h

더보기
#pragma once

#include "stdafx.h"

struct asBone
{
	int Index;
	string Name;

	int Parent;
	Matrix Transform;
};

struct asMeshPart
{
	string MaterialName;

	//DPCall에서 사용하기 위함
	UINT StartVertex;
	UINT VertexCount;
	UINT StartIndex;
	UINT IndexCount;
};

struct asMesh
{
	int BoneIndex;

	vector<Model::VertexModel> Vertices;
	vector<UINT> indices;

	vector<asMeshPart*> MeshParts;
};

ModelEditor(솔루션) -> Converter.cpp, h

더보기
#include "stdafx.h"
#include "Converter.h"
#include "Types.h"
#include "Utilities/BinaryFile.h"

Converter::Converter()
{
	importer = new Assimp::Importer();
}

Converter::~Converter()
{
	SafeDelete(importer);
}

//파일 읽기
void Converter::ReadFile(wstring file)
{
	this->file = L"../../_Assets/" + file;

	//aiProcess_ConvertToLeftHanded -> 왼손좌표계, CW 
	//aiProcess_Triangulate -> 삼각형으로 그려주게 만듬
	//aiProcess_GenUVCoords -> UV에 맞게 다시 계산
	//aiProcess_GenNormals -> 법선 백터이 있으면 가져옴
	//aiProcess_CalcTangentSpace -> 탄젠트를 가져옴
	scene = importer->ReadFile
	(
		String::ToString(this->file),
		aiProcess_ConvertToLeftHanded | aiProcess_Triangulate | aiProcess_GenUVCoords
		| aiProcess_GenNormals | aiProcess_CalcTangentSpace
	);

	if (scene == NULL)
	{
		string str = importer->GetErrorString();

		MessageBoxA(D3D::GetDesc().Handle, str.c_str(), "Assimp Error", MB_OK);
		exit(-1);
	}
}

//파일 추출
void Converter::ExportMesh(wstring savePath)
{
	ReadBoneData(scene->mRootNode, -1, -1);
	WriteMeshData(L"../../_Models/" + savePath + L".mesh");

	//file test
	{
		FILE* file;
		fopen_s(&file, "../BoneData.csv", "w");
		
		for (asBone* bone : bones)
		{
			fprintf(file, "%d,%d,%s\n", bone->Index, bone->Parent, bone->Name.c_str());
		}
		fclose(file);
	}
}

void Converter::ReadBoneData(aiNode * node, int index, int parent)
{
	asBone* bone = new asBone();
	bone->Index = index;
	bone->Parent = parent;
	bone->Name = node->mName.C_Str();
	
	Matrix transform(node->mTransformation[0]);
	//열 -> 행 변경
	D3DXMatrixTranspose(&bone->Transform, &transform);
	
	Matrix matParent;
	if (parent < 0)
		D3DXMatrixIdentity(&matParent); //초기 부모값
	else
		matParent = bones[parent]->Transform;

	//핵심 코드 (읽어온 상대 위치 * 부모위치)
	bone->Transform = bone->Transform * matParent;

	//본 추가
	bones.push_back(bone);

	ReadMeshData(node, index);

	//재귀함수
	for (UINT i = 0; i < node->mNumChildren; i++)
		ReadBoneData(node->mChildren[i], bones.size(), index );
}

void Converter::ReadMeshData(aiNode * node, int index)
{
	if (node->mNumMeshes < 1) return;

	//Model은 1개의 Mesh 덩어리를 만들기 위해 여러개의 MeshPart로 분할 저장되어 있음
	asMesh* mesh = new asMesh();
	mesh->BoneIndex = index; //Mesh덩어리가 붙어야 있어야 할 본 번호

	//Mesh 덩어리가 가진 Mesh Part 개수 만큼 반복
	for (UINT i = 0; i < node->mNumMeshes; i++)
	{
		//현재 본에 붙어있는 매쉬 번호
		UINT index = node->mMeshes[i];
		//번호를 통해 실제 ai 메쉬파트 정보 가져옴
		aiMesh* srcMesh = scene->mMeshes[index]; 

		UINT startVertex = mesh->Vertices.size();
		UINT startIndex = mesh->indices.size();

		//aiMesh가 가진 정점의 개수 만큼 돌면서 -> ps, uv, noraml만 저장
		for (UINT v = 0; v < srcMesh->mNumVertices; v++)
		{
			Model::VertexModel vertex;
			memcpy(&vertex.Postition, &srcMesh->mVertices[v], sizeof(Vector3));

			if (srcMesh->HasTextureCoords(0))
				memcpy(&vertex.UV, &srcMesh->mTextureCoords[0][v], sizeof(Vector2));

			if(srcMesh->HasNormals())
				memcpy(&vertex.Normal, &srcMesh->mNormals[v], sizeof(Vector3));

			mesh->Vertices.push_back(vertex);
		}

		//face의 인덱스는 0부터 출발하므로 +startVertex를 수행함
		for (UINT f = 0; f < srcMesh->mNumFaces; f++)
		{
			aiFace& face = srcMesh->mFaces[f];

			for (UINT k = 0; k < face.mNumIndices; k++)
				mesh->indices.push_back(face.mIndices[k] + startVertex);
		}

		//마테리얼 
		aiMaterial* material = scene->mMaterials[srcMesh->mMaterialIndex];

		asMeshPart* meshPart = new asMeshPart();
		meshPart->MaterialName = material->GetName().C_Str();
		meshPart->StartVertex = startVertex;
		meshPart->StartIndex = startIndex;
		meshPart->VertexCount = srcMesh->mNumVertices;
		meshPart->IndexCount = srcMesh->mNumFaces * srcMesh->mFaces->mNumIndices;

		mesh->MeshParts.push_back(meshPart);
	}

}

void Converter::WriteMeshData(wstring savePath)
{
	Path::CreateFolders(Path::GetDirectoryName(savePath));

	BinaryWriter* w = new BinaryWriter(savePath);

	//Save Bone
	w->UInt(bones.size());
	for (asBone* bone : bones)
	{
		w->Int(bone->Index);
		w->String(bone->Name);
		w->Int(bone->Parent);
		w->Matrix(bone->Transform);
	}

	//Save Mesh
	w->UInt(meshes.size());
	for (asMesh* meshData : meshes)
	{
		w->Int(meshData->BoneIndex);

		w->UInt(meshData->Vertices.size());
		w->Byte(&meshData->Vertices[0], sizeof(Model::VertexModel) * meshData->Vertices.size());

		w->UInt(meshData->indices.size());
		w->Byte(&meshData->indices[0], sizeof(UINT) * meshData->indices.size());

		w->UInt(meshData->MeshParts.size());
		for (asMeshPart* part : meshData->MeshParts)
		{
			w->String(part->MaterialName);

			w->UInt(part->StartVertex);
			w->UInt(part->VertexCount);

			w->UInt(part->StartIndex);
			w->UInt(part->IndexCount);
		}
	}

	SafeDelete(w);
}

 

#pragma once

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

	void ReadFile(wstring file);

public:
	void ExportMesh(wstring savePath); //저장할 매쉬정보

private:
	void ReadBoneData(aiNode* node, int index, int parent);
	void ReadMeshData(aiNode* node, int index);
	void WriteMeshData(wstring savePath); //바이너리 파일로 만들기

private:
	wstring file;
	
	Assimp::Importer* importer;
	const aiScene* scene; //읽어 오기만

	vector<struct asBone*> bones;
	vector<struct asMesh*> meshes;
	
};

ModelEditor(솔루션) -> Converter.cpp, h

더보기
#include "stdafx.h"
#include "ExportFile.h"
#include "Converter.h"

void ExportFile::Initialize()
{
	Tank(); // 한번 실행하면 주석처리
}

void ExportFile::Tank()
{
	Converter* conv = new Converter;
	conv->ReadFile(L"Tank/Tank.fbx");
	conv->ExportMesh(L"Tank/Tank");
	SafeDelete(conv);

}

 

#pragma once
#include "Systems/IExecute.h"

class ExportFile : public IExecute
{
public:
	virtual void Initialize() override;
	virtual void Destroy() override {};
	virtual void Update() override {};
	virtual void PreRender() override {};
	virtual void Render() override {};
	virtual void PostRender() override {};
	virtual void ResizeScreen() override {};

private:
	void Tank();
};

Framework(솔루션) -> Model.cpp, h

더보기
#pragma once

class Model
{
public:
	struct VertexModel;

public:
	struct VertexModel
	{
		Vector3 Postition;
		Vector2 UV;
		Vector3 Normal;
		Vector3 Tangent;// NormalMapping
		Vector4 BlendIndices; //애니메이션용 Animation Skinning
		Vector4 BlendWeights;

		VertexModel()
		{
			Postition = Vector3(0, 0, 0);
			UV = Vector2(0, 0);
			Normal = Vector3(0, 0, 0);
			Tangent = Vector3(0, 0, 0);
			BlendIndices = Vector4(0, 0, 0, 0);
			BlendWeights = Vector4(0, 0, 0, 0);
		}
	};

};

실행 결과물


ModelEditor 솔루션 : 오로지 모델의 파일을 추출해서 bin파일 형식등 쓰기 편하게 만들려고 하는 목적

Converter : 파일을 추출, 읽기, 저장 등 사용 하기 위한 클래스 

ExportFile : UnitTest에 있는 MeshDemo 처럼 Main클래스에 사용 하는 클래스

 

Model : ModelEditor 통해서 만들어진 모델 파일을 사용하기 위한 클래스

'DirectX > DirectX 3D_(구)' 카테고리의 다른 글

19_ModelMaterial [1/2]  (0) 2021.07.17
18_ModelEditor [2/2]  (0) 2021.07.14
17_Framework (Transform, PerFrame, Renderer)  (0) 2021.07.13
17_Framework (Buffer + hlsl)  (0) 2021.07.07
16_CubeMap [ 1 / 2 ] (수정)  (0) 2021.07.06