노말 벡터(Nomrals, Binomrals, Tangents)
아래와 같은 레이어 요소는 모두 읽는 방법이 같습니다.
- Normals (FbxLayerElementNormal)
- Binormals (FbxLayerElementBinormal)
- Tangents (FbxLayerElementTangent)
/* 생략 */
vec3* positions = nullptr; // 정점 위치 리스트
ProcessControlPoints(mesh); // 제어점으로부터 위치 리스트를 채운다.
unsigned int triCount = mesh->GetPolygonCount(); // 메쉬의 삼각형 개수를 가져온다.
unsigned int vertexCount = 0; // 정점의 개수
for(unsigned int i = 0; i < triCount; ++i) // 삼각형의 개수
{
for(unsigned int j = 0; j < 3; ++j) // 삼각형은 세 개의 정점으로 구성
{
int controlPointIndex = mesh->GetPolygonVertex(i, j); // 제어점 인덱스를 가져온다.
vec3& position = positions[controlPointIndex]; // 현재 정점에 대한 위치
vec3 normal = ReadNormal (mesh, controlPointIndex, vertexCounter);
vec3 binormal = ReadBinormal(mesh, controlPointIndex, vertexCounter);
vec3 tangent = ReadTangent (mesh, controlPointIndex, vertexCounter);
vertexCount++; // 정점의 개수++
}
}
각 레이어 요소를 읽은 후 파싱하는 기능을 가진 함수(ReadNormal, ReadBinormal, ReadTangent)를 만듭니다.
vec3 ReadTangent(FbxMesh* mesh, int controlPointIndex, int vertexCounter)
//vec3 ReadNormal
//vec3 RadeBinormal
{
//if (mesh->GetElementNormalCount() < 1)
//if (mesh->GetElementBinormalCount() < 1)
if (mesh->GetElementTangentCount() < 1)
std::cout << "Invalid ****** Number" << std::endl;
//FbxGeometryElementNormal* vertexNormal = mesh->GetElementNormal(0);
//FbxGeometryElementBinormal* vertexBinormal = mesh->GetElementBinormal(0);
FbxGeometryElementTangent* vertexTangent = mesh->GetElementTangent(0);
vec3 result;
/* 생략 */
return result;
}
생략부분 에서는 각 레이어의 매핑모드와 참조모드에 따라 처리합니다.
//switch (vertexNormal->GetMappingMode())
switch (vertexBinormal->GetMappingMode())
//switch (vertexTangnet->GetMappingMode())
{
case FbxGeometryElement::eByControlPoint:
switch (vertexBinormal->GetReferenceMode())
{
case FbxGeometryElement::eDirect:
{
result.x = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(controlPointIndex).mData[0]);
result.y = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(controlPointIndex).mData[1]);
result.z = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(controlPointIndex).mData[2]);
}
break;
case FbxGeometryElement::eIndexToDirect:
{
int index = vertexBinormal->GetIndexArray().GetAt(controlPointIndex);
result.x = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(index).mData[0]);
result.y = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(index).mData[1]);
result.z = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(index).mData[2]);
}
break;
default:
std::cout << "Error: Invalid vertex reference mode!" << std::endl;
}
break;
case FbxGeometryElement::eByPolygonVertex:
switch (vertexBinormal->GetReferenceMode())
{
case FbxGeometryElement::eDirect:
{
result.x = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(vertexCounter).mData[0]);
result.y = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(vertexCounter).mData[1]);
result.z = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(vertexCounter).mData[2]);
}
break;
case FbxGeometryElement::eIndexToDirect:
{
int index = vertexBinormal->GetIndexArray().GetAt(vertexCounter);
result.x = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(index).mData[0]);
result.y = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(index).mData[1]);
result.z = static_cast<float>(vertexBinormal->GetDirectArray().GetAt(index).mData[2]);
}
break;
default:
std::cout << "Error: Invalid vertex reference mode!" << std::endl;
}
break;
}
텍스쳐 UV
텍스쳐의 UV 정보는 polygon vertex(eByPolygonVertex)에 매핑된 경우 별도의 텍스쳐 인덱스가 필요합니다. FbxMesh::GetTextureUVIndex() 멤버 함수로 접근 가능합니다.
for(unsigned int i = 0; i < triCount; ++i) // 삼각형의 개수
{
for(unsigned int j = 0; j < 3; ++j) // 삼각형은 세 개의 정점으로 구성
{
int controlPointIndex = mesh->GetPolygonVertex(i, j); // 제어점 인덱스를 가져온다.
vec3& position = positions[controlPointIndex]; // 현재 정점에 대한 위치
vec3 normal = ReadNormal (mesh, controlPointIndex, vertexCounter);
vec3 binormal = ReadBinormal(mesh, controlPointIndex, vertexCounter);
vec3 tangent = ReadTangent (mesh, controlPointIndex, vertexCounter);
vec2 uv = ReadUV (mesh, controlPointIndex, mesh->GetTextureUVIndex(i, j));
vertexCount++; // 정점의 개수++
}
}
int controlPointIndex = mesh->GetPolygonVertex(i, j); // 제어점 인덱스를 가져온다.
ReadUV() 함수의 내용은 다른 ReadXX와 내용이 같습니다.
정점 배열과 인덱스 배열
이상으로 FBX파일에서 정점에 대한 정보(position, normal, binormal, tangent, uv)를 모두 알 수 있습니다. 이러한 정보는 구조체로 다음과 같이 추상화 가능합니다.
struct Vertex
{
vec3 position;
vec3 normal;
vec2 uv;
vec3 binormal;
vec3 tangent;
bool operator==(const Vertex& other) const
{
return position == other.position && normal == other.normal && uv == other.uv && binormal == other.binormal && tangent == other.tangent;
}
};
정점들을 담을 배열, 인덱스를 담을 배열, 정점과 인덱스를 맵핑할 맵을 전역변수로 만듭니다.
std::vector<Vertex> vertices;
std::vector<uint> indices;
std::unordered_map<Vertex, uint> indexMapping;
다음으로 정점정보를 배열에 삽입할 기능을 구현합니다.
void InsertVertex(const vec3& position, const vec3& normal, const vec2& uv, const vec3& binormal, const vec3& tangent)
{
Vertex vertex = { position, normal, uv, binormal, tangent };
auto lookup = indexMapping.find(vertex);
if (lookup != indexMapping.end())
{
indices.push_back(lookup->second);
}
else
{
unsigned int index = vertices.size();
indexMapping[vertex] = index;
indices.push_back(index);
vertices.push_back(vertex);
}
}
// ~ 생략
vec3& position = positions[controlPointIndex]; // 현재 정점에 대한 위치
vec3 normal = ReadNormal (mesh, controlPointIndex, vertexCounter);
vec3 binormal = ReadBinormal(mesh, controlPointIndex, vertexCounter);
vec3 tangent = ReadTangent (mesh, controlPointIndex, vertexCounter);
vec2 uv = ReadUV (mesh, controlPointIndex, mesh->GetTextureUVIndex(i, j));
InsertVertex(position, normal, uv, binormal, tangent);
지금까지의 과정으로 얻은 정점 배열과 인덱스 배열을 DirectX, OpenGL등 에서 적절하게 활용하여 렌더링할 수 있습니다.
'C++ 이야기 > FBX SDK' 카테고리의 다른 글
FBX SDK 사용법 #5 레이어와 레이어 요소 (0) | 2018.02.17 |
---|---|
FBX SDK 사용법 #4 메쉬와 정점 (12) | 2018.02.16 |
FBX SDK 사용법 #3 씬과 노드 그리고 노드 속성 (2) | 2018.02.15 |
FBX SDK 사용법 #2 .FBX 파일에서 씬 가져오기 (0) | 2018.02.15 |
FBX SDK 사용법 #1 SDK 관리자로 메모리 관리하기 (1) | 2018.02.15 |
댓글 로드 중…