그전에 렌더링 과정을 좀 개선해야할 필요성을 느꼈다. 나도 Material 쓰고 싶다고!
먼저 Renderer에서 필요한 리소스를 모두 들고 있는게 마음에 안 들었다. 다른 곳에서 해당 리소스에 접근하려면 무조건 Renderer에게 말을 걸어야 했기 때문이다.
그러니까 아예 파일을 관리하는 file manager를 제작하기로 했다.
Shader live compile과 Shader 초기화 방법 : https://stackoverflow.com/questions/50155656/creating-dx11-shaders-from-string
일단 파일들을 탐색하는 방법은 io.h 헤더를 포함시키고 다음과 같이 파일을 찾아낼 수 있다.
void FileManager::Search(std::string path)
{
std::string dirPath = path + "\\*.*";
struct _finddata_t fileData;
intptr_t handle;
handle = _findfirst(dirPath.c_str(), &fileData);
if (handle == -1L) { CONSOLE(">>File Search :: No file in directory."); return; }
CONSOLE(">>File Search :: Find " + dirPath);
bool isFile;
do {
isFile = IsFile(fileData);
if (fileData.name[0] != '.')
{
std::string pFile = path + "\\" + fileData.name;
if (isFile) files.insert(std::make_pair(fileData.name, pFile));
else Search(pFile);
}
} while (!_findnext(handle, &fileData));
_findclose(handle);
}
Asset 폴더 내부에 있는 모든 파일들에 대한 이름과 경로를 files라는 hash map에 저장하고 있다. 64비트인지 32비트인지에 따라 _finddata_t 구조체의 선언이 바뀌기 때문에 매크로 형태로 되어 있었다. 그리고 처음 검색하는 디렉토리에 대한 핸들을 저장하기 위해 handle을 선언한 후 _findfirst 함수를 호출하여 초기화 해준다. 이때 핸들 값이 -1이면 디렉토리 내부에 아무것도 없다는 뜻이므로 리턴하도록 해준 후, 본격적으로 탐색을 시작한다. 이때 찾아낸 data가 폴더인지 파일인지 판정해야 다음 작업을 수행하는데, 그래서 호출해주는 IsFile 함수는 다음과 같이 생겼다.
bool FileManager::IsFile(_finddata_t fd)
{
return fd.attrib & _A_SUBDIR ? false : true;
}
이렇게 서브 디렉토리인 경우 다시 재귀호출을 통해 폴더 내부를 샅샅이 뒤져서 file 목록을 만드는 것이다.
물론 file에 대해서 바로 작업을 할 수도 있겠지만. 어떤 게 더 효율이 좋을 지는 모르겠다.
자 이제 file 목록을 가져왔으니 이 파일이 Texture인지, Model인지, Shader 인지 판정을 해주어야 한다.
string.substr함수를 이용해서 '.'을 기준으로 쪼개면 확장자를 얻을 수 있는데, 다음과 같이 될 것이다.
std::string FileManager::GetExtension(std::string file)
{
return file.substr(file.find_last_of(".") + 1);
}
자 그러면 이제 확장자도 얻었고, file이름만 넘겨주면 이제 이게 어떤 파일인지 인식하여 그에 맞는 클래스의 생성자를 호출하도록 만들면 된다. 근데 VertexShader와 PixelShader의 경우 바이너리 코드의 확장자가 cso로 똑같기 때문에 2개를 구분지을 수가 없다. 확장자를 따로 써주거나, 아니면 Shader 저장시 V, P 등을 첫 글자에 넣어서 이게 vertex shader 인지, Pixel Shader인지 구분하는 방법이 있겠다. 프로젝트 세팅에서 hlsl을 보면 셰이더 목적파일의 확장자를 세팅할 수는 있지만 셰이더마다 따로 설정하는 부분이 존재하지 않아 일단 이름으로 구분지어 보도록 한다.
그리고 void*로 저장된 데이터를 불러오기 위한 함수를 어떻게 해야할까 고민했다.
내가 원하는 동작은 함수에 string을 전달하면 그 tsring에 맞는 클래스의 포인터를 리턴하도록 하는 것이다.
즉, 나는 texture를 얻고 싶으면 그냥 템플릿이면 내가 함수에 2가지 정보를 넘겨줘야 하는데, 그러고 싶지 않고 오직 string 만 전달해서 원하는 값을 얻고 싶은 것이다. 하지만 템플릿 함수여야 리턴 타입을 여러 개 동적으로 지정할 수 있기 때문에 내가 원하는 클래스형태를 넘기지 않고 값을 얻을 방법이 없을까 고민 끝에 결정한 것은 다음과 같다.
template<typename T>
void Get(std::string fileName, T* out){
out = dynamic_cast<T>(pDatas[fileName].get());
if (!out)
{
MSG_EXCEPT("File Type Not Matching! : " + typeid(T).name()+ " : " + fileName);
}
}
핵심은 템플릿 함수를 만들었을 때 굳이 <>안에 인수를 전달하지 않아도 매개변수에 해당 타입이 명시가 되어있으면 자동으로 그에 맞는 템플릿 함수가 생성된다는 것을 이용한 것이다.
예제를 하나 든다면 아래와 같다.
template <typename T>
auto DoSomething(T* type) -> std::unique_ptr<T>
{
return std::unique_ptr<T>(type);
}
//이후 단순히 아래와 같이 써도 잘 동작한다.
int * a = new int(5);
auto uPtr = DoSomething(a);
좀 허접하지만 어쨌든 이제 Asset 파일 내부에 있는 모든 파일에 대해서 핸들링할 수 있게 되었다. 이제 이 정보를 Renderer에게 전달하여 렌더링 작업을 수행하도록 해보자.
음, 이렇게 하면 될 줄 알았는데, 컴파일이 안된다. 뭐가 문제인건지 모르겠다. 강제 형 변환이 안되는 건가..?
static_cast도 reinterpret_cast도 모두 안 먹힌다. 해결이 안된다. 내일 다시 고민해보자.
'진행과정 기록 > GameEngine' 카테고리의 다른 글
20200207 Picking (0) | 2020.02.07 |
---|---|
20200206 어떻게 다른 타입의 클래스포인터를 한곳에 저장하고, 또 꺼낼 수 있을까? (0) | 2020.02.06 |
20200204 Model 임포트(Re) 하... (0) | 2020.02.04 |
20200204 'dll이 없어 실행할 수 없습니다.' 오류 해결 (0) | 2020.02.04 |
20200203 Model (Assimp사용하여 임포팅) (0) | 2020.02.03 |