title: FBX Sdk Skeletal Animations
body: <p>I have successfully loaded and rendered a fbx model using fbx sdk and directx.
  I'm now trying to implement skeletal animations. First  I'm trying to put my character on different poses by modifying bones matrices. (So I can achieve animations by setting different poses at times)</p>
  
  <p>Here's my code of loading fbx and skinning information.</p>
  
  <p><strong>FBXLoader.h</strong></p>
  
  <pre><code>#include &lt;fbxsdk.h&gt;
  #include &lt;vector&gt;
  #include &lt;string&gt;
  #include &lt;map&gt;
  #include &lt;d3d11_1.h&gt;
  #include &lt;DirectXMath.h&gt;
  #include "TextureLoader.h"
  
  using namespace DirectX;
  
  struct VERTEX
  {
      XMFLOAT3 pos;
      XMFLOAT2 tex;
      XMFLOAT4 boneids;
      XMFLOAT4 weights;
  
      VERTEX()
      {
          boneids = { 0, 0, 0, 0 };
          weights = { 0, 0, 0, 0 };
      }
  };
  
  struct Keyframe {
      FbxLongLong mFrameNum;
      FbxAMatrix mGlobalTransform;
      Keyframe* mNext;
  
      Keyframe() : mNext(nullptr)
      {}
  };
  
  struct Joint {
  int mParentIndex;
  const char* mName;
  FbxAMatrix mGlobalBindposeInverse;
  Keyframe* mAnimation;
  FbxNode *mNode;
  
  Joint() :
      mNode(nullptr),
      mAnimation(nullptr)
  {
      mGlobalBindposeInverse.SetIdentity();
      mParentIndex = -1;
  }
  
  ~Joint()
  {
      while (mAnimation)
      {
          Keyframe* temp = mAnimation-&gt;mNext;
          delete mAnimation;
          mAnimation = temp;
      }
  }
  };
  
  struct Skeleton {
  std::vector&lt;Joint&gt; mJoints;
  };
  
  class Mesh
  {
  public:
  Mesh(ID3D11Device *dev, std::vector&lt;VERTEX&gt; vertices, ID3D11ShaderResourceView *texture)
  {
      this-&gt;vertices = vertices;
      this-&gt;texture = texture;
  
      this-&gt;SetupMesh(dev);
  }
  
  void Draw(ID3D11DeviceContext *devcon)
  {
      UINT stride = sizeof(VERTEX);
      UINT offset = 0;
  
      devcon-&gt;IASetVertexBuffers(0, 1, &amp;vertexBuffer, &amp;stride, &amp;offset);
  
      if (this-&gt;texture != nullptr)
          devcon-&gt;PSSetShaderResources(0, 1, &amp;texture);
  
      devcon-&gt;Draw(vertices.size(), 0);
  }
  private:
  std::vector&lt;VERTEX&gt; vertices;
  ID3D11ShaderResourceView *texture = nullptr;
  ID3D11Buffer* vertexBuffer;
  
  bool SetupMesh(ID3D11Device *dev)
  {
      HRESULT hr;
  
      D3D11_BUFFER_DESC vbd;
      vbd.Usage = D3D11_USAGE_IMMUTABLE;
      vbd.ByteWidth = sizeof(VERTEX) * vertices.size();
      vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
      vbd.CPUAccessFlags = 0;
      vbd.MiscFlags = 0;
  
      D3D11_SUBRESOURCE_DATA initData;
      initData.pSysMem = &amp;vertices[0];
  
      hr = dev-&gt;CreateBuffer(&amp;vbd, &amp;initData, &amp;vertexBuffer);
      if (FAILED(hr))
          return false;
  }
  };
  
  class FBXLoader
  {
  public:
  FBXLoader();
  ~FBXLoader();
  
  void LoadFBX(HWND hwnd, ID3D11Device *dev, ID3D11DeviceContext *devcon, const char* filename);
  
  void Draw(ID3D11DeviceContext *devcon);
  
  XMMATRIX GetAnimatedMatrix(int index);
  
  Skeleton skeleton;
  
  private:
  FbxManager *fbxsdkManager = nullptr;
  FbxScene *fbxScene;
  std::map&lt;int, int&gt; controlpoints;
  std::vector&lt;Mesh&gt; meshes;
  HWND hwnd;
  
  void ProcessNode(ID3D11Device *dev, ID3D11DeviceContext *devcon, FbxNode *node, FbxGeometryConverter *gConverter);
  
  Mesh ProcessMesh(ID3D11Device* dev, ID3D11DeviceContext *devcon, FbxMesh *mesh);
  
  void ProcessSkeletonHeirarchy(FbxNode* rootnode);
  
  void ProcessSkeletonHeirarchyre(FbxNode* node, int depth, int index, int parentindex);
  
  unsigned int FindJointIndex(const std::string&amp; jointname);
  
  ID3D11ShaderResourceView *LoadTexture(ID3D11Device *dev, ID3D11DeviceContext *devcon, const char* texturefilename);
  };
  </code></pre>
  
  <p><strong>FBXLoader.cpp</strong></p>
  
  <pre><code>#include "FBXLoader.h"
  
  
  
  FBXLoader::FBXLoader()
  {
  }
  
  
  FBXLoader::~FBXLoader()
  {
  }
  
  void FBXLoader::LoadFBX(HWND hwnd, ID3D11Device * dev, ID3D11DeviceContext *devcon, const char * filename)
  {
  if (fbxsdkManager == nullptr)
  {
      fbxsdkManager = FbxManager::Create();
  
      FbxIOSettings* ioSettings = FbxIOSettings::Create(fbxsdkManager, IOSROOT);
      fbxsdkManager-&gt;SetIOSettings(ioSettings);
  }
  
  FbxImporter *importer = FbxImporter::Create(fbxsdkManager, "");
  fbxScene = FbxScene::Create(fbxsdkManager, "");
  
  FbxGeometryConverter gConverter(fbxsdkManager);
  
  bool bSuccess = importer-&gt;Initialize(filename, -1, fbxsdkManager-&gt;GetIOSettings());
  
  bSuccess = importer-&gt;Import(fbxScene);
  
  importer-&gt;Destroy();
  
  FbxNode *fbxRootNode = fbxScene-&gt;GetRootNode();
  
  ProcessSkeletonHeirarchy(fbxRootNode);
  
  this-&gt;hwnd = hwnd;
  
  ProcessNode(dev, devcon, fbxRootNode, &amp;gConverter);
  }
  
  void FBXLoader::Draw(ID3D11DeviceContext * devcon)
  {
  for (int i = 0; i &lt; meshes.size(); i++)
  {
      meshes[i].Draw(devcon);
  }
  }
  
  XMMATRIX FBXLoader::GetAnimatedMatrix(int index)
  {
  XMMATRIX bonematxm;
  FbxAMatrix bonemat = skeleton.mJoints[index].mGlobalBindposeInverse; //* skeleton.mJoints[0].mAnimation-&gt;mGlobalTransform;
  
  bonematxm = XMMatrixTranslation(bonemat.GetT().mData[0], bonemat.GetT().mData[1], bonemat.GetT().mData[2]);
  bonematxm *= XMMatrixRotationX(bonemat.GetR().mData[0]);
  bonematxm *= XMMatrixRotationY(bonemat.GetR().mData[1]);
  bonematxm *= XMMatrixRotationZ(bonemat.GetR().mData[2]);
  
  return bonematxm;
  }
  
  void FBXLoader::ProcessNode(ID3D11Device * dev, ID3D11DeviceContext *devcon, FbxNode * node, FbxGeometryConverter * gConverter)
  {
  if (node)
  {
      if (node-&gt;GetNodeAttribute() != nullptr)
      {
          FbxNodeAttribute::EType AttributeType = node-&gt;GetNodeAttribute()-&gt;GetAttributeType();
  
          if (AttributeType == FbxNodeAttribute::eMesh)
          {
              FbxMesh *mesh;
  
              mesh = (FbxMesh*)gConverter-&gt;Triangulate(node-&gt;GetNodeAttribute(), true);
  
              meshes.push_back(ProcessMesh(dev, devcon, mesh));
          }
      }
  
      for (int i = 0; i &lt; node-&gt;GetChildCount(); i++)
      {
          ProcessNode(dev, devcon, node-&gt;GetChild(i), gConverter);
      }
  }
  }
  
  Mesh FBXLoader::ProcessMesh(ID3D11Device * dev, ID3D11DeviceContext *devcon, FbxMesh * mesh)
  {
  std::vector&lt;VERTEX&gt; meshvertices;
  ID3D11ShaderResourceView *meshtexture = nullptr;
  
  FbxVector4 *vertices = mesh-&gt;GetControlPoints();
  
  for (int j = 0; j &lt; mesh-&gt;GetPolygonCount(); j++)
  {
      int numVertices = mesh-&gt;GetPolygonSize(j);
  
      FbxLayerElementArrayTemplate&lt;FbxVector2&gt; *uvVertices = 0;
      mesh-&gt;GetTextureUV(&amp;uvVertices, FbxLayerElement::eTextureDiffuse);
  
      for (int k = 0; k &lt; numVertices; k++)
      {
          int controlPointIndex = mesh-&gt;GetPolygonVertex(j, k);
  
          VERTEX vertex;
  
          vertex.pos.x = (float)vertices[controlPointIndex].mData[0];
          vertex.pos.y = (float)vertices[controlPointIndex].mData[1];
          vertex.pos.z = (float)vertices[controlPointIndex].mData[2];
  
          vertex.tex.x = (float)uvVertices-&gt;GetAt(mesh-&gt;GetTextureUVIndex(j, k)).mData[0];
          vertex.tex.y = 1.0f - (float)uvVertices-&gt;GetAt(mesh-&gt;GetTextureUVIndex(j, k)).mData[1];
  
          controlpoints[controlPointIndex] = meshvertices.size();
  
          meshvertices.push_back(vertex);
      }
  }
  
  int materialcount = mesh-&gt;GetNode()-&gt;GetSrcObjectCount&lt;FbxSurfaceMaterial&gt;();
  
  for (int i = 0; i &lt; materialcount; i++)
  {
      FbxSurfaceMaterial *material = (FbxSurfaceMaterial*)mesh-&gt;GetNode()-&gt;GetSrcObject&lt;FbxSurfaceMaterial&gt;(i);
  
      if (material)
      {
          FbxProperty prop = material-&gt;FindProperty(FbxSurfaceMaterial::sDiffuse);
  
          const FbxTexture* texture = FbxCast&lt;FbxTexture&gt;(prop.GetSrcObject&lt;FbxTexture&gt;(0));
          const FbxFileTexture* filetexture = FbxCast&lt;FbxFileTexture&gt;(texture);
  
          ID3D11ShaderResourceView *meshctexture = LoadTexture(dev, devcon, filetexture-&gt;GetFileName());
  
          meshtexture = meshctexture;
      }
  }
  
  const FbxVector4 lT = mesh-&gt;GetNode()-&gt;GetGeometricTranslation(FbxNode::eSourcePivot);
  const FbxVector4 lR = mesh-&gt;GetNode()-&gt;GetGeometricRotation(FbxNode::eSourcePivot);
  const FbxVector4 lS = mesh-&gt;GetNode()-&gt;GetGeometricScaling(FbxNode::eSourcePivot);
  
  FbxAMatrix geometryTransform = FbxAMatrix(lT, lR, lS);
  
  for (unsigned int deformerIndex = 0; deformerIndex &lt; mesh-&gt;GetDeformerCount(); ++deformerIndex)
  {
      FbxSkin* skin = reinterpret_cast&lt;FbxSkin*&gt;(mesh-&gt;GetDeformer(deformerIndex, FbxDeformer::eSkin));
      if (!skin)
          continue;
  
      for (unsigned int clusterIndex = 0; clusterIndex &lt; skin-&gt;GetClusterCount(); ++clusterIndex)
      {
          FbxCluster* cluster = skin-&gt;GetCluster(clusterIndex);
          std::string jointname = cluster-&gt;GetLink()-&gt;GetName();
          unsigned int jointIndex = FindJointIndex(jointname);
          FbxAMatrix transformMatrix;
          FbxAMatrix transformLinkMatrix;
          FbxAMatrix globalBindposeInverseMatrix;
  
          cluster-&gt;GetTransformMatrix(transformMatrix);
          cluster-&gt;GetTransformLinkMatrix(transformLinkMatrix);
          globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix * geometryTransform;
  
          skeleton.mJoints[jointIndex].mGlobalBindposeInverse = globalBindposeInverseMatrix;
          skeleton.mJoints[jointIndex].mNode = cluster-&gt;GetLink();
  
          for (unsigned int i = 0; i &lt; cluster-&gt;GetControlPointIndicesCount(); ++i)
          {
              int vertexid = controlpoints[cluster-&gt;GetControlPointIndices()[i]];
  
              if (meshvertices[vertexid].boneids.x == 0) meshvertices[vertexid].boneids.x = jointIndex;
              if (meshvertices[vertexid].boneids.y == 0) meshvertices[vertexid].boneids.y = jointIndex;
              if (meshvertices[vertexid].boneids.z == 0) meshvertices[vertexid].boneids.z = jointIndex;
              if (meshvertices[vertexid].boneids.w == 0) meshvertices[vertexid].boneids.w = jointIndex;
              if (meshvertices[vertexid].weights.x == 0) meshvertices[vertexid].weights.x = cluster-&gt;GetControlPointWeights()[i];
              if (meshvertices[vertexid].weights.y == 0) meshvertices[vertexid].weights.y = cluster-&gt;GetControlPointWeights()[i];
              if (meshvertices[vertexid].weights.z == 0) meshvertices[vertexid].weights.z = cluster-&gt;GetControlPointWeights()[i];
              if (meshvertices[vertexid].weights.w == 0) meshvertices[vertexid].weights.w = cluster-&gt;GetControlPointWeights()[i];
          }
  
          FbxAnimStack* animstack = fbxScene-&gt;GetSrcObject&lt;FbxAnimStack&gt;(0);
          FbxString animstackname = animstack-&gt;GetName();
          FbxTakeInfo* takeinfo = fbxScene-&gt;GetTakeInfo(animstackname);
          FbxTime start = takeinfo-&gt;mLocalTimeSpan.GetStart();
          FbxTime end = takeinfo-&gt;mLocalTimeSpan.GetStop();
          FbxLongLong animationlength = end.GetFrameCount(FbxTime::eFrames30) - start.GetFrameCount(FbxTime::eFrames30) + 1;
          Keyframe** anim = &amp;skeleton.mJoints[jointIndex].mAnimation;
  
          for (FbxLongLong i = start.GetFrameCount(FbxTime::eFrames30); i &lt;= end.GetFrameCount(FbxTime::eFrames30); ++i)
          {
              FbxTime time;
              time.SetFrame(i, FbxTime::eFrames30);
              *anim = new Keyframe();
              (*anim)-&gt;mFrameNum = i;
              FbxAMatrix transformoffset = mesh-&gt;GetNode()-&gt;EvaluateGlobalTransform(1.0f) * geometryTransform;
              (*anim)-&gt;mGlobalTransform = transformoffset.Inverse() * cluster-&gt;GetLink()-&gt;EvaluateGlobalTransform(time);
              anim = &amp;((*anim)-&gt;mNext);
          }
      }
  }
  
  return Mesh(dev, meshvertices, meshtexture);
  }
  
  void FBXLoader::ProcessSkeletonHeirarchy(FbxNode * rootnode)
  {
  for (int childindex = 0; childindex &lt; rootnode-&gt;GetChildCount(); ++childindex)
  {
      FbxNode *node = rootnode-&gt;GetChild(childindex);
      ProcessSkeletonHeirarchyre(node, 0, 0, -1);
  }
  }
  
  void FBXLoader::ProcessSkeletonHeirarchyre(FbxNode * node, int depth, int index, int parentindex)
  {
  if (node-&gt;GetNodeAttribute() &amp;&amp; node-&gt;GetNodeAttribute()-&gt;GetAttributeType() &amp;&amp; node-&gt;GetNodeAttribute()-&gt;GetAttributeType() == FbxNodeAttribute::eSkeleton)
  {
      Joint joint;
      joint.mParentIndex = parentindex;
      joint.mName = node-&gt;GetName();
      skeleton.mJoints.push_back(joint);
  }
  for (int i = 0; i &lt; node-&gt;GetChildCount(); i++)
  {
      ProcessSkeletonHeirarchyre(node-&gt;GetChild(i), depth + 1, skeleton.mJoints.size(), index);
  }
  }
  
  unsigned int FBXLoader::FindJointIndex(const std::string &amp; jointname)
  {
  for (unsigned int i = 0; i &lt; skeleton.mJoints.size(); ++i)
  {
      if (skeleton.mJoints[i].mName == jointname)
      {
          return i;
      }
  }
  }
  
  ID3D11ShaderResourceView * FBXLoader::LoadTexture(ID3D11Device * dev, ID3D11DeviceContext * devcon, const char * texturefilename)
  {
  HRESULT hr;
  
  ID3D11ShaderResourceView *texture;
  
  std::string filenamestr(texturefilename);
  std::string sl = "/";
  size_t start_pos = filenamestr.find(sl);
  filenamestr.replace(start_pos, sl.length(), "\\");
  std::wstring filename = std::wstring(filenamestr.begin(), filenamestr.end());
  
  hr = CreateWICTextureFromFile(dev, devcon, filename.c_str(), nullptr, &amp;texture);
  if (FAILED(hr))
      return nullptr;
  
  return texture;
  }
  </code></pre>
  
  <p>Here's my vertex shader</p>
  
  <pre><code>matrix bonetransform = mul(bones[boneids[0]], weights[0]);
  bonetransform += mul(bones[boneids[1]], weights[1]);
  bonetransform += mul(bones[boneids[2]], weights[2]);
  bonetransform += mul(bones[boneids[3]], weights[3]);
  
  float4 posl = mul(bonetransform, pos);
  
  output.pos = mul(posl, World);
  </code></pre>
  
  <p>Here's my drawing code</p>
  
  <pre><code>cb.mWorld = XMMatrixTranspose(m_World);
  cb.mView = XMMatrixTranspose(m_View);
  cb.mProjection = XMMatrixTranspose(m_Projection);
  for (int i = 0; i &lt; jasper-&gt;skeleton.mJoints.size(); i++)
      cb.bones[i] = XMMatrixTranspose(XMMatrixIdentity());
  devcon-&gt;UpdateSubresource(pConstantBuffer, 0, nullptr, &amp;cb, 0, 0);
  
  devcon-&gt;VSSetShader(pVS, 0, 0);
  devcon-&gt;VSSetConstantBuffers(0, 1, &amp;pConstantBuffer);
  devcon-&gt;PSSetShader(pPS, 0, 0);
  devcon-&gt;PSSetSamplers(0, 1, &amp;TexSamplerState);
  model-&gt;Draw(devcon);
  </code></pre>
  
  <p>And when I set all bones matrices in vertex shader to identity matrix this is what I get.</p>
  
  <p><a href="https://i.stack.imgur.com/hw8wC.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hw8wC.png" alt="enter image description here"></a></p>
  
  <p>Shouldn't it be this when I use identity matrix?</p>
  
  <p><a href="https://i.stack.imgur.com/JGLzs.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/JGLzs.png" alt="enter image description here"></a></p>