본문 바로가기
공부/DirectX3D

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

by MY블로그 2023. 6. 28.

현재진행상황

하이어라이키 만들기

메쉬 만들기

머테리얼 만들기

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

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

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

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;
}

 

댓글