Jump to content
  • Advertisement
Sign in to follow this  
Gumgo

FBX SDK normals

This topic is 2990 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've exported meshes to FBX format and I'm using the FBX SDK to convert these to my own custom format. So far, I can successfully convert vertex positions and texture coordinates. However, normals (and tangents and binormals I'm guessing) are not converting correctly.

Here's what I'm doing so far (just deals with normals):

// vector for our triangles
vector <Triangle> triangles;

// get all the triangles, positions, texCoords, normals, tangents, binormals
int triCount = mesh->GetPolygonCount();
KFbxVector4 * positions = mesh->GetControlPoints();

for (int i = 0; i < triCount; ++i) {
// add a triangle
triangles.push_back( Triangle() );
// get a reference to it
Triangle & tri = triangles.back();

// for each vertex in the triangle
for (int v = 0; v < 3; ++v) {
// get a reference to it
Vertex & vert = tri.vertices[v];

// set its position
int positionIndex = mesh->GetPolygonVertex( i, v );
for (int p = 0; p < 3; ++p)
vert.position

= (float)positions[positionIndex]

;
}
}

bool texCoordsFound = false;
bool normalsFound = false;
bool tangentsFound = false;
bool binormalsFound = false;

bool error = false;
for (int l = 0; l < mesh->GetLayerCount(); ++l) {
KFbxLayerElementUV * texCoords = texCoordsFound ? NULL : mesh->GetLayer( l )->GetUVs();
KFbxLayerElementNormal * normals = normalsFound ? NULL : mesh->GetLayer( l )->GetNormals();
KFbxLayerElementTangent * tangents = tangentsFound ? NULL : mesh->GetLayer( l )->GetTangents();
KFbxLayerElementBinormal * binormals = binormalsFound ? NULL : mesh->GetLayer( l )->GetBinormals();

// loop through triangles
for (int i = 0; i < triCount; ++i) {
// get a reference to it
Triangle & tri = triangles;

// for each vertex in the triangle
for (int v = 0; v < 3; ++v) {
// get a reference to it
Vertex & vert = tri.vertices[v];

int positionIndex = mesh->GetPolygonVertex( i, v );

//...
//... get tex coords here (works)
//...

// set its normal
if (normals != NULL) {
normalsFound = true;
if (normals->GetMappingMode() == KFbxLayerElement::eBY_POLYGON_VERTEX) {
switch (normals->GetReferenceMode()) {
case KFbxLayerElement::eDIRECT:
for (int p = 0; p < 3; ++p)
vert.normal

= (float)normals->GetDirectArray().GetAt( positionIndex )

;
break;
case KFbxLayerElement::eINDEX_TO_DIRECT:
{
int index = normals->GetIndexArray().GetAt( positionIndex );
for (int p = 0; p < 3; ++p)
vert.normal

= (float)normals->GetDirectArray().GetAt( index )

;
}
break;
default:
cout << " Error: invalid normal reference mode\n";
error = true;
}
} else {
cout << " Error: invalid normal mapping mode\n";
error = true;
}
}

//...
//... get tangents/binormals here (probably doesn't work, haven't tested though, but similar to normals)
//...

if (error)
break;
}
if (error)
break;
}
if (error)
break;
}
if (error)
continue;


This seems to be what the FBX examples are doing to get the normals.

The actual normals I'm getting seem to be correct - that is, for an object like a box, I get a bunch of (0, 0, +-1), (0, +-1, 0), (+-1, 0, 0) for the normals. The issue seems to be that I'm not using the correct indices. Here is a screenshot of what I mean:

Each face should be "flat shaded" and should have a solid color on the entire face. It appears that there are only around 6 unique colors (+-1 in directions x, y, z), which is correct, but that they're applied to the wrong vertices.

Can anyone see anything I'm doing wrong in extracting the normal indices?

Share this post


Link to post
Share on other sites
Advertisement
I'd firstly recommend using an index buffer since it can be more efficient when rendering. I do read verts in like this:


memcpy( m->IndexArray, Node->GetMesh( )->GetPolygonVertices( ), sizeof( int ) * info->NumTriangles * 3 );

KFbxVector4 *Verts = Node->GetMesh( )->GetControlPoints( );

for( int i = 0; i < info->NumVerts; i++ )
{
float x = ( float ) Verts.GetAt( 0 );
float y = ( float ) Verts.GetAt( 1 );
float z = ( float ) Verts.GetAt( 2 );

m->VertexArray[0] = x;
m->VertexArray[1] = y;
m->VertexArray[2] = z;

// snip
}



I had a bit of trouble with normals as well but this has seemed to work for me so far, still need to do more testing though:


KFbxLayerElementNormal *Normals2 = Node->GetMesh( )->GetLayer( 0 )->GetNormals( );
MappingMode = Node->GetMesh( )->GetLayer( 0 )->GetNormals( )->GetMappingMode( );

if( MappingMode == KFbxLayerElement::eBY_CONTROL_POINT )
{
for( int vIdx = 0; vIdx < info->NumVerts; vIdx++ )
{
int nIdx = 0;

if( Normals2->GetReferenceMode( ) == KFbxLayerElement::eDIRECT )
{
nIdx = vIdx;
}

if( Normals2->GetReferenceMode( ) == KFbxLayerElement::eINDEX_TO_DIRECT )
{
nIdx = Normals2->GetIndexArray( ).GetAt( vIdx );
}

KFbxVector4 Normal = Normals2->GetDirectArray( ).GetAt( nIdx );

m->NormalArray[ vIdx ][0] = ( float ) Normals->GetAt( nIdx ).GetAt( 0 );
m->NormalArray[ vIdx ][1] = ( float ) Normals->GetAt( nIdx ).GetAt( 1 );
m->NormalArray[ vIdx ][2] = ( float ) Normals->GetAt( nIdx ).GetAt( 2 );
}
}

Share this post


Link to post
Share on other sites
I actually am using index buffers. What I'm doing is I'm making a big list with 3 vertices per triangle first, then reducing it down and indexing by finding vertices that are equivalent after I've read all the vertices.

Unfortunately, I don't think your method will work for me because the normals aren't mapped using eBY_CONTROL_POINT mapping (if they were, my program would display an error, but it does not).

EDIT: I believe I may have discovered the issue, though I'm not sure how to fix it. It appears that calling GetPolygonVertex() returns the index of a control point. However, there may be multiple normals specified at each control point, so using GetPolygonVertex() and using the index returned to find the normal clearly won't work. It looks like what I need is something like GetPolygonVertexNormal(), which returns the vertex normal of the ith vertex of the polygon. However, there aren't matching GetPolygonVertexUVCoordinate(), GetPolygonVertexTangent(), and GetPolygonVertexBinormal(), so this won't work.

EDIT2: Ah hah, got it! GetPolygonVertex( polygon )+vertex_in_polygon is the way to find the polygon's index. A fair amount of people seem to have trouble with this, so I'll post the whole code (it might not be entirely right, but it seems to work):

for (int l = 0; l < mesh->GetLayerCount(); ++l) {
KFbxLayerElementUV * texCoords = texCoordsFound ? NULL : mesh->GetLayer( l )->GetUVs();
KFbxLayerElementNormal * normals = normalsFound ? NULL : mesh->GetLayer( l )->GetNormals();
KFbxLayerElementTangent * tangents = tangentsFound ? NULL : mesh->GetLayer( l )->GetTangents();
KFbxLayerElementBinormal * binormals = binormalsFound ? NULL : mesh->GetLayer( l )->GetBinormals();

// loop through triangles
for (int i = 0; i < triCount; ++i) {
// get a reference to it
Triangle & tri = triangles;

// for each vertex in the triangle
for (int v = 0; v < 3; ++v) {
// get a reference to it
Vertex & vert = tri.vertices[v];

//int positionIndex = mesh->GetPolygonVertex( i, v );
int positionIndex = mesh->GetPolygonVertexIndex( i ) + v;

// set its texture coordinate
if (texCoords != NULL) {
texCoordsFound = true;
switch (texCoords->GetMappingMode()) {
case KFbxLayerElement::eBY_CONTROL_POINT:
switch (texCoords->GetReferenceMode()) {
case KFbxLayerElement::eDIRECT:
for (int p = 0; p < 2; ++p)
vert.texCoord

= (float)texCoords->GetDirectArray().GetAt( positionIndex )

;
break;
case KFbxLayerElement::eINDEX_TO_DIRECT:
{
int index = texCoords->GetIndexArray().GetAt( positionIndex );
for (int p = 0; p < 2; ++p)
vert.texCoord

= (float)texCoords->GetDirectArray().GetAt( index )

;
}
break;
default:
cout << " Error: invalid texture coordinate reference mode\n";
error = true;
}
break;
case KFbxLayerElement::eBY_POLYGON_VERTEX:
{
int index = mesh->GetTextureUVIndex( i, v );
switch (texCoords->GetReferenceMode()) {
case KFbxLayerElement::eDIRECT:
case KFbxLayerElement::eINDEX_TO_DIRECT:
for (int p = 0; p < 2; ++p)
vert.texCoord

= (float)texCoords->GetDirectArray().GetAt( index )

;
break;
default:
cout << " Error: invalid texture coordinate reference mode\n";
error = true;
}
}
break;
case KFbxLayerElement::eBY_POLYGON:
case KFbxLayerElement::eALL_SAME:
case KFbxLayerElement::eNONE:
cout << " Error: invalid texture coordinate mapping mode\n";
error = true;
}
}

// set its normal
if (normals != NULL) {
normalsFound = true;
if (normals->GetMappingMode() == KFbxLayerElement::eBY_POLYGON_VERTEX) {
switch (normals->GetReferenceMode()) {
case KFbxLayerElement::eDIRECT:
for (int p = 0; p < 3; ++p)
vert.normal

= (float)normals->GetDirectArray().GetAt( positionIndex )

;
break;
case KFbxLayerElement::eINDEX_TO_DIRECT:
{
int index = normals->GetIndexArray().GetAt( positionIndex );
for (int p = 0; p < 3; ++p)
vert.normal

= (float)normals->GetDirectArray().GetAt( index )

;
}
break;
default:
cout << " Error: invalid normal reference mode\n";
error = true;
}
} else {
cout << " Error: invalid normal mapping mode\n";
error = true;
}
}

// set its tangent
if (tangents != NULL) {
tangentsFound = true;
if (tangents->GetMappingMode() == KFbxLayerElement::eBY_POLYGON_VERTEX) {
switch (tangents->GetReferenceMode()) {
case KFbxLayerElement::eDIRECT:
for (int p = 0; p < 3; ++p)
vert.tangent

= (float)tangents->GetDirectArray().GetAt( positionIndex )

;
break;
case KFbxLayerElement::eINDEX_TO_DIRECT:
{
int index = tangents->GetIndexArray().GetAt( positionIndex );
for (int p = 0; p < 3; ++p)
vert.tangent

= (float)tangents->GetDirectArray().GetAt( index )

;
}
break;
default:
cout << " Error: invalid tangent reference mode\n";
error = true;
}
} else {
cout << " Error: invalid tangent mapping mode\n";
error = true;
}
}

// set its binormals
if (binormals != NULL) {
binormalsFound = true;
if (binormals->GetMappingMode() == KFbxLayerElement::eBY_POLYGON_VERTEX) {
switch (binormals->GetReferenceMode()) {
case KFbxLayerElement::eDIRECT:
for (int p = 0; p < 3; ++p)
vert.binormal

= (float)binormals->GetDirectArray().GetAt( positionIndex )

;
break;
case KFbxLayerElement::eINDEX_TO_DIRECT:
{
int index = binormals->GetIndexArray().GetAt( positionIndex );
for (int p = 0; p < 3; ++p)
vert.binormal

= (float)binormals->GetDirectArray().GetAt( index )

;
}
break;
default:
cout << " Error: invalid binormal reference mode\n";
error = true;
}
} else {
cout << " Error: invalid binormal mapping mode\n";
error = true;
}
}

if (error)
break;
}
if (error)
break;
}
if (error)
break;
}
if (error)
continue;

if (!texCoordsFound) {
cout << " Error: " << mesh->GetName() << " has no texture coordinates\n";
continue;
}
if (!normalsFound) {
cout << " Error: " << mesh->GetName() << " has no normals\n";
continue;
}
if (!tangentsFound) {
cout << " Error: " << mesh->GetName() << " has no tangents\n";
continue;
}
if (!binormalsFound) {
cout << " Error: " << mesh->GetName() << " has no binormals\n";
continue;
}



[Edited by - Gumgo on July 10, 2010 12:09:43 AM]

Share this post


Link to post
Share on other sites
it depends on what options have been setted in your fbx exporter, by default the exporter will smooth your normals.

There is an option in order not do it , split normal at edge.

Hopes this help

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!