소년코딩

노말 벡터(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등 에서 적절하게 활용하여 렌더링할 수 있습니다.


fbx

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기