Community
FBX Forum
Welcome to Autodesk’s FBX Forums. Share your knowledge, ask questions, and explore popular FBX topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Useful things you might want to know about FBXSDK

24 REPLIES 24
Reply
Message 1 of 25
tlang
9236 Views, 24 Replies

Useful things you might want to know about FBXSDK

Useful things you might want to know about FBXSDK

I have wanted to make a FBX Exporter to convert FBX file to my own format for a long time.
And the entire process is not very smooth, mainly because FBX's offcial documentation is not very clear.
Plus, since FBX format is utilized by a number of applications, rather than just game engine, the sample code provided is not
using the slogans we use in game development.

I have searched almost all the corners on the Internet to clarify things so that I can have a clear mapping from FBXSDK's data to what I need in a game engine.
Since I don't think anyone has ever posted a clear and thorough tutorial on how to convert FBX file to custom format, I will do it.
And I hope this could help people.

This tutorial would be specifically about game engine. Basically I will tell the reader how to get the data they need for their game engine.
For things like "how to initialize FBXSDK", please check the sample code yourself, the "ImportScene" sample would be very useful in this aspect.

 

1. Mesh data(position, UV, normal, tangent, binormal)
The first thing you want to do is to get the mesh data, it already feels pretty **** good if you can import your static mesh into your engine.

First please let me explain how FBX stores all its information about a mesh.
In FBX we have the term "Control Point", basically a control point is a physical vertex. For example, you have a cube, then you have 8 vertices. These
8 vertices are the only 8 "control points" in the FBX file. As a result, if you want, you can use "Vertex" and "Control Point" interchangeably.
The position information is stored in the control points.

The following code would get you the positions of all the vertices of your mesh:

// inNode is the Node in this FBX Scene that contains the mesh
// this is why I can use inNode->GetMesh() on it to get the mesh

void FBXExporter::ProcessControlPoints(FbxNode* inNode)
{
	FbxMesh* currMesh = inNode->GetMesh();
	unsigned int ctrlPointCount = currMesh->GetControlPointsCount();
	for(unsigned int i = 0; i < ctrlPointCount; ++i)
	{
		CtrlPoint* currCtrlPoint = new CtrlPoint();
		XMFLOAT3 currPosition;
		currPosition.x = static_cast<float>(currMesh->GetControlPointAt(i).mData[0]);
		currPosition.y = static_cast<float>(currMesh->GetControlPointAt(i).mData[1]);
		currPosition.z = static_cast<float>(currMesh->GetControlPointAt(i).mData[2]);
		currCtrlPoint->mPosition = currPosition;
		mControlPoints[i] = currCtrlPoint;
	}
}

 


Then you ask "how the hell can I get the UVs, Normals, Tangents, Binormals?"
Well, please think a mesh like this for a moment:
You have this body of the mesh, but this is only the geometry, the shape of it. This body does not have any information about its surface.
In other words, you have this shape, but you don't have any information on how the surface of this shape looks.

FBX introduces this sense of "Layer", which covers the body of the mesh. It is like you have a box, and you wrap it with you gift paper.
This gift paper is the layer of the mesh in FBX.
And in the layer, you can acquire the information of UVs, Normals, Tangents, Binormals.

However, you might have already asked me. How can I relate the Control Points to the information in the layer?
Well, this is pretty tricky part and please let me show you some code and then explain it line by line.
Without loss of generality, I will use Binormal as an example:

void FBXExporter::ReadNormal(FbxMesh* inMesh, int inCtrlPointIndex, int inVertexCounter, XMFLOAT3& outNormal)
{
	if(inMesh->GetElementNormalCount() < 1)
	{
		throw std::exception("Invalid Normal Number");
	}

	FbxGeometryElementNormal* vertexNormal = inMesh->GetElementNormal(0);
	switch(vertexNormal->GetMappingMode())
	{
	case FbxGeometryElement::eByControlPoint:
		switch(vertexNormal->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			outNormal.x = static_cast<float>(vertexNormal->GetDirectArray().GetAt(inCtrlPointIndex).mData[0]);
			outNormal.y = static_cast<float>(vertexNormal->GetDirectArray().GetAt(inCtrlPointIndex).mData[1]);
			outNormal.z = static_cast<float>(vertexNormal->GetDirectArray().GetAt(inCtrlPointIndex).mData[2]);
		}
		break;

		case FbxGeometryElement::eIndexToDirect:
		{
			int index = vertexNormal->GetIndexArray().GetAt(inCtrlPointIndex);
			outNormal.x = static_cast<float>(vertexNormal->GetDirectArray().GetAt(index).mData[0]);
			outNormal.y = static_cast<float>(vertexNormal->GetDirectArray().GetAt(index).mData[1]);
			outNormal.z = static_cast<float>(vertexNormal->GetDirectArray().GetAt(index).mData[2]);
		}
		break;

		default:
			throw std::exception("Invalid Reference");
		}
		break;

	case FbxGeometryElement::eByPolygonVertex:
		switch(vertexNormal->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			outNormal.x = static_cast<float>(vertexNormal->GetDirectArray().GetAt(inVertexCounter).mData[0]);
			outNormal.y = static_cast<float>(vertexNormal->GetDirectArray().GetAt(inVertexCounter).mData[1]);
			outNormal.z = static_cast<float>(vertexNormal->GetDirectArray().GetAt(inVertexCounter).mData[2]);
		}
		break;

		case FbxGeometryElement::eIndexToDirect:
		{
			int index = vertexNormal->GetIndexArray().GetAt(inVertexCounter);
			outNormal.x = static_cast<float>(vertexNormal->GetDirectArray().GetAt(index).mData[0]);
			outNormal.y = static_cast<float>(vertexNormal->GetDirectArray().GetAt(index).mData[1]);
			outNormal.z = static_cast<float>(vertexNormal->GetDirectArray().GetAt(index).mData[2]);
		}
		break;

		default:
			throw std::exception("Invalid Reference");
		}
		break;
	}
}

 

Well, this is pretty **** long but please don't be scared. Actually it is very simple.
One thing to keep in mind is that outside of this function, we are using a loop to traverse all the vertices of all the triangles in this mesh.
That is why we can have parameters like "inCtrlPointIndex" and "inVertexCounter"

The parameters of this function:
FbxMesh* inMesh: the mesh that we are trying to export
int inCtrlPointIndex: the index of the Control Point. We need this because we want to relate our layer information with our vertices(Control Points)
int inVertexCounter: this is the index of the current vertex that we are processing. This might be confusing. Ignore this for now.
XMFLOAT3& outNormal: the output. This is trivial to explain

This gets us the normal information in the layer
FbxGeometryElementNormal* vertexNormal = inMesh->GetElementNormal(0);

The first switch statement is about MappingMode().
For a game engine, I think we only need to worry about FbxGeometryElement::eByControlPoint and FbxGeometryElement::eByPolygonVertex
Let me explain the 2 modes.
As I said, Control Points are basically the vertices. However, there is a problem. Although a cube has 8 vertices, it will have more than 8 normal if you
want your cube to look correct. The reason is if you have a sharp edge, each face of your mesh would have a different normal, which makes your vertices have
different normals. And to deal with this, we have to assign more than one normals to the same control point(vertex).

As a result, FbxGeometryElement::eByControlPoint is when you don't have situations like sharp edge so each control point only has one normal.
FbxGeometryElement::eByPolygonVertex is when you have sharp edges and you need to get the normals of each vertex on each face because each face
has a different normal assigned for the same control point.
So FbxGeometryElement::eByControlPoint means we can pinpoint the normal of a control point by the index of the control point
FbxGeometryElement::eByPolygonVertex means we cna pinpoint the normal of a vertex on a face by the index of the vertex


This is why in the above code I passed in both "inCtrlPointIndex" and "inVertexCounter". Because we don't know which one we need to get the
information we need, we better pass in both.

Now we have another switch statement nested inside, and we are "switching" on ReferenceMode()
This is some kind of optimization FBX is doing, same idea like index buffer in computer graphics. You don't want to have the same Vector3 many times; instead,
you refer to it using its index.

FbxGeometryElement::eDirect means you can refer to our normal using the index of control point or index of face-vertex directly
FbxGeometryElement::eIndexToDirect means using the index of control point or index of face-vertex would only gives us an index pointing to the normal we want,
we have to use this index to find the actual normal.

This line of code gives us the index we need
int index = vertexNormal->GetIndexArray().GetAt(inVertexCounter);

So this is the main steps to extract position and "layer" information of a mesh. Below is how I traverse the triangles in a mesh.

void FBXExporter::ProcessMesh(FbxNode* inNode)
{
	FbxMesh* currMesh = inNode->GetMesh();

	mTriangleCount = currMesh->GetPolygonCount();
	int vertexCounter = 0;
	mTriangles.reserve(mTriangleCount);

	for (unsigned int i = 0; i < mTriangleCount; ++i)
	{
		XMFLOAT3 normal[3];
		XMFLOAT3 tangent[3];
		XMFLOAT3 binormal[3];
		XMFLOAT2 UV[3][2];
		Triangle currTriangle;
		mTriangles.push_back(currTriangle);

		for (unsigned int j = 0; j < 3; ++j)
		{
			int ctrlPointIndex = currMesh->GetPolygonVertex(i, j);
			CtrlPoint* currCtrlPoint = mControlPoints[ctrlPointIndex];


			ReadNormal(currMesh, ctrlPointIndex, vertexCounter, normal[j]);
			// We only have diffuse texture
			for (int k = 0; k < 1; ++k)
			{
				ReadUV(currMesh, ctrlPointIndex, currMesh->GetTextureUVIndex(i, j), k, UV[j][k]);
			}


			PNTIWVertex temp;
			temp.mPosition = currCtrlPoint->mPosition;
			temp.mNormal = normal[j];
			temp.mUV = UV[j][0];
			// Copy the blending info from each control point
			for(unsigned int i = 0; i < currCtrlPoint->mBlendingInfo.size(); ++i)
			{
				VertexBlendingInfo currBlendingInfo;
				currBlendingInfo.mBlendingIndex = currCtrlPoint->mBlendingInfo[i].mBlendingIndex;
				currBlendingInfo.mBlendingWeight = currCtrlPoint->mBlendingInfo[i].mBlendingWeight;
				temp.mVertexBlendingInfos.push_back(currBlendingInfo);
			}
			// Sort the blending info so that later we can remove
			// duplicated vertices
			temp.SortBlendingInfoByWeight();

			mVertices.push_back(temp);
			mTriangles.back().mIndices.push_back(vertexCounter);
			++vertexCounter;
		}
	}

	// Now mControlPoints has served its purpose
	// We can free its memory
	for(auto itr = mControlPoints.begin(); itr != mControlPoints.end(); ++itr)
	{
		delete itr->second;
	}
	mControlPoints.clear();
}

 

Note that there is some code related to blending info for animation. You can ignore it for now. We will come back to it later.


Now we move onto animation and this is the hard part of FBX exporting.

24 REPLIES 24
Message 21 of 25
donggas90
in reply to: tlang

Normal problem was fixed and solution is wrote in previous post.

 

And now I found FbxLayerElementBinormal and FbxLayerElementTangent class in SDK documentation.

 

I think to use this classes is faster than calculate.

 

I'll trying to that.

Message 22 of 25
donggas90
in reply to: tlang

Oops...

 

pMesh->GetElementTangentCount()

 

is Zero...

Message 23 of 25
tlang
in reply to: donggas90

Did you try

FbxGeometryElementNormal* vertexNormal = inMesh->GetElementNormal(0);

and see if you get a nullptr? This is strange, I will try on my models and my exporter to see if I get the same problem.

 

Message 24 of 25
donggas90
in reply to: tlang

I have no problem about vertex normal now.

 

And FBX doesn't have tangent information but that's OK.

 

I can calculate it.

 

Finally all problems are solved what I was needed.

 

In additionaly, un-uniformed scaling animation is remain.

 

However I don't have animation data that include un-uniform scaling.

 

So this problem will treat later.

 

Thanks for your efforts!

Message 25 of 25
donggas90
in reply to: tlang

Hi Guy!

 

I have a question about animation.

 

Can you check my question please?

 

Here is the link.

http://forums.autodesk.com/t5/FBX-SDK/How-do-you-manage-FBX-animation-data-in-your-Application/td-p/...

 

Thanks!

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report