3
\$\begingroup\$

I am trying to build a game with animation in c++ with DirectX.

I have some fbx files with animation that I want to integrate in my game.

But I do not want to draw the fbx files as a FbxScene or FbxMesh but to get all the data of the mesh to a primitive data (like array of vertex) so I can easily change some things like convert it to directx mesh.

I searched the internet but I could not find a solution how to read the data from the FbxScene.

Thanks very much.

\$\endgroup\$
  • \$\begingroup\$ Autodesk provides an FBX SDK you can use to read FBX files from c++ \$\endgroup\$ – Panda Pajama Sep 20 '14 at 14:50
  • \$\begingroup\$ The DirectX SDK Samples Content Exporter generates .sdkmesh files from FBX files. You can then load and render .sdkmesh files with DXUT or DirectX Tool Kit. Otherwise, you can use it as example code for your own runtime format. \$\endgroup\$ – Chuck Walbourn Sep 20 '14 at 17:27
  • \$\begingroup\$ If using VS 2012 or VS 2013, you can also make use of the built-in FBX to CMO exporter with the Mesh Content Pipeline. CMO files can be loaded and rendered with VS Starter Kit or DirectX Tool Kit. \$\endgroup\$ – Chuck Walbourn Sep 20 '14 at 17:29
  • \$\begingroup\$ The FBX SDK is a nightmare to work with. I'd highly recommending exporting as a COLLADA file and using Assimp to load it. \$\endgroup\$ – Syntac_ Sep 20 '14 at 21:45
  • \$\begingroup\$ I have importer for collada but i need to get a path of fbx file and show in my program.I can not convert it befure. \$\endgroup\$ – amit Sep 21 '14 at 5:18
3
\$\begingroup\$

In supplemental to Syntac_'s example, here's how you could also load some keyframe and skeletal animation data.

For a start, given you have an Fbx scene object initialized, you could pass fbxScene->GetRootNode() to a method like this:

void MyFbxScene::LoadNode(FbxNode* fbxNode)
{
    int numAttributes = fbxNode->GetNodeAttributeCount();
    for (int i = 0; i < numAttributes; i++)
    {
        FbxNodeAttribute *nodeAttributeFbx = fbxNode->GetNodeAttributeByIndex(i);
        FbxNodeAttribute::EType attributeType = nodeAttributeFbx->GetAttributeType();

        switch (attributeType)
        {
            case FbxNodeAttribute::eMesh:
            {
                // Load keyframe transformations
                this->LoadNodeKeyframeAnimation(fbxNode);

                // Load mesh vertices, texcoords, normals, etc
                this->LoadMesh((FbxMesh*)nodeAttributeFbx);

                // Load mesh skeleton
                this->LoadMesh_Skeleton((FbxMesh*)nodeAttributeFbx);
                break;
            }
        }
    }

    // Load the child nodes
    int numChildren = fbxNode->GetChildCount();
    for(int i = 0; i < numChildren; i++)
    {
        this->LoadNode(fbxNode->GetChild(i));
    }
}

Load some mesh data

void MyFbxScene::LoadMesh(FbxMesh *fbxMesh)
    // Load Vertices
    int numVertices = fbxMesh->GetControlPointsCount();
    FbxVector4 *verticesFbx = fbxMesh->GetControlPoints(); 
    for (int vertexIndex = 0; vertexIndex < numVertices; vertexIndex++)
    {
        float x = (float)verticesFbx[vertexIndex][0];
        float y = (float)verticesFbx[vertexIndex][0];
        float z = (float)verticesFbx[vertexIndex][0];
    }
    // You'd probably want to also load at least texture coords. It's a little bit complicated, if you want to take into account the different mapping modes, but you can check it from the reference link i've included below
    // You may also want to load normals, as Syntac_ suggests, as well as tangents, or you could generate them yourself. And you could load any bone weights if you want to perform skinning animation.
}

Load keyframes - the data on translation, rotation and scaling for each keyframe of an animation

void MyFbxScene::LoadNodeKeyframeAnimation(FbxNode* fbxNode)
{
    bool isAnimated = false;

    // Iterate all animations (for example, walking, running, falling and etc.)
    int numAnimations = this->fbxScene->GetSrcObjectCount(FbxAnimStack::ClassId);
    for (int animationIndex = 0; animationIndex < numAnimations; animationIndex++)
    {
        FbxAnimStack *animStack = (FbxAnimStack*)this->fbxScene->GetSrcObject(FbxAnimStack::ClassId, animationIndex);
        FbxAnimEvaluator *animEvaluator = this->fbxScene->GetAnimationEvaluator();
        animStack->GetName(); // Get the name of the animation if needed

        // Iterate all the transformation layers of the animation. You can have several layers, for example one for translation, one for rotation, one for scaling and each can have keys at different frame numbers.
        int numLayers = animStack->GetMemberCount();
        for (int layerIndex = 0; layerIndex < numLayers; layerIndex++)
        {
            FbxAnimLayer *animLayer = (FbxAnimLayer*)animStack->GetMember(layerIndex);
            animLayer->GetName(); // Get the layer's name if needed

            FbxAnimCurve *translationCurve = fbxNode->LclTranslation.GetCurve(animLayer);
            FbxAnimCurve *rotationCurve = fbxNode->LclRotation.GetCurve(animLayer);
            FbxAnimCurve *scalingCurve = fbxNode->LclScaling.GetCurve(animLayer);

            if (scalingCurve != 0)
            {
                int numKeys = scalingCurve->KeyGetCount();
                for (int keyIndex = 0; keyIndex < numKeys; keyIndex++)
                {
                    FbxTime frameTime = scalingCurve->KeyGetTime(keyIndex);
                    FbxDouble3 scalingVector = fbxNode->EvaluateLocalScaling(frameTime);
                    float x = (float)scalingVector[0];
                    float y = (float)scalingVector[1];
                    float z = (float)scalingVector[2];

                    float frameSeconds = (float)frameTime.GetSecondDouble(); // If needed, get the time of the scaling keyframe, in seconds
                }
            }
            else
            {
                // If this animation layer has no scaling curve, then use the default one, if needed
                FbxDouble3 scalingVector = fbxNode->LclScaling.Get();
                float x = (float)scalingVector[0];
                float y = (float)scalingVector[1];
                float z = (float)scalingVector[2];
            }

            // Analogically, process rotationa and translation 
        }
    }
}

Load skeletal animation

void MyFbxScene::LoadMesh_Skeleton(FbxMesh *fbxMesh)
{
    int numDeformers = fbxMesh->GetDeformerCount();
    FbxSkin* skin = (FbxSkin*)fbxMesh->GetDeformer(0, FbxDeformer::eSkin);
    if (skin != 0)
    {
        int boneCount = skin->GetClusterCount();
        for (int boneIndex = 0; boneIndex < boneCount; boneIndex++)
        {
            FbxCluster* cluster = skin->GetCluster(boneIndex);
            FbxNode* bone = cluster->GetLink(); // Get a reference to the bone's node

            // Get the bind pose
            FbxAMatrix bindPoseMatrix;
            cluster->GetTransformLinkMatrix(bindPoseMatrix);

            int *boneVertexIndices = cluster->GetControlPointIndices();
            double *boneVertexWeights = cluster->GetControlPointWeights();

            // Iterate through all the vertices, which are affected by the bone
            int numBoneVertexIndices = cluster->GetControlPointIndicesCount();
            for (int boneVertexIndex = 0; boneVertexIndex < numBoneVertexIndices; boneVertexIndex++) 
            {
                int boneVertexIndex = boneVertexIndices[boneVertexIndex];
                float boneWeight = (float)boneVertexWeights[boneVertexIndex];
            }
        }
    }
}

However, it's a bit complicated, because if you want to open the fbx properly, you'd have to consider things like smoothing groups and sub materials. Skinning is also tricky, because of taking the mesh's binding pose and the pile of tranformations affecting the mesh. It's kinda messy.

These examples are adapted cut-offs from Meshwork's MwModuleFbx. For a complete example reference, you can take a look at the full code, but bear in mind that it too has issues with skinning. You can find it at http://morroworks.com/meshwork/source/

\$\endgroup\$
  • \$\begingroup\$ Ok Thank you. But how can I render it using DirectX as regular mesh? \$\endgroup\$ – amit Sep 27 '14 at 12:40
  • \$\begingroup\$ Of course, since you know the mesh's vertices and triangles you can load them in the respective buffers and render it just as in the very first tutorials in the DirectX SDK. And you can perform the animation, since you have the animation information. Oh, should I add also how to extract polygon indices for the index buffer? \$\endgroup\$ – Rain Sep 29 '14 at 20:47
  • \$\begingroup\$ No. I know how to render vertices. Again thanks you very muce. \$\endgroup\$ – amit Oct 3 '14 at 19:09
  • \$\begingroup\$ Sorry but I have another question. How do I know how to set the fvf property of the device? \$\endgroup\$ – amit Oct 11 '14 at 12:39
1
\$\begingroup\$

Once you have loaded your scene you can do the following to get a reference to all the meshes.

FbxNode* pNode = m_pScene->GetRootNode();

if(pNode)
{
    for(int nNode = 0; nNode < pNode->GetChildCount(); nNode++)
    {
        FbxNode* pChildNode = pNode->GetChild(nNode);

        // Search for mesh node
        if(pChildNode->GetNodeAttribute())
        {
            if( pChildNode->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eMesh)
            {
                FbxMesh* pMesh = (FbxMesh*)pChildNode->GetNodeAttribute();

                // return here if you only expect one mesh or store them in an array
            }       
        }
    }
}

Once you have a valid FbxMesh you can then do something like the following to read per-vertex data.

unsigned long uPolyCount = pMesh->GetPolygonCount();
unsigned long uVertexCount = 0;
unsigned long uVertexNumber = 0; 

for(unsigned long uPoly = 0; uPoly < uPolyCount; ++uPoly)
{
    // Get number of vertices in current poly - is your mesh triangulated?
    uVertexCount = pMesh->GetPolygonSize(uPoly);

    for(unsigned long uVertex = 0; uVertex < uVertexCount; ++uVertex)
    {
         // Get corresponding vertex index for this poly-vertex
         uVertexIndex = m_pMesh->GetPolygonVertex(uPoly, uVertex);
         assert(uVertexIndex != -1 && "Index is out of range");

         // Get vertex position
         FbxVector4 fbxVertex = pMesh->GetControlPointAt(uVertexIndex);

         // Get vertex normal
         FbxVector4 fbxNormal;
         bool bResult = pMesh->GetPolygonVertexNormal(uPoly, uVertex, fbxNormal);   

         // etc..
    }
 }

Note: This example uses the 2013.3 FBX SDK.

\$\endgroup\$
  • \$\begingroup\$ But how can I get also the animation of the mesh. I found how to load the vertex of the mesh but not how to load also the animation. \$\endgroup\$ – amit Sep 21 '14 at 17:54
  • \$\begingroup\$ There's documentation here docs.autodesk.com/FBX/2013/ENU/FBX-SDK-Documentation/index.html Is there a reason why you cannot re-export as COLLADA? \$\endgroup\$ – Syntac_ Sep 21 '14 at 18:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.