ResourceManager
사용하고 있는 Resource들을 관리해주는 매니저이다.
전반적인 사용방법은 Component처럼 리소스들을 클래스 형태로 유동적이게 갖고온다.
IResource.h
#pragma once
enum class ResourceType : uint
{
Unknown,
Animation,
Texture2D,
Mesh,
Material
};
class IResource
{
public:
template <typename T>
static constexpr ResourceType DeduceResourceType();
public:
IResource(class Context* const context, const ResourceType type = ResourceType::Unknown);
virtual ~IResource() = default;
virtual bool SaveToFile(const std::string& path) = 0;
virtual bool LoadFromFile(const std::string& path) = 0;
const ResourceType& GetResourceType() const { return resource_type; }
const std::string& GetResourceName() const { return resource_name; }
const std::string& GetResourcePath() const { return resource_path; }
void SetResourceName(const std::string& name) { this->resource_name = name; }
void SetResourcePath(const std::string path) { this->resource_path = path; }
protected:
class Context* context = nullptr;
ResourceType resource_type = ResourceType::Unknown;
std::string resource_name;
std::string resource_path;
};
리소스 인터페이스이다.
일단 여러가지 타입을 만들었다 지금은 애니메이션만 사용할것.
IResource.cpp
#include "stdafx.h"
#include "IResourece.h"
IResource::IResource(Context* const context, const ResourceType type)
: context(context)
, resource_type(type)
{
}
template<typename T>
inline constexpr ResourceType IResource::DeduceResourceType()
{
return ResourceType::Unknown;
}
// explicite template instantiation
#define REGISTER_RESOURCE_TYPE(T, enum_type) template <>ResourceType IResource::DeduceResourceType<T>(){ return enum_type; }
REGISTER_RESOURCE_TYPE(Animation, ResourceType::Animation)
이런 식으로 Component 형태와 매우 유사하게 작성한다.
그리고 기존에 사용하던 Animation클래스에 IResource 클래스를 상속시킨다.
Resource.h
#pragma once
#include "ISubsystem.h"
#include "Resource/IResourece.h"
#include <filesystem>
enum class AssetType : uint
{
Texture,
Shader,
Animation
};
class ResourceManager final : public ISubsystem
{
public:
ResourceManager(class Context* context);
~ResourceManager() = default;
bool Initialize() override;
void Update() override;
/*
* Resource
*/
template <typename T>
const std::shared_ptr<T> Load(const std::string& path);
template <typename T>
const std::shared_ptr<T> GetResourceFromName(const std::string& name);
template <typename T>
const std::shared_ptr<T> GetResourceFromPath(const std::string& path);
template <typename T>
void RegisterResource(const std::shared_ptr<T>& resource);
bool HasResource(const std::string& resource_name, const ResourceType& resource_type);
/*
* Directory
*/
const std::string& GetAssetDirectory() const { return "_Assets/"; }
const std::string& GetAssetDirectory(const AssetType& type);
private:
void RegisterDirectory(const AssetType& type, const std::string& directory);
private:
using resource_group_t = std::vector<std::shared_ptr<IResource>>;
std::map<ResourceType, resource_group_t> resource_groups;
std::mutex resource_mutex;
std::map<AssetType, std::string> asset_directories;
};
그리고 리소스 매니저를 만들어 현재 이름과 경로로 갖고오는 방법으로 만든다.
ResourceManager.cpp
#include "stdafx.h"
#include "ResourceManager.h"
ResourceManager::ResourceManager(Context* context)
: ISubsystem(context)
{
std::string root_directory = GetAssetDirectory();
RegisterDirectory(AssetType::Animation, root_directory + "Animation/");
RegisterDirectory(AssetType::Shader, root_directory + "Shader/");
RegisterDirectory(AssetType::Texture, root_directory + "Texture/");
}
bool ResourceManager::Initialize()
{
return true;
}
void ResourceManager::Update()
{
}
bool ResourceManager::HasResource(const std::string& resource_name, const ResourceType& resource_type)
{
for (const auto& resource : resource_groups[resource_type])
{
if (resource->GetResourceName() == resource_name)
{
return true;
}
}
return false;
}
const std::string& ResourceManager::GetAssetDirectory(const AssetType& type)
{
return asset_directories[type];
}
void ResourceManager::RegisterDirectory(const AssetType& type, const std::string& directory)
{
asset_directories[type] = directory;
}
이런 식으로 현재 디렉토리 루트를 먼저 설정해주고 리소스를 갖고 있느지 확인 그리고 현재 디렉토리에 추가하고 갖고오는 함수를 만든다.
ResourceManager.h template
template<typename T>
inline const std::shared_ptr<T> ResourceManager::Load(const std::string& path)
{
static_assert(std::is_base_of<IResource, T>::value, "Provided type dose not implement IComponent");
if (!std::filesystem::exists(path))
{
assert(false);
return nullptr;
}
auto last_index = path.find_first_not_of("\\/");
auto file_name = path.substr(last_index + 1, path.length());
last_index = file_name.find_last_of('.');
std::string resource_name = file_name.substr(0, last_index);
if (HasResource(resource_name, IResource::DeduceResourceType<T>()))
{
return GetResourceFromName<T>(resource_name);
}
std::shared_ptr<T> resource = std::make_shared<T>(context);
resource->SetResourceName(resource_name);
if (!resource->LoadFromFile(path))
{
assert(false);
return nullptr;
}
RegisterResource<T>(resource);
return resource;
}
template<typename T>
inline const std::shared_ptr<T> ResourceManager::GetResourceFromName(const std::string& name)
{
static_assert(std::is_base_of<IResource, T>::value, "Provided type dose not implement IComponent");
for (const auto& resource : resource_groups[IResource::DeduceResourceType<T>()])
{
if (resource->GetResourceName() == name)
{
return std::static_pointer_cast<T>(resource);
}
}
return nullptr;
}
template<typename T>
inline const std::shared_ptr<T> ResourceManager::GetResourceFromPath(const std::string& path)
{
static_assert(std::is_base_of<IResource, T>::value, "Provided type dose not implement IComponent");
for (const auto& resource : resource_groups[IResource::DeduceResourceType<T>()])
{
if (resource->GetResourcePath() == path)
{
return std::static_pointer_cast<T>(resource);
}
}
return std::shared_ptr<T>();
}
template<typename T>
inline void ResourceManager::RegisterResource(const std::shared_ptr<T>& resource)
{
static_assert(std::is_base_of<IResource, T>::value, "Provided type dose not implement IComponent");
if (!resource)
{
assert(false);
return;
}
std::lock_guard<std::mutex> guard(resource_mutex);
resource_groups[resource->GetResourceType()].emplace_back(resource);
}
많이 복잡해 보이는데 일반적으로 Component 형태와 비슷하다.
하지만 Load부분에 파일이 있는지 없는지 검사하는 부분에서 경로 이름을 잘라서 검사한다.
강좌는 여기서 끝이다. 이것저것 생각하면 아직 넣을게 많은데 조금 아쉽다.
현재 프레임마다 갱신하여 Time반환해주고 그 프레임 고정으로 하는 방법 하고,
LOG시스템까지 알려줄 주 알았는데 좀 아쉽다.
다음엔 DirectX3D이다