[DirectX] Res 매니저와 스마트 포인터
in Study on WinAPI&DirectX
Res(리소스) 매니저 클래스 구현하기
// 헤더파일
#pragma once
#include "CSingleton.h"
#include "ptr.h"
#include "CRes.h"
class CMesh;
class CResMgr :
public CSingleton<CResMgr>
{
private:
map<wstring, Ptr<CRes>> m_arrRes[(UINT)RES_TYPE::END];
public:
void init();
private:
void CreateDefaultMesh();
void CreateDefaultGraphicsShader();
public:
template<typename T>
Ptr<T> FindRes(const wstring& _strKey);
public:
CResMgr();
~CResMgr();
};
// T가 무엇인지 알아내는 함수(전역함수)
RES_TYPE GetResType()
{
const type_info& mesh = typeid(CMesh);
const type_info& meshdata = typeid(CMeshData);
const type_info& material = typeid(CMaterial);
const type_info& texture = typeid(CTexture);
const type_info& sound = typeid(CSound);
const type_info& prefab = typeid(CPrefab);
const type_info& gs = typeid(CGraphicsShader);
const type_info& cs = typeid(CComputeShader);
if (typeid(T).hash_code == mesh.hash_code())
return RES_TYPE::MESH;
if (typeid(T).hash_code == gs.hash_code())
return RES_TYPE::GRAPHICS_SHADER;
return RES_TYPE::END;
}
template<typename T>
inline Ptr<T> CResMgr::FindRes(const wstring& _strKey)
{
if (typeid(T).hash_code() == typeid(CMesh).hash_code())
{
map<wstring, Ptr<CRes>>::iterator iter = m_arrRes[(UINT)RES_TYPE::MESH].find(_strKey);
if (iter == m_arrRes[(UINT)RES_TYPE::MESH].end())
return nullptr;
return (T*)iter->second.Get();
}
return nullptr;
}
// cpp 파일
#include "pch.h"
#include "CResMgr.h"
#include "CMesh.h"
#include "CGraphicsShader.h"
CResMgr::CResMgr()
{
}
CResMgr::~CResMgr()
{
}
void CResMgr::init()
{
CreateDefaultMesh(); // 메쉬 생성
CreateDefaultGraphicsShader(); // 쉐이더 생성
}
void CResMgr::CreateDefaultMesh()
{
vector<Vtx> vecVtx;
vector<UINT> vecIdx;
Vtx v;
Ptr<CMesh> pMesh = nullptr;
// =============
// RectMesh 생성
// =============
// 0 --- 1
// | \ |
// 3 --- 2
v.vPos = Vec3(-0.5f, 0.5f, 0.5f);
v.vColor = Vec4(1.f, 0.f, 0.f, 1.f);
vecVtx.push_back(v);
v.vPos = Vec3(0.5f, 0.5f, 0.5f);
v.vColor = Vec4(0.f, 1.f, 0.f, 1.f);
vecVtx.push_back(v);
v.vPos = Vec3(0.5f, -0.5f, 0.5f);
v.vColor = Vec4(0.f, 0.f, 1.f, 1.f);
vecVtx.push_back(v);
v.vPos = Vec3(-0.5f, -0.5f, 0.5f);
v.vColor = Vec4(0.f, 0.f, 0.f, 1.f);
vecVtx.push_back(v);
vecIdx.push_back(0);
vecIdx.push_back(2);
vecIdx.push_back(3);
vecIdx.push_back(0);
vecIdx.push_back(1);
vecIdx.push_back(2);
pMesh = new CMesh;
pMesh->SetKey(L"RectMesh");
pMesh->Create(vecVtx.data(), (UINT)vecVtx.size(), vecIdx.data(), (UINT)vecIdx.size());
m_arrRes[(UINT)RES_TYPE::MESH].insert(make_pair(pMesh->GetKey(), pMesh.Get()));
}
void CResMgr::CreateDefaultGraphicsShader()
{
// Shader 생성
Ptr<CGraphicsShader> pShader = nullptr;
// Test Shader
pShader = new CGraphicsShader;
pShader->SetKey(L"TestShader");
pShader->CreateVertexShader(L"shader\\test.fx", "VS_Test");
pShader->CreatePixelShader(L"shader\\test.fx", "PS_Test");
m_arrRes[(UINT)RES_TYPE::GRAPHICS_SHADER].insert(make_pair(pShader->GetKey(), pShader.Get()));
}
Res 매니저에서 사용하기 위한 스마트 포인터 구현
스마트 포인터는 클래스가 아닌 헤더파일로 구현한다.
#pragma once
template<typename T>
class Ptr
{
private:
T* m_Res;
public:
T* Get() const { return m_Res; }
T* operator -> ()
{
return m_Res;
}
void operator = (T* _Res)
{
if (m_Res != nullptr)
m_Res->Release();
m_Res = _Res;
if (m_Res != nullptr)
m_Res->AddRef();
}
void operator = (const Ptr<T>& _Res)
{
if (m_Res != nullptr)
m_Res->Release();
m_Res = _Res;
if (m_Res != nullptr)
m_Res->AddRef();
}
// 스마트 포인터는 사실 객체이므로 nullptr과 비교하기 위해서
// 직접 구현을 해놓는다.
bool operator == (T* _Other)
{
return m_Res == _Other;
}
bool operator != (T* _Other)
{
return m_Res != _Other;
}
bool operator == (const Ptr<T>& _Other)
{
return m_Res == _Other.m_Res;
}
bool operator != (const Ptr<T>& _Other)
{
return m_Res != _Other.m_Res;
}
public:
Ptr()
: m_Res(nullptr)
{
}
Ptr(T* _Res)
: m_Res(_Res)
{
if (m_Res != nullptr)
m_Res->AddRef();
}
Ptr(const Ptr<T>& _Res)
: m_Res(_Res.m_Res)
{
if (m_Res != nullptr)
m_Res->AddRef();
}
~Ptr()
{
if (m_Res != nullptr)
{
m_Res->Release();
}
}
}
그리고 global.h에
#include <typeinfo>
를 추가한다.
CMeshRender에서의 코드 수정
메쉬와 쉐이더가 스마트 포인터를 사용하게 만들기 위해서 다음과 같이 코드를 작성한다.
// 헤더파일
#pragma once
#include "CComponent.h"
#include "CMesh.h"
#include "CGraphicsShader.h"
#include "ptr.h"
class CMeshRender :
public CComponent
{
private:
Ptr<CMesh> m_pMesh;
Ptr<CGraphicsShader> m_pShader;
public:
void SetMesh(Ptr<CMesh> _Mesh) { m_pMesh = _Mesh; }
void SetShader(Ptr<CGraphicsShader> _Shader) { m_pShader = _Shader; }
Ptr<CMesh> GetMesh() { return m_pMesh; }
Ptr<CGraphicsShader> GetShader() { return m_pShader; }
public:
virtual void finaltick() override;
void render();
CLONE(CMeshRender);
public:
CMeshRender();
~CMeshRender();
};
// cpp 파일
#include "pch.h"
#include "CMeshRender.h"
#include "CTransform.h"
CMeshRender::CMeshRender()
: CComponent(COMPONENT_TYPE::MESHRENDER)
, m_pMesh(nullptr)
, m_pShader(nullptr)
{
}
CMeshRender::~CMeshRender()
{
}
void CMeshRender::finaltick()
{
}
void CMeshRender::render()
{
if (nullptr == m_pMesh || nullptr == m_pShader)
return;
// Transform 에 UpdateData 요청
Transform()->UpdateData();
m_pShader->UpdateData();
m_pMesh->render();
}
Test.cpp에서의 동작 코드 작성
이제 동작 코드는 다음과 같이 작성된다.
CGameObject* g_Obj = nullptr;
void Init()
{
}
void Tick()
{
g_Obj->tick();
g_Obj->finaltick();
}
void Render()
{
g_Obj->render();
}
void Release()
{
delete g_Obj;
}
왼쪽에서 비교를 하게 만들고 싶을 때
nullptr == class 형태로 왼쪽에서 비교할 수 있도록 만들고 싶을 때.
// Ptr 헤더에서 전역 operator로 다음과 같이 작성한다.
template<typename T>
bool operator == (void* _Res, const Ptr<T>& _Ptr)
{
return _Res == _Ptr.Get();
}
template<typename T>
bool operator != (void* _Res, const Ptr<T>& _Ptr)
{
return _Res != _Ptr.Get();
}