공부/DirectX3D

[DirectX3D/ASSIMP] ASSIMP 코드 정리(진행중)

MY블로그 2023. 6. 28. 22:04

현재진행상황

하이어라이키 만들기

메쉬 만들기

머테리얼 만들기

본정보 받아와 스켈레톤 만들기

현재 내용이 지속적으로 추가되면서 이해하지 못한 부분도 있기때문에 지속적으로 복습이 필요함.

아래의 코드는 내용정리를 위한것.

ASSIMP의 라이브러리는 따로 첨부 X

 

Main.h

#pragma once

//메인에는 씬만 구성

class Main : public Scene
{
private:
	Camera*				Cam;
	Grid*				grid;

	Actor*				temp;
	string				file;

	Assimp::Importer	importer;
	const aiScene*		scene;



public:
	Main();
	~Main();
	virtual void Init() override;
	virtual void Release() override; //해제
	virtual void Update() override;
	virtual void LateUpdate() override;//갱신
	virtual void Render() override;
	virtual void PreRender() override;
	virtual void ResizeScreen() override;

	void MakeHierarchy(aiNode* node, GameObject* node2);
	void MakeMesh(aiNode* node, GameObject* node2);
	void MakeMaterial();

	Matrix ToMatrix(aiMatrix4x4& value)
	{
		return Matrix
		(
			value.a1, value.b1, value.c1, value.d1,
			value.a2, value.b2, value.c2, value.d2,
			value.a3, value.b3, value.c3, value.d3,
			value.a4, value.b4, value.c4, value.d4
		);
	};
	void ReadBoneData(aiMesh* mesh, vector<class VertexWeights>& vertexWeights);
};


#define MAX_WEIGHTS 4 // 매크로 값
struct VertexWeights // 구조체 생성
{
	UINT	boneIdx[MAX_WEIGHTS]; // 배열 4개 선언
	float	boneWeights[MAX_WEIGHTS]; // 배열 4개 선언
	VertexWeights() // 생성자
	{
		ZeroMemory(boneIdx, sizeof(UINT) * MAX_WEIGHTS); // 배열을 초기화
		ZeroMemory(boneWeights, sizeof(float) * MAX_WEIGHTS); // 배열을 초기화
	}
	void AddData(UINT boneId, float weight) // 데이터 추가 함수
	{
		for (UINT i = 0; i < MAX_WEIGHTS; i++)
		{
			if (boneWeights[i] == 0.0f) // 가중치가 0인 요소를 찾았으면
			{
				boneIdx[i] = boneId; // 인덱스 배열에 아이디 저장
				boneWeights[i] = weight; // 가중치 배열에 가중치 저장
				return;
			}
		}
	}
	void Normalize() // 정규화
	{
		float total = 0.0f; // total 변수 초기화
		for (UINT i = 0; i < MAX_WEIGHTS; i++) // 정해둔 영향권(4곳) 만큼 반복진행
		{
			if (boneWeights[i] != 0.0f) // 가중치가 0이아니라면
			{
				total += boneWeights[i]; // 토탈에 전부 더해준다
			}
		}
		for (UINT i = 0; i < MAX_WEIGHTS; i++) // 정해둔 영향권(4곳) 만큼 반복진행
		{
			if (boneWeights[i] != 0.0f) // 가중치가 0이아니라면
			{
				boneWeights[i] /= total; // 해당배열의값을 total로 나누어 일정비율로 만든다.
			}
		}
		// 즉 정규화 작업은 4개의 가중치를 모두더했을때 1이라는 값이 되도록
		// 각각 일정비율화 시키는 것입니다.
	}
};

Main.cpp

#include "stdafx.h"
#include "Main.h"


Main::Main()
{

}

Main::~Main()
{

}


void Main::Init()
{
	Cam = Camera::Create();
	Cam->LoadFile("Cam.xml");
	Camera::main = Cam;
	Cam->width = App.GetWidth();
	Cam->height = App.GetHeight();
	Cam->viewport.width = App.GetWidth();
	Cam->viewport.height = App.GetHeight();

	grid = Grid::Create();

	temp = Actor::Create();

}

void Main::Release()
{

}


void Main::Update()
{
	ImGui::Begin("Hierarchy");
	Cam->RenderHierarchy();
	temp->RenderHierarchy();
	grid->RenderHierarchy();
	ImGui::End();

	if (GUI->FileImGui("ModelImporter", "ModelImporter",
		".fbx,.obj,.x", "../Assets"))
	{
		file = ImGuiFileDialog::Instance()->GetCurrentFileName();
		string path = "../Assets/" + file;

		importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
		scene = importer.ReadFile
		(
			path,
			aiProcess_ConvertToLeftHanded
			| aiProcess_Triangulate
			| aiProcess_GenUVCoords
			| aiProcess_GenNormals
			| aiProcess_CalcTangentSpace
		);
		assert(scene != NULL and "Import Error");

		

		temp->ReleaseMember();
		temp->skeleton = new Skeleton();

		MakeMaterial();

		GameObject* empty = GameObject::Create("empty");
		temp->AddChild(empty);
		temp->Update();

		MakeHierarchy(scene->mRootNode, empty);

		{
			int tok = file.find_last_of(".");
			string checkPath = "../Contents/Skeleton/" + file.substr(0, tok);
			if (!PathFileExistsA(checkPath.c_str()))
			{
				CreateDirectoryA(checkPath.c_str(), NULL);
			}

			string filePath = file.substr(0, tok) + "/";
			temp->skeleton->file = filePath + file.substr(0, tok) + ".skel";
			temp->skeleton->BonesUpdate(temp);
			temp->skeleton->SaveFile(temp->skeleton->file);
		}


		MakeMesh(scene->mRootNode, empty);


		importer.FreeScene();
	}

	

	temp->skeleton->BonesUpdate(temp);

	Camera::ControlMainCam();
	Cam->Update();
	grid->Update();
	temp->Update();
}

void Main::LateUpdate()
{
}
void Main::PreRender()
{
}
void Main::Render()
{
	Cam->Set();
	grid->Render();
	temp->Render();
}

void Main::ResizeScreen()
{
}

void Main::MakeHierarchy(aiNode* node, GameObject* node2)
{
	Matrix tempMat = ToMatrix(node->mTransformation);
	Vector3 s, r, t; Quaternion q;
	tempMat.Decompose(s, q, t);
	r = Util::QuaternionToYawPtichRoll(q);

	//if(node2->parent)
	//node2->parent->Update();

	node2->scale = s;
	node2->rotation = r;
	node2->SetLocalPos(t);

	temp->Update();

	temp->skeleton->bonesOffset[node2->boneIndex]
		= node2->W.Invert();

	//MakeMesh(node, node2);

	
	for (int i = 0; i < node->mNumChildren; i++)
	{
		GameObject* child = GameObject::Create(node->mChildren[i]->mName.C_Str());
		node2->AddChild(child);
		MakeHierarchy(node->mChildren[i], child);
	}
}

void Main::MakeMesh(aiNode* node, GameObject* node2)
{
	//
	//루트 노드에 담겨있는 메쉬 갯수만큼 반복
	for (int i = 0; i < node->mNumMeshes; i++)
	{
		int index = node->mMeshes[i];
		aiMesh* mesh = scene->mMeshes[index];
		aiMaterial* mtl = scene->mMaterials[mesh->mMaterialIndex];
		string mtlFile = mtl->GetName().C_Str();
		int tok = file.find_last_of(".");
		string filePath = file.substr(0, tok) + "/";
		mtlFile = filePath + mtlFile + ".mtl";


	

		GameObject* Current = node2;
		//메쉬가 두개 이상일때
		if (i != 0)
		{
			Current = 
				GameObject::Create(node2->name + "meshObject" + to_string(i));
			node2->AddChild(Current);
		}
		Current->shader = RESOURCE->shaders.Load("4.Cube.hlsl");
		Current->material =new Material();
		Current->material->LoadFile(mtlFile);
		Current->mesh = make_shared<Mesh>();
		Current->mesh->byteWidth = sizeof(VertexModel);
		Current->mesh->primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		Current->mesh->vertexType = VertexType::MODEL;
		Current->mesh->vertexCount = mesh->mNumVertices;
		Current->mesh->vertices = new VertexModel[mesh->mNumVertices];
		//Current->mesh->indexCount = 
		vector<UINT> indexList;

		vector<VertexWeights>	VertexWeights;
		VertexWeights.resize(mesh->mNumVertices);
		//VertexWeights.shrink_to_fit();
		ReadBoneData(mesh, VertexWeights);

		for (int j = 0; j < mesh->mNumVertices; j++)
		{
			VertexModel* vertex = (VertexModel*)Current->mesh->vertices;
			//aiVector3D
			//텍스쳐 좌표가 있다면
			if (mesh->HasTextureCoords(0))
			{
				vertex[j].uv.x =mesh->mTextureCoords[0][j].x;
				vertex[j].uv.y =mesh->mTextureCoords[0][j].y;
			}
			if (mesh->HasNormals())
			{
				vertex[j].normal.x = mesh->mNormals[j].x;
				vertex[j].normal.y = mesh->mNormals[j].y;
				vertex[j].normal.z = mesh->mNormals[j].z;
			}
			if (mesh->HasPositions())
			{
				vertex[j].position.x = mesh->mVertices[j].x;
				vertex[j].position.y = mesh->mVertices[j].y;
				vertex[j].position.z = mesh->mVertices[j].z;
			}
			if (mesh->HasTangentsAndBitangents())
			{
				vertex[j].tangent.x = mesh->mTangents[j].x;
				vertex[j].tangent.y = mesh->mTangents[j].y;
				vertex[j].tangent.z = mesh->mTangents[j].z;
			}
			//본데이터가 있을때
			if (!VertexWeights.empty())
			{
				VertexWeights[j].Normalize();

				vertex[j].indices.x = (float)VertexWeights[j].boneIdx[0];
				vertex[j].indices.y = (float)VertexWeights[j].boneIdx[1];
				vertex[j].indices.z = (float)VertexWeights[j].boneIdx[2];
				vertex[j].indices.w = (float)VertexWeights[j].boneIdx[3];

				vertex[j].weights.x = VertexWeights[j].boneWeights[0];
				vertex[j].weights.y = VertexWeights[j].boneWeights[1];
				vertex[j].weights.z = VertexWeights[j].boneWeights[2];
				vertex[j].weights.w = VertexWeights[j].boneWeights[3];
			}
		}

		for (int j = 0; j < mesh->mNumFaces; j++)
		{
			for (int k = 0; k < mesh->mFaces[j].mNumIndices; k++)
			{
				indexList.push_back(mesh->mFaces[j].mIndices[k]);
			}
		}

		Current->mesh->indexCount = indexList.size();
		Current->mesh->indices = new UINT[indexList.size()];
		copy(indexList.begin(), indexList.end(), 
			stdext::checked_array_iterator<UINT*>
			(Current->mesh->indices, indexList.size()));

		Current->mesh->Reset();


		{
			int tok = file.find_last_of(".");
			string checkPath = "../Contents/Mesh/" + file.substr(0, tok);
			if (!PathFileExistsA(checkPath.c_str()))
			{
				CreateDirectoryA(checkPath.c_str(), NULL);
			}

			string filePath = file.substr(0, tok) + "/";
			string meshFile = mesh->mName.C_Str();
			Current->mesh->file = filePath + meshFile + ".mesh";
			Current->mesh->SaveFile(Current->mesh->file);
		}


	}
	for (UINT i = 0; i < node->mNumChildren; i++)
	{
		MakeMesh(node->mChildren[i],node2->children[node->mChildren[i]->mName.C_Str()]);
	}

}

void Main::MakeMaterial()
{
	for (int i = 0; i < scene->mNumMaterials; i++)
	{
		aiMaterial* srcMtl = scene->mMaterials[i];
		Material* destMtl = new Material();
		aiColor3D tempColor;

		destMtl->file = srcMtl->GetName().C_Str();

		//ambient
		srcMtl->Get(AI_MATKEY_COLOR_AMBIENT, tempColor);
		destMtl->ambient.x = tempColor.r;
		destMtl->ambient.y = tempColor.g;
		destMtl->ambient.z = tempColor.b;

		//diffuse
		srcMtl->Get(AI_MATKEY_COLOR_DIFFUSE, tempColor);
		destMtl->diffuse.x = tempColor.r;
		destMtl->diffuse.y = tempColor.g;
		destMtl->diffuse.z = tempColor.b;

		//specular
		srcMtl->Get(AI_MATKEY_COLOR_SPECULAR, tempColor);
		destMtl->specular.x = tempColor.r;
		destMtl->specular.y = tempColor.g;
		destMtl->specular.z = tempColor.b;

		//emissive
		srcMtl->Get(AI_MATKEY_COLOR_EMISSIVE, tempColor);
		destMtl->emissive.x = tempColor.r;
		destMtl->emissive.y = tempColor.g;
		destMtl->emissive.z = tempColor.b;

		//Shininess
		srcMtl->Get(AI_MATKEY_SHININESS, destMtl->shininess);
		//opacity
		srcMtl->Get(AI_MATKEY_OPACITY, destMtl->opacity);

		//Normal
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_NORMALS, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->ambient.w = 1.0f;
				destMtl->normalMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->normalMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);

			}
		}

		//Diffuse
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_DIFFUSE, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->diffuse.w = 1.0f;
				destMtl->diffuseMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->diffuseMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);

			}
		}

		//specular
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_SPECULAR, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->specular.w = 1.0f;
				destMtl->specularMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->specularMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);

			}
		}

		//emissive
		{
			aiString aifile;
			string TextureFile;
			aiReturn texFound;
			texFound = srcMtl->GetTexture(aiTextureType_EMISSIVE, 0, &aifile);
			TextureFile = aifile.C_Str();
			size_t index = TextureFile.find_last_of('/');
			TextureFile = TextureFile.substr(index + 1, TextureFile.length());

			//텍스쳐가 있다.
			if (texFound == AI_SUCCESS && file != "")
			{
				destMtl->emissive.w = 1.0f;
				destMtl->emissiveMap = make_shared<Texture>();

				size_t tok = file.find_last_of(".");
				string checkPath = "../Contents/Texture/" + file.substr(0, tok);
				if (!PathFileExistsA(checkPath.c_str()))
				{
					CreateDirectoryA(checkPath.c_str(), NULL);
				}
				string orgin = "../Assets/" + TextureFile;
				string copy = "../Contents/Texture/" + file.substr(0, tok) + "/" + TextureFile;
				bool isCheck = true;
				CopyFileA(orgin.c_str(), copy.c_str(), isCheck);

				destMtl->emissiveMap->LoadFile(file.substr(0, tok) + "/" + TextureFile);

			}
		}

		size_t tok = file.find_last_of(".");
		string checkPath = "../Contents/Material/" + file.substr(0, tok);
		if (!PathFileExistsA(checkPath.c_str()))
		{
			CreateDirectoryA(checkPath.c_str(), NULL);
		}

		string filePath = file.substr(0, tok) + "/";
		destMtl->file = filePath + destMtl->file + ".mtl";
		destMtl->SaveFile(destMtl->file);
	}



}

void Main::ReadBoneData(aiMesh* mesh, vector<class VertexWeights>& vertexWeights) // 본데이터 읽어오는 함수
{
	//메쉬가 가지고 있는 본 개수 만큼
	for (UINT i = 0; i < mesh->mNumBones; i++)
	{
		//현재본이 하이어라이키에서 몇번째 인덱스인가?
		string boneName = mesh->mBones[i]->mName.C_Str();
		int boneIndex = temp->Find(boneName)->boneIndex;

		for (UINT j = 0; j < mesh->mBones[i]->mNumWeights; j++) // 가중치 넣어주기
		{
			UINT vertexID = mesh->mBones[i]->mWeights[j].mVertexId;
			vertexWeights[vertexID].AddData(boneIndex, mesh->mBones[i]->mWeights[j].mWeight);
		}
	}
}

int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR param, int command)
{
	App.SetAppName(L"ObjLoader");
	App.SetInstance(instance);
    WIN->Create();
    D3D->Create();
	Main * main = new Main();
    main->Init();

	int wParam = (int)WIN->Run(main);


    main->Release();
	SafeDelete(main);
    D3D->DeleteSingleton();
	WIN->DeleteSingleton();

	return wParam;
}