03
02

이전에 FBX 파일을 읽어와 다이렉트 환경에서 렌더링을 했었다.

FBX는 광범위한 속성을 담을 수 있는 포맷으로

특히 애니메이션이 있을때 유용하다.

 

하지만 Obj 확장자는 매쉬, 텍스쳐, 메터리얼 3가지만 담는

단순한 구조이다. 그래서 외부 라이브러리 없이도

간단히 파싱해서 사용하기 좋다고 판단했다.

 

OBJ (wavefront file format)

 

아무 obj 파일 하나를 열어서 봐보자.

 

# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org
mtllib cube.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray.png
s off
f 5/1/1 1/2/1 4/3/1
f 5/1/1 4/3/1 8/4/1
f 3/5/2 7/6/2 8/7/2
f 3/5/2 8/7/2 4/8/2
f 2/9/3 6/10/3 3/5/3
f 6/10/4 7/6/4 3/5/4
f 1/2/5 5/1/5 2/9/5
f 5/1/6 6/10/6 2/9/6
f 5/1/7 8/11/7 6/10/7
f 8/11/7 7/12/7 6/10/7
f 1/2/8 2/9/8 3/13/8
f 1/2/8 3/13/8 4/14/8

 

v : 버텍스 로컬좌표

vt : 텍스쳐 로컬좌표

vn : 노말값 로컬좌표

f : (v, vt, vn) 인덱스

 

위 구조로 되어있으니, 이 데이터들을 파일입출력으로 읽어와

다이렉트 버퍼들을 넘겨주면 된다.

본 포스팅은 간단하게 버텍스 버퍼로만 렌더링 해보겠다.

 

1. 파일 불러오기

 

FILE* fp = nullptr;
	_tfopen_s(&fp, objfile.c_str(), _T("rt"));
	if (fp == NULL)
	{
		//파일이 없음
		return false;
	}

 

2. 파일의 데이터 배열에 저장

 

//실패인 경우 0 리턴 EOF은 -1
	//한줄을 읽어옴
	while (_fgetts(buffer, _countof(buffer), fp)!=0)
	{
		TCHAR type[6] = { 0, };
		TCHAR value[256] = { 0, };

		_stscanf_s(buffer, _T("%s"), type,(unsigned int)_countof(type));
		//문자열이 같은 지 체크하는 함수 strcmp 와 같음
		if (_tcscmp(type, L"v") == 0)
		{
			KVector3 vertex;
			_stscanf_s(buffer, _T("%s %f %f %f\n"), value, (unsigned int)_countof(value),
				&vertex.x, &vertex.y, &vertex.z);
			m_vlist.push_back(vertex);
		}
		else if (_tcscmp(type, L"vt") == 0)
		{
			KVector2 uv;
			_stscanf_s(buffer, _T("%s %f %f \n"), value, (unsigned int)_countof(value),
				&uv.x, &uv.y);
			m_vtlist.push_back(uv);
		}
		else if (_tcscmp(type, L"vn") == 0) {
			KVector3 normal;
			_stscanf_s(buffer, _T("%s %f %f %f\n"), value, (unsigned int)_countof(value),
				&normal.x, &normal.y, &normal.z);
			m_vnlist.push_back(normal);
		}
	
		else if (_tcscmp(type, L"f") == 0) {
			std::string vertex1, vertex2, vertex3;
			unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
			int matches = _stscanf_s(buffer, _T("%s %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n"),
				value, (unsigned int)_countof(type),
				&vertexIndex[0], &uvIndex[0], &normalIndex[0],
				&vertexIndex[1], &uvIndex[1], &normalIndex[1],
				&vertexIndex[2], &uvIndex[2], &normalIndex[2]);
			if (matches != 10) {
				printf("File can't be read by our simple parser \n");
				continue;
			}
			m_vertexindex.push_back(vertexIndex[0]);
			m_vertexindex.push_back(vertexIndex[1]);
			m_vertexindex.push_back(vertexIndex[2]);
			m_uvindex.push_back(uvIndex[0]);
			m_uvindex.push_back(uvIndex[1]);
			m_uvindex.push_back(uvIndex[2]);
			m_normalindex.push_back(normalIndex[0]);
			m_normalindex.push_back(normalIndex[1]);
			m_normalindex.push_back(normalIndex[2]);
		}
	}
	

	fclose(fp);
	return true;
}

 

 

길지만 간단하다. 한줄 한줄 읽는 반복문안에서

먼저 앞 알파벳을 읽어 오고

v, vt, vn, f일때 구별해서 각 배열에 넣어줄 뿐이다.

 

3. 버텍스 데이터 채우기

 

	for (unsigned int index = 0; index < m_vertexindex.size(); index++)
	{
		unsigned int vertexindex = m_vertexindex[index];
		unsigned int uvindex = m_uvindex[index];
		unsigned int normalindex = m_normalindex[index];

		PNCT_VERTEX pnct;
		pnct.pos = m_vlist[vertexindex-1];
		pnct.tex = m_vtlist[uvindex - 1];
		pnct.normal = m_vnlist[normalindex - 1];
		m_VertexList.push_back(pnct);
	}
	return true;

 

최종으로 버텍스버퍼로 넘어갈 배열에 넣어준다.

간단히 렌더링하기 위해 인덱스 순서대로, 해당 좌표를 넣어준다.

바람직한 방법은 아니다. 출력이 되는지 확인하기 위해 사용했다.

 

4. 결과

 

 

만약에 출력된 오브젝트의 텍스쳐와 좌표가 거울로 본 것 처럼 반대로 되어있다면

모델링 프로그램에서 Export할때, 방향을 제대로 설정했는지 확인한다.

Direct는 왼손좌표계를 사용하고 대부분의 모델링 프로그램은

오른손좌표계를 사용함으로 좌표계를 변환해야한다.

 

COMMENT