FBX SDK normals

Started by
2 comments, last by nini 13 years, 9 months ago
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?
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 );	}}
[size="1"]
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]
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

This topic is closed to new replies.

Advertisement