#pragma once
enum FileMode :uint8
{ // 읽기, 쓰기
Write,
Read
};
class FileUtils
{
public:
FileUtils();
~FileUtils();
void Open(wstring filePath, FileMode mode);
////////// Write //////////
template<typename T>
void Write(const T& data)
{
DWORD numOfBytes = 0;
assert(::WriteFile(_handle, &data, sizeof(T), (LPDWORD)&numOfBytes, nullptr));
}
// 템플릿특수화 - 예외적 상황가정(템플릿을 사용하겠지만, string이 들어갈 경우에는 이 템프릿을 사용하겠다)
template<>
void Write<string>(const string& data)
{
return Write(data);
}
void Write(void* data, uint32 dataSize);
void Write(const string& data);
////////// Write //////////
////////// Read //////////
template<typename T>
void Read(OUT T& data)
{
DWORD numOfBytes = 0;
assert(::ReadFile(_handle, &data, sizeof(T), (LPDWORD)&numOfBytes, nullptr));
}
template<typename T>
T Read()
{
T data;
Read(data);
return data;
}
void Read(void** data, uint32 dataSize);
void Read(OUT string& data);
////////// Read //////////
private:
HANDLE _handle = INVALID_HANDLE_VALUE;
};
#include "pch.h"
#include "FileUtils.h"
FileUtils::FileUtils()
{
}
FileUtils::~FileUtils()
{
if (_handle != INVALID_HANDLE_VALUE)
{
::CloseHandle(_handle);
_handle = INVALID_HANDLE_VALUE;
}
}
void FileUtils::Open(wstring filePath, FileMode mode)
{
if (mode == FileMode::Write)
{
_handle = ::CreateFile(
filePath.c_str(),
GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
}
else
{
_handle = ::CreateFile(
filePath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
}
assert(_handle != INVALID_HANDLE_VALUE);
}
void FileUtils::Write(void* data, uint32 dataSize)
{
uint32 numOfBytes = 0;
assert(::WriteFile(_handle, data, dataSize, reinterpret_cast<LPDWORD>(&numOfBytes), nullptr));
}
void FileUtils::Write(const string& data)
{
uint32 size = (uint32)data.size();
Write(size);
if (data.size() == 0)
return;
Write((void*)data.data(), size);
}
void FileUtils::Read(void** data, uint32 dataSize)
{
uint32 numOfBytes = 0;
assert(::ReadFile(_handle, *data, dataSize, reinterpret_cast<LPDWORD>(&numOfBytes), nullptr));
}
void FileUtils::Read(OUT string& data)
{
uint32 size = Read<uint32>();
if (size == 0)
return;
char* temp = new char[size + 1];
temp[size] = 0;
Read((void**)&temp, size);
data = temp;
delete[] temp;
}
Bone과 Mesh정보를 하나로 모아, 모델 정보를 저장하는 함수 만들기void Converter::WriteModelFile(wstring finalPath)
{
auto path = filesystem::path(finalPath);
// 폴더가 없으면 만든다.
filesystem::create_directory(path.parent_path());
shared_ptr<FileUtils> file = make_shared<FileUtils>();
file->Open(finalPath, FileMode::Write);
// Bone Data
file->Write<uint32>(_bones.size());
for (shared_ptr<asBone>& bone : _bones)
{
// 순서랑 자료형 잘 맞추기
file->Write<int32>(bone->index);
file->Write<string>(bone->name);
file->Write<int32>(bone->parent);
file->Write<Matrix>(bone->transform);
}
// Mesh Data
file->Write<uint32>(_meshes.size());
for (shared_ptr<asMesh>& meshData : _meshes)
{
file->Write<string>(meshData->name);
file->Write<int32>(meshData->boneIndex);
file->Write<string>(meshData->materialName);
// Vertex Data
file->Write<uint32>(meshData->vertices.size());
file->Write(&meshData->vertices[0], sizeof(VertexType) * meshData->vertices.size());
// Index Data
file->Write<uint32>(meshData->indices.size());
file->Write(&meshData->indices[0], sizeof(uint32) * meshData->indices.size());
}
}
Bone Mesh정보를 가지게 될 Model과 ModelMesh클래스 생성하기struct ModelBone
{
wstring name;
int32 index;
int32 parentIndex;
shared_ptr<ModelBone> parent; // Cache
Matrix transform;
vector<shared_ptr<ModelBone>> children; // Cache
};
struct ModelMesh
{
void CreateBuffers();
wstring name;
// Mesh
shared_ptr<Geometry<ModelVertexType>> geometry = make_shared<Geometry<ModelVertexType>>();
shared_ptr<VertexBuffer> vertexBuffer;
shared_ptr<IndexBuffer> indexBuffer;
// Material
wstring materialName = L"";
shared_ptr<Material> material; // Cache
// Bones
int32 boneIndex;
shared_ptr<ModelBone> bone; // Cache;
};
void ModelMesh::CreateBuffers()
{
vertexBuffer = make_shared<VertexBuffer>();
vertexBuffer->Create(geometry->GetVertices());
indexBuffer = make_shared<IndexBuffer>();
indexBuffer->Create(geometry->GetIndices());
}
#pragma once
struct ModelBone;
struct ModelMesh;
class Model : public enable_shared_from_this<Model>
{
public:
Model();
~Model();
public:
// Custom -> Memory
void ReadMaterial(wstring filename);
void ReadModel(wstring filename);
// 헬퍼 함수들 (개수, 전체(Material, Mesh, Bone), Index로 찾기, Name으로 찾기)
uint32 GetMaterialCount() { return static_cast<uint32>(_materials.size()); }
vector<shared_ptr<Material>>& GetMaterials() { return _materials; }
shared_ptr<Material> GetMaterialByIndex(uint32 index) { return _materials[index]; }
shared_ptr<Material> GetMaterialByName(const wstring& name);
uint32 GetMeshCount() { return static_cast<uint32>(_meshes.size()); }
vector<shared_ptr<ModelMesh>>& GetMeshes() { return _meshes; }
shared_ptr<ModelMesh> GetMeshByIndex(uint32 index) { return _meshes[index]; }
shared_ptr<ModelMesh> GetMeshByName(const wstring& name);
uint32 GetBoneCount() { return static_cast<uint32>(_bones.size()); }
vector<shared_ptr<ModelBone>>& GetBones() { return _bones; }
shared_ptr<ModelBone> GetBoneByIndex(uint32 index) { return (index < 0 || index >= _bones.size() ? nullptr : _bones[index]); }
shared_ptr<ModelBone> GetBoneByName(const wstring& name);
// 헬퍼 함수들
private:
// 캐쉬정보 채워주기
void BindCacheInfo();
private:
wstring _modelPath = L"../Resources/Models/";
wstring _texturePath = L"../Resources/Textures/";
private:
shared_ptr<ModelBone> _root;
vector<shared_ptr<Material>> _materials;
vector<shared_ptr<ModelBone>> _bones;
vector<shared_ptr<ModelMesh>> _meshes;
};
#include "pch.h"
#include "Model.h"
#include "tinyxml2.h"
#include <filesystem>
#include "Utils.h"
#include "FileUtils.h"
#include "Material.h"
#include "ModelMesh.h"
Model::Model() { }
Model::~Model() { }
void Model::ReadMaterial(wstring filename)
{
wstring fullPath = _texturePath + filename + L".xml";
auto parentPath = filesystem::path(fullPath).parent_path();
tinyxml2::XMLDocument* document = new tinyxml2::XMLDocument();
tinyxml2::XMLError error = document->LoadFile(Utils::ToString(fullPath).c_str());
assert(error == tinyxml2::XML_SUCCESS);
tinyxml2::XMLElement* root = document->FirstChildElement();
tinyxml2::XMLElement* materialNode = root->FirstChildElement();
while (materialNode)
{
shared_ptr<Material> material = make_shared<Material>();
tinyxml2::XMLElement* node = nullptr;
node = materialNode->FirstChildElement();
material->SetName(Utils::ToWString(node->GetText()));
// Diffuse Texture
node = node->NextSiblingElement();
if (node->GetText())
{
wstring textureStr = Utils::ToWString(node->GetText());
if (textureStr.length() > 0)
{
auto texture = RESOURCES->GetOrAddTexture(textureStr, (parentPath / textureStr).wstring());
material->SetDiffuseMap(texture);
}
}
// Specular Texture
node = node->NextSiblingElement();
if (node->GetText())
{
wstring textureStr = Utils::ToWString(node->GetText());
if (textureStr.length() > 0)
{
auto texture = RESOURCES->GetOrAddTexture(textureStr, (parentPath / textureStr).wstring());
material->SetSpecularMap(texture);
}
}
// Normal Texture
node = node->NextSiblingElement();
if (node->GetText())
{
wstring textureStr = Utils::ToWString(node->GetText());
if (textureStr.length() > 0)
{
auto texture = RESOURCES->GetOrAddTexture(textureStr, (parentPath / textureStr).wstring());
material->SetNormalMap(texture);
}
}
// Ambient
{
node = node->NextSiblingElement();
Color color;
color.x = node->FloatAttribute("R");
color.y = node->FloatAttribute("G");
color.z = node->FloatAttribute("B");
color.w = node->FloatAttribute("A");
material->GetMaterialDesc().ambient = color;
}
// Diffuse
{
node = node->NextSiblingElement();
Color color;
color.x = node->FloatAttribute("R");
color.y = node->FloatAttribute("G");
color.z = node->FloatAttribute("B");
color.w = node->FloatAttribute("A");
material->GetMaterialDesc().diffuse = color;
}
// Specular
{
node = node->NextSiblingElement();
Color color;
color.x = node->FloatAttribute("R");
color.y = node->FloatAttribute("G");
color.z = node->FloatAttribute("B");
color.w = node->FloatAttribute("A");
material->GetMaterialDesc().specular = color;
}
// Emissive
{
node = node->NextSiblingElement();
Color color;
color.x = node->FloatAttribute("R");
color.y = node->FloatAttribute("G");
color.z = node->FloatAttribute("B");
color.w = node->FloatAttribute("A");
material->GetMaterialDesc().emissive = color;
}
_materials.push_back(material);
// Next Material
materialNode = materialNode->NextSiblingElement();
}
BindCacheInfo();
}
void Model::ReadModel(wstring filename)
{
wstring fullPath = _modelPath + filename + L".mesh";
shared_ptr<FileUtils> file = make_shared<FileUtils>();
file->Open(fullPath, FileMode::Read);
// WriteModel해주었던 부분과 동일한 순서와 자료형으로 작성해줘야 꼬이지 않음
// Bones
{
const uint32 count = file->Read<uint32>();
for (uint32 i = 0; i < count; i++)
{
shared_ptr<ModelBone> bone = make_shared<ModelBone>();
bone->index = file->Read<int32>();
bone->name = Utils::ToWString(file->Read<string>());
bone->parentIndex = file->Read<int32>();
bone->transform = file->Read<Matrix>();
_bones.push_back(bone);
}
}
// Mesh
{
const uint32 count = file->Read<uint32>();
for (uint32 i = 0; i < count; i++)
{
shared_ptr<ModelMesh> mesh = make_shared<ModelMesh>();
mesh->name = Utils::ToWString(file->Read<string>());
mesh->boneIndex = file->Read<int32>();
// Material
mesh->materialName = Utils::ToWString(file->Read<string>());
//VertexData
{
const uint32 count = file->Read<uint32>();
vector<ModelVertexType> vertices;
vertices.resize(count);
void* data = vertices.data();
file->Read(&data, sizeof(ModelVertexType) * count);
mesh->geometry->AddVertices(vertices);
}
//IndexData
{
const uint32 count = file->Read<uint32>();
vector<uint32> indices;
indices.resize(count);
void* data = indices.data();
file->Read(&data, sizeof(uint32) * count);
mesh->geometry->AddIndices(indices);
}
mesh->CreateBuffers();
_meshes.push_back(mesh);
}
}
BindCacheInfo();
}
std::shared_ptr<Material> Model::GetMaterialByName(const wstring& name)
{
for (auto& material : _materials)
{
if (material->GetName() == name)
return material;
}
return nullptr;
}
std::shared_ptr<ModelMesh> Model::GetMeshByName(const wstring& name)
{
for (auto& mesh : _meshes)
{
if (mesh->name == name)
return mesh;
}
return nullptr;
}
std::shared_ptr<ModelBone> Model::GetBoneByName(const wstring& name)
{
for (auto& bone : _bones)
{
if (bone->name == name)
return bone;
}
return nullptr;
}
void Model::BindCacheInfo()
{
// Mesh에 Material 캐싱
for (const auto& mesh : _meshes)
{
// 이미 찾았으면 스킵
if (mesh->material != nullptr)
continue;
mesh->material = GetMaterialByName(mesh->materialName);
}
// Mesh에 Bone 캐싱
for (const auto& mesh : _meshes)
{
// 이미 찾았으면 스킵
if (mesh->bone != nullptr)
continue;
mesh->bone = GetBoneByIndex(mesh->boneIndex);
}
// Bone 계층 정보 채우기
if (_root == nullptr && _bones.size() > 0)
{
_root = _bones[0];
for (const auto& bone : _bones)
{
if (bone->parentIndex >= 0)
{
bone->parent = _bones[bone->parentIndex];
bone->parent->children.push_back(bone);
}
else
{
bone->parent = nullptr;
}
}
}
}
#pragma once
#include "Component.h"
class Model;
class Shader;
class Material;
class ModelRenderer : public Component
{
using Super = Component;
public:
ModelRenderer(shared_ptr<Shader> shader);
virtual ~ModelRenderer();
virtual void Update() override;
void SetModel(shared_ptr<Model> model);
void SetPass(uint8 pass) { _pass = pass; }
private:
shared_ptr<Shader> _shader;
uint8 _pass = 0;
shared_ptr<Model> _model;
};
#include "pch.h"
#include "ModelRenderer.h"
#include "Material.h"
#include "ModelMesh.h"
#include "Model.h"
ModelRenderer::ModelRenderer(shared_ptr<Shader> shader)
:Super(ComponentType::ModelRenderer), _shader(shader)
{
}
ModelRenderer::~ModelRenderer()
{
}
void ModelRenderer::Update()
{
if (_model == nullptr)
return;
auto world = GetTransform()->GetWorldMatrix();
RENDER->PushTransformData(TransformDesc{ world });
const auto& meshes = _model->GetMeshes();
for (auto& mesh : meshes)
{
if (mesh->material)
mesh->material->Update();
uint32 stride = mesh->vertexBuffer->GetStride();
uint32 offset = mesh->vertexBuffer->GetOffset();
DC->IASetVertexBuffers(0, 1, mesh->vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
DC->IASetIndexBuffer(mesh->indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
_shader->DrawIndexed(0, _pass, mesh->indexBuffer->GetCount(), 0, 0);
}
}
void ModelRenderer::SetModel(shared_ptr<Model> model)
{
_model = model;
const auto& materials = _model->GetMaterials();
for (auto& material : materials)
{
material->SetShader(_shader);
}
}
14.NormalMapping.fx와 크게 달라지지 않고, RasterizerState가 추가된 부분과 Color값을 단순하게만 수정float4 PS(MeshOutput input) : SV_TARGET
{
//ComputeNormalMapping(input.normal, input.tangent, input.uv);
//float4 color = ComputeLight(input.normal, input.uv, input.worldPosition);
float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
return color;
}
float4 PS_RED(MeshOutput input) : SV_TARGET
{
return float4(1, 0, 0, 1);
}
technique11 T0
{
PASS_VP(P0, VS, PS)
PASS_RS_VP(P1, FillModeWireFrame, VS, PS_RED)
}
#define PASS_RS_VP(name, rs, vs, ps) \
pass name \
{ \
SetRasterizerState(rs); \
SetVertexShader(CompileShader(vs_5_0, vs())); \
SetPixelShader(CompileShader(ps_5_0, ps())); \
}
18.NormalMappingDemo와 동일CreateTower()함수를 생성하기void StaticMeshDemo::CreateTower()
{
// CustomData -> Memory
shared_ptr<Model> m1 = make_shared<Model>();
m1->ReadModel(L"Tower/Tower");
m1->ReadMaterial(L"Tower/Tower");
_obj = make_shared<GameObject>();
_obj->GetOrAddTransform()->SetPosition(Vec3(0, 0, 50));
_obj->GetOrAddTransform()->SetScale(Vec3(1.f));
_obj->GetOrAddTransform()->SetRotation(Vec3(3.14f / 2, 0, 0));
_obj->AddComponent(make_shared<ModelRenderer>(_shader));
{
_obj->GetModelRenderer()->SetModel(m1);
_obj->GetModelRenderer()->SetPass(1);
}
}


https://www.inflearn.com/courses/lecture?courseId=329791&type=LECTURE&unitId=161141