응애맘마조

230630 강의 본문

공부/3D강의

230630 강의

TH.Wert 2023. 6. 30. 18:25

어제 애니메이션 로드에 실패했지만 오늘 성공했습니다. 스켈레톤 클래스를 제거하고 애니메이션 클래스를 따로 만들어서 프레임과 뼈대를 합치게 되었습니다.

#pragma once
#define MAX_BONE 256

class Animation
{
public:
	UINT					frameMax;
	UINT					boneMax;
	Matrix**				arrFrameBone;
	float					tickPerSecond;
	string					file;

	Animation();
	~Animation();
	void LoadFile(string file);
	void SaveFile(string file);
};

class Animations
{
private:
	static shared_ptr<Animation> TPose;
public:
	static void CreateStaticMember();
	static void DeleteStaticMember();
private:
	ID3D11Buffer* frameBuffer;
	struct AnimBuffer
	{
		Matrix Frame[MAX_BONE];
	};
public:
	Animations();
	~Animations();
	void Set();
	void	RenderDetail();
	vector<shared_ptr<Animation>>		playList;
	int aniFrame = 0;
	int aniIdx = 0;
};
#include "framework.h"

Animation::Animation()
{
	frameMax = 0;
	boneMax = 0;
	tickPerSecond = 0;
	arrFrameBone = nullptr;
	file = "";
}

Animation::~Animation()
{
	for (UINT i = 0; i < frameMax; i++)
	{
		delete[] arrFrameBone[i];
	}
	delete[] arrFrameBone;
}

void Animation::LoadFile(string file)
{
	this->file = file;
	BinaryReader in;
	wstring path = L"../Contents/Animation/" + Util::ToWString(file);
	in.Open(path);

	frameMax = in.Int();
	boneMax = in.Int();
	tickPerSecond = in.Float();

	arrFrameBone = new Matrix * [frameMax];
	for (UINT i = 0; i < frameMax; i++)
	{
		arrFrameBone[i] = new Matrix[MAX_BONE];
	}

	for (UINT i = 0; i < frameMax; i++)
	{
		for (UINT j = 0; j < boneMax; j++)
		{
			arrFrameBone[i][j] = in.matrix();
		}
	}
	in.Close();
}

void Animation::SaveFile(string file)
{
	this->file = file;
	BinaryWriter out;
	wstring path = L"../Contents/Animation/" + Util::ToWString(file);
	out.Open(path);

	out.Int(frameMax);
	out.Int(boneMax);
	out.Float(tickPerSecond);

	for (UINT i = 0; i < frameMax; i++)
	{
		for (UINT j = 0; j < boneMax; j++)
		{
			out.matrix(arrFrameBone[i][j]);
		}
	}
	out.Close();
}

void Animations::Set()
{
	if (playList.size() > 0)
	{
		D3D11_MAPPED_SUBRESOURCE mappedResource;
		D3D->GetDC()->Map(frameBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
		memcpy_s(mappedResource.pData, sizeof(AnimBuffer), playList[aniIdx]->arrFrameBone[aniFrame], sizeof(AnimBuffer));
		D3D->GetDC()->Unmap(frameBuffer, 0);
	}
	D3D->GetDC()->VSSetConstantBuffers(2, 1, &frameBuffer);
}

Animations::Animations()
{
	D3D11_BUFFER_DESC desc = { 0 };
	desc.ByteWidth = sizeof(AnimBuffer);
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;//상수버퍼
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
	HRESULT hr = D3D->GetDevice()->CreateBuffer(&desc, NULL, &frameBuffer);

	assert(SUCCEEDED(hr));
}

Animations::~Animations()
{
	for (int i = 0; i < playList.size(); i++)
	{
		SafeReset(playList[i]);
	}
	SafeRelease(frameBuffer);
}
shared_ptr<Animation> Animations::TPose = nullptr;

void Animations::CreateStaticMember()
{
}

void Animations::DeleteStaticMember()
{
}

애니메이션의 헤더와 cpp파일입니다. 여기에서 T-Pose를 저장하고 애니메이션을 받아옵니다.


#pragma once

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

	Actor*				temp;
	string				file;

	Assimp::Importer	importer;
	const aiScene*		scene;
	vector<map<string, Matrix>> aniMap;
	shared_ptr<Animation> Anim;
	Matrix				bonesOffset[MAX_BONE];

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 MakeAnim( GameObject* node ,int frame);
	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];
	float	boneWeights[MAX_WEIGHTS];
	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)
			{
				boneIdx[i] = boneId;
				boneWeights[i] = weight;
				return;
			}
		}
	}
	void Normalize()
	{
		float total = 0.0f;
		for (UINT i = 0; i < MAX_WEIGHTS; i++)
		{
			if (boneWeights[i] != 0.0f)
			{
				total += boneWeights[i];
			}
		}
		for (UINT i = 0; i < MAX_WEIGHTS; i++)
		{
			if (boneWeights[i] != 0.0f)
			{
				boneWeights[i] /= total;
			}
		}
	}
};

struct AnimPosition
{
	float time;
	Vector3 pos;
};

struct AnimScale
{
	float time;
	Vector3 scale;
};

struct AnimRotation
{
	float time;
	Quaternion quater;
};

struct AnimNode
{
	string					name;
	//각 벡터 사이즈가 다를수있다.
	vector<AnimPosition>	position;
	vector<AnimScale>		scale;
	vector<AnimRotation>	rotation;
};

namespace Interpolated
{
	Vector3 CalcInterpolatedScaling(AnimNode* iter, float time, int Duration);
	Quaternion CalcInterpolatedRotation(AnimNode* iter, float time, int Duration);
	Vector3 CalcInterpolatedPosition(AnimNode* iter, float time, int Duration);
	int FindScale(AnimNode* iter, float time);
	int FindRot(AnimNode* iter, float time);
	int FindPos(AnimNode* iter, float time);
}
#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();
		
		MakeMaterial();

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

		MakeHierarchy(scene->mRootNode, empty);
		MakeMesh(scene->mRootNode, empty);

		SafeDelete(temp->animations);
		temp->animations = new Animations();

		importer.FreeScene();
	}

	if (GUI->FileImGui("AnimationImporter", "AnimationImporter",
		".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");

		//애니메이션 갯수 i
		for (UINT i = 0; i < scene->mNumAnimations; i++)
		{
			Anim = make_shared<Animation>();
			aiAnimation* srcAnim = scene->mAnimations[i];
			size_t tok2 = file.find_last_of(".");
			Anim->file = file.substr(0, tok2) + to_string(i);
			Anim->frameMax = (int)srcAnim->mDuration + 1;
			Anim->tickPerSecond = srcAnim->mTicksPerSecond != 0.0 ? (float)srcAnim->mTicksPerSecond : 25.0f;
			Anim->boneMax = temp->boneIndexCount;
			Anim->arrFrameBone = new Matrix * [Anim->frameMax];
			//프레임 j
			for (UINT j = 0; j < Anim->frameMax; j++)
			{
				Anim->arrFrameBone[j] = new Matrix[MAX_BONE];
			}

			aniMap.clear();
			aniMap.resize(Anim->frameMax);

			//채널갯수 -> 본에 대응
			for (UINT j = 0; j < srcAnim->mNumChannels; j++)
			{
				AnimNode* animNode = new AnimNode();
				aiNodeAnim* srcAnimNode = srcAnim->mChannels[j];

				animNode->name = srcAnimNode->mNodeName.C_Str();
				//Scale
				for (UINT k = 0; k < srcAnimNode->mNumScalingKeys; k++)
				{
					AnimScale srcScale;
					srcScale.time = (float)srcAnimNode->mScalingKeys[k].mTime;
					srcScale.scale.x = (float)srcAnimNode->mScalingKeys[k].mValue.x;
					srcScale.scale.y = (float)srcAnimNode->mScalingKeys[k].mValue.y;
					srcScale.scale.z = (float)srcAnimNode->mScalingKeys[k].mValue.z;
					animNode->scale.push_back(srcScale);
				}
				//Position
				for (UINT k = 0; k < srcAnimNode->mNumPositionKeys; k++)
				{
					AnimPosition srcPosition;
					srcPosition.time = (float)srcAnimNode->mPositionKeys[k].mTime;
					srcPosition.pos.x = (float)srcAnimNode->mPositionKeys[k].mValue.x;
					srcPosition.pos.y = (float)srcAnimNode->mPositionKeys[k].mValue.y;
					srcPosition.pos.z = (float)srcAnimNode->mPositionKeys[k].mValue.z;
					animNode->position.push_back(srcPosition);
				}
				//Rotation
				for (UINT k = 0; k < srcAnimNode->mNumRotationKeys; k++)
				{
					AnimRotation srcRotation;
					srcRotation.time = (float)srcAnimNode->mRotationKeys[k].mTime;
					srcRotation.quater.x = (float)srcAnimNode->mRotationKeys[k].mValue.x;
					srcRotation.quater.y = (float)srcAnimNode->mRotationKeys[k].mValue.y;
					srcRotation.quater.z = (float)srcAnimNode->mRotationKeys[k].mValue.z;
					srcRotation.quater.w = (float)srcAnimNode->mRotationKeys[k].mValue.w;
					animNode->rotation.push_back(srcRotation);
				}
			
				GameObject* chanel = temp->Find(animNode->name);
				if (chanel)
				{
					Matrix S, R, T;
					Quaternion quter;
					Vector3 pos, scale;
					for (UINT k = 0; k < Anim->frameMax; k++)
					{
						pos = Interpolated::CalcInterpolatedPosition(animNode, (float)k, Anim->frameMax);
						scale = Interpolated::CalcInterpolatedScaling(animNode, (float)k, Anim->frameMax);
						quter = Interpolated::CalcInterpolatedRotation(animNode, (float)k, Anim->frameMax);

						S = Matrix::CreateScale(scale);
						R = Matrix::CreateFromQuaternion(quter);
						T = Matrix::CreateTranslation(pos);
						Matrix W = S * R * T;
						//Anim->arrFrameBone[k][chanel->boneIndex] = 
						//aniMap[k][animNode->name] = chanel->GetLocalInverse() * W;
						aniMap[k][animNode->name] =  W;
					}
				}
				//여기서 채널끝(본)
			}
			for (UINT j = 0; j < Anim->frameMax; j++)
			{
				MakeAnim(temp,j);
			}
			for (UINT j = 0; j < Anim->frameMax; j++)
			{

				for (UINT k = 0; k < Anim->boneMax; k++)
				{
					Anim->arrFrameBone[j][k] = Anim->arrFrameBone[j][k].Transpose();
				}
			}
            
			//여기서 애님끝
			temp->animations->playList.push_back(Anim);

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

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

		importer.FreeScene();
	}
	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::MakeAnim(GameObject* node, int frame)
{
	int idx = node->boneIndex;
	if (aniMap[frame].find(node->name) != aniMap[frame].end())
	{
		node->W = aniMap[frame][node->name];
	}
	else
	{
		node->W = node->GetLocal();
	}

	//노드가 부모가 있으면
	if (node->parent)
	{
		node->W *= node->parent->W;
	}              
	Anim->arrFrameBone[frame][idx] = bonesOffset[idx] * node->W;

	for (auto it = node->children.begin(); it != node->children.end(); it++)
		MakeAnim(it->second, frame);
}

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

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

	temp->Update();

	bonesOffset[node2->boneIndex] = node2->W.Invert();
    
	for (int i = 0; i < node->mNumChildren; i++)
	{
		GameObject* child = GameObject::Create(node->mChildren[i]->mName.C_Str());
		node2->AddBone(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);
		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;
}

Vector3 Interpolated::CalcInterpolatedScaling(AnimNode* iter, float time, int Duration)
{
	//비어있으면 1,1,1반환
	if (iter->scale.empty())
		return Vector3(1.0f, 1.0f, 1.0f);
	//한개만 있는놈은 첫번째 값 반환
	if (iter->scale.size() == 1)
		return iter->scale.front().scale;
	//마지막 놈은 마지막값 반환
	if (time == Duration - 1)
	{
		return iter->scale.back().scale;
	}
	//보간 시작
	int scaling_index = FindScale(iter, time);
    
	if (scaling_index == -1)
	{
		return iter->scale.back().scale;
	}

	UINT next_scaling_index = scaling_index + 1;
	assert(next_scaling_index < iter->scale.size());


	float delta_time = (float)(iter->scale[next_scaling_index].time
		- iter->scale[scaling_index].time);

	float factor = (time - (float)(iter->scale[scaling_index].time)) / delta_time;

	if (factor < 0.0f)
	{
		return iter->scale.front().scale;
	}

	auto start = iter->scale[scaling_index].scale;
	auto end = iter->scale[next_scaling_index].scale;

	start = Vector3::Lerp(start, end, factor);
	return start;
}
Quaternion Interpolated::CalcInterpolatedRotation(AnimNode* iter, float time, int Duration)
{
	if (iter->rotation.empty())
		return Quaternion(0.0f, 0.0f, 0.0f, 1.0f);

	if (iter->rotation.size() == 1)
		return iter->rotation.front().quater;

	if (time == Duration - 1)
	{
		return iter->rotation.back().quater;
	}

	int quter_index = FindRot(iter, time);
	if (quter_index == -1)
	{
		return iter->rotation.back().quater;
	}
	UINT next_quter_index = quter_index + 1;
	assert(next_quter_index < iter->rotation.size());

	float delta_time = static_cast<float>(iter->rotation[next_quter_index].time - iter->rotation[quter_index].time);
	float factor = (time - static_cast<float>(iter->rotation[quter_index].time)) / delta_time;
	if (factor < 0.0f)
	{
		return iter->rotation.front().quater;
	}

	auto start = iter->rotation[quter_index].quater;
	auto end = iter->rotation[next_quter_index].quater;

	start = Quaternion::Slerp(start, end, factor);
	return start;
}
Vector3 Interpolated::CalcInterpolatedPosition(AnimNode* iter, float time, int Duration)
{
	if (iter->position.empty())
		return Vector3(0.0f, 0.0f, 0.0f);

	if (iter->position.size() == 1)
		return iter->position.front().pos;

	if (time == Duration - 1)
	{
		return iter->position.back().pos;
	}

	int position_index = FindPos(iter, time);

	if (position_index == -1)
	{
		return iter->position.back().pos;
	}

	UINT next_position_index = position_index + 1;
	assert(next_position_index < iter->position.size());

	float delta_time = static_cast<float>(iter->position[next_position_index].time - iter->position[position_index].time);
	float factor = (time - static_cast<float>(iter->position[position_index].time)) / delta_time;

	if (factor < 0.0f)
	{
		return iter->position.front().pos;
	}

	auto start = iter->position[position_index].pos;
	auto end = iter->position[next_position_index].pos;

	start = Vector3::Lerp(start, end, factor);
	return start;
}

int Interpolated::FindScale(AnimNode* iter, float time)
{
	if (iter->scale.empty())
		return -1;

	for (UINT i = 0; i < iter->scale.size() - 1; i++)
	{
		if (time < (float)(iter->scale[i + 1].time))
			return i;
	}
	return -1;
}
int Interpolated::FindRot(AnimNode* iter, float time)
{
	if (iter->rotation.empty())
		return -1;

	for (UINT i = 0; i < iter->rotation.size() - 1; i++)
	{
		if (time < static_cast<float>(iter->rotation[i + 1].time))
			return i;
	}
	return -1;
}
int Interpolated::FindPos(AnimNode* iter, float time)
{
	if (iter->position.empty())
		return -1;

	for (UINT i = 0; i < iter->position.size() - 1; i++)
	{
		if (time < static_cast<float>(iter->position[i + 1].time))
			return i;
	}
	return -1;
}

클래스가 바뀜에 따라 Main의 헤더와 cpp파일도 변경되었습니다. 이후 obj나 fbx 파일과 애니메이션 파일을 로드라고 SlideInt를 하게 되면 프레임별로 애니메이션 효과를 볼 수 있습니다.

ImGui에서 SlideInt로 프레임을 조정함에 따라 움직이는 모션을 확인

읽어주셔서 감사합니다.

'공부 > 3D강의' 카테고리의 다른 글

230704 강의  (0) 2023.07.04
230703 강의  (0) 2023.07.03
230629 강의  (0) 2023.06.29
230628 강의  (0) 2023.06.28
230627 강의  (0) 2023.06.27