FBX Sdk骨骼动画

时间:2017-08-15 09:20:50

标签: c++ animation directx

我使用fbx sdk和directx成功加载并渲染了fbx模型。 我现在正在尝试实现骨架动画。首先,我试图通过修改骨骼矩阵来将我的角色放在不同的姿势上。 (所以我可以通过设置不同的姿势来实现动画)

这是我加载fbx和皮肤信息的代码。

FBXLoader.h

#include <fbxsdk.h>
#include <vector>
#include <string>
#include <map>
#include <d3d11_1.h>
#include <DirectXMath.h>
#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->mNext;
        delete mAnimation;
        mAnimation = temp;
    }
}
};

struct Skeleton {
std::vector<Joint> mJoints;
};

class Mesh
{
public:
Mesh(ID3D11Device *dev, std::vector<VERTEX> vertices, ID3D11ShaderResourceView *texture)
{
    this->vertices = vertices;
    this->texture = texture;

    this->SetupMesh(dev);
}

void Draw(ID3D11DeviceContext *devcon)
{
    UINT stride = sizeof(VERTEX);
    UINT offset = 0;

    devcon->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

    if (this->texture != nullptr)
        devcon->PSSetShaderResources(0, 1, &texture);

    devcon->Draw(vertices.size(), 0);
}
private:
std::vector<VERTEX> 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 = &vertices[0];

    hr = dev->CreateBuffer(&vbd, &initData, &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<int, int> controlpoints;
std::vector<Mesh> 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& jointname);

ID3D11ShaderResourceView *LoadTexture(ID3D11Device *dev, ID3D11DeviceContext *devcon, const char* texturefilename);
};

FBXLoader.cpp

#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->SetIOSettings(ioSettings);
}

FbxImporter *importer = FbxImporter::Create(fbxsdkManager, "");
fbxScene = FbxScene::Create(fbxsdkManager, "");

FbxGeometryConverter gConverter(fbxsdkManager);

bool bSuccess = importer->Initialize(filename, -1, fbxsdkManager->GetIOSettings());

bSuccess = importer->Import(fbxScene);

importer->Destroy();

FbxNode *fbxRootNode = fbxScene->GetRootNode();

ProcessSkeletonHeirarchy(fbxRootNode);

this->hwnd = hwnd;

ProcessNode(dev, devcon, fbxRootNode, &gConverter);
}

void FBXLoader::Draw(ID3D11DeviceContext * devcon)
{
for (int i = 0; i < meshes.size(); i++)
{
    meshes[i].Draw(devcon);
}
}

XMMATRIX FBXLoader::GetAnimatedMatrix(int index)
{
XMMATRIX bonematxm;
FbxAMatrix bonemat = skeleton.mJoints[index].mGlobalBindposeInverse; //* skeleton.mJoints[0].mAnimation->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->GetNodeAttribute() != nullptr)
    {
        FbxNodeAttribute::EType AttributeType = node->GetNodeAttribute()->GetAttributeType();

        if (AttributeType == FbxNodeAttribute::eMesh)
        {
            FbxMesh *mesh;

            mesh = (FbxMesh*)gConverter->Triangulate(node->GetNodeAttribute(), true);

            meshes.push_back(ProcessMesh(dev, devcon, mesh));
        }
    }

    for (int i = 0; i < node->GetChildCount(); i++)
    {
        ProcessNode(dev, devcon, node->GetChild(i), gConverter);
    }
}
}

Mesh FBXLoader::ProcessMesh(ID3D11Device * dev, ID3D11DeviceContext *devcon, FbxMesh * mesh)
{
std::vector<VERTEX> meshvertices;
ID3D11ShaderResourceView *meshtexture = nullptr;

FbxVector4 *vertices = mesh->GetControlPoints();

for (int j = 0; j < mesh->GetPolygonCount(); j++)
{
    int numVertices = mesh->GetPolygonSize(j);

    FbxLayerElementArrayTemplate<FbxVector2> *uvVertices = 0;
    mesh->GetTextureUV(&uvVertices, FbxLayerElement::eTextureDiffuse);

    for (int k = 0; k < numVertices; k++)
    {
        int controlPointIndex = mesh->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->GetAt(mesh->GetTextureUVIndex(j, k)).mData[0];
        vertex.tex.y = 1.0f - (float)uvVertices->GetAt(mesh->GetTextureUVIndex(j, k)).mData[1];

        controlpoints[controlPointIndex] = meshvertices.size();

        meshvertices.push_back(vertex);
    }
}

int materialcount = mesh->GetNode()->GetSrcObjectCount<FbxSurfaceMaterial>();

for (int i = 0; i < materialcount; i++)
{
    FbxSurfaceMaterial *material = (FbxSurfaceMaterial*)mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(i);

    if (material)
    {
        FbxProperty prop = material->FindProperty(FbxSurfaceMaterial::sDiffuse);

        const FbxTexture* texture = FbxCast<FbxTexture>(prop.GetSrcObject<FbxTexture>(0));
        const FbxFileTexture* filetexture = FbxCast<FbxFileTexture>(texture);

        ID3D11ShaderResourceView *meshctexture = LoadTexture(dev, devcon, filetexture->GetFileName());

        meshtexture = meshctexture;
    }
}

const FbxVector4 lT = mesh->GetNode()->GetGeometricTranslation(FbxNode::eSourcePivot);
const FbxVector4 lR = mesh->GetNode()->GetGeometricRotation(FbxNode::eSourcePivot);
const FbxVector4 lS = mesh->GetNode()->GetGeometricScaling(FbxNode::eSourcePivot);

FbxAMatrix geometryTransform = FbxAMatrix(lT, lR, lS);

for (unsigned int deformerIndex = 0; deformerIndex < mesh->GetDeformerCount(); ++deformerIndex)
{
    FbxSkin* skin = reinterpret_cast<FbxSkin*>(mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
    if (!skin)
        continue;

    for (unsigned int clusterIndex = 0; clusterIndex < skin->GetClusterCount(); ++clusterIndex)
    {
        FbxCluster* cluster = skin->GetCluster(clusterIndex);
        std::string jointname = cluster->GetLink()->GetName();
        unsigned int jointIndex = FindJointIndex(jointname);
        FbxAMatrix transformMatrix;
        FbxAMatrix transformLinkMatrix;
        FbxAMatrix globalBindposeInverseMatrix;

        cluster->GetTransformMatrix(transformMatrix);
        cluster->GetTransformLinkMatrix(transformLinkMatrix);
        globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix * geometryTransform;

        skeleton.mJoints[jointIndex].mGlobalBindposeInverse = globalBindposeInverseMatrix;
        skeleton.mJoints[jointIndex].mNode = cluster->GetLink();

        for (unsigned int i = 0; i < cluster->GetControlPointIndicesCount(); ++i)
        {
            int vertexid = controlpoints[cluster->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->GetControlPointWeights()[i];
            if (meshvertices[vertexid].weights.y == 0) meshvertices[vertexid].weights.y = cluster->GetControlPointWeights()[i];
            if (meshvertices[vertexid].weights.z == 0) meshvertices[vertexid].weights.z = cluster->GetControlPointWeights()[i];
            if (meshvertices[vertexid].weights.w == 0) meshvertices[vertexid].weights.w = cluster->GetControlPointWeights()[i];
        }

        FbxAnimStack* animstack = fbxScene->GetSrcObject<FbxAnimStack>(0);
        FbxString animstackname = animstack->GetName();
        FbxTakeInfo* takeinfo = fbxScene->GetTakeInfo(animstackname);
        FbxTime start = takeinfo->mLocalTimeSpan.GetStart();
        FbxTime end = takeinfo->mLocalTimeSpan.GetStop();
        FbxLongLong animationlength = end.GetFrameCount(FbxTime::eFrames30) - start.GetFrameCount(FbxTime::eFrames30) + 1;
        Keyframe** anim = &skeleton.mJoints[jointIndex].mAnimation;

        for (FbxLongLong i = start.GetFrameCount(FbxTime::eFrames30); i <= end.GetFrameCount(FbxTime::eFrames30); ++i)
        {
            FbxTime time;
            time.SetFrame(i, FbxTime::eFrames30);
            *anim = new Keyframe();
            (*anim)->mFrameNum = i;
            FbxAMatrix transformoffset = mesh->GetNode()->EvaluateGlobalTransform(1.0f) * geometryTransform;
            (*anim)->mGlobalTransform = transformoffset.Inverse() * cluster->GetLink()->EvaluateGlobalTransform(time);
            anim = &((*anim)->mNext);
        }
    }
}

return Mesh(dev, meshvertices, meshtexture);
}

void FBXLoader::ProcessSkeletonHeirarchy(FbxNode * rootnode)
{
for (int childindex = 0; childindex < rootnode->GetChildCount(); ++childindex)
{
    FbxNode *node = rootnode->GetChild(childindex);
    ProcessSkeletonHeirarchyre(node, 0, 0, -1);
}
}

void FBXLoader::ProcessSkeletonHeirarchyre(FbxNode * node, int depth, int index, int parentindex)
{
if (node->GetNodeAttribute() && node->GetNodeAttribute()->GetAttributeType() && node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eSkeleton)
{
    Joint joint;
    joint.mParentIndex = parentindex;
    joint.mName = node->GetName();
    skeleton.mJoints.push_back(joint);
}
for (int i = 0; i < node->GetChildCount(); i++)
{
    ProcessSkeletonHeirarchyre(node->GetChild(i), depth + 1, skeleton.mJoints.size(), index);
}
}

unsigned int FBXLoader::FindJointIndex(const std::string & jointname)
{
for (unsigned int i = 0; i < 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, &texture);
if (FAILED(hr))
    return nullptr;

return texture;
}

这是我的顶点着色器

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);

这是我的绘图代码

cb.mWorld = XMMatrixTranspose(m_World);
cb.mView = XMMatrixTranspose(m_View);
cb.mProjection = XMMatrixTranspose(m_Projection);
for (int i = 0; i < jasper->skeleton.mJoints.size(); i++)
    cb.bones[i] = XMMatrixTranspose(XMMatrixIdentity());
devcon->UpdateSubresource(pConstantBuffer, 0, nullptr, &cb, 0, 0);

devcon->VSSetShader(pVS, 0, 0);
devcon->VSSetConstantBuffers(0, 1, &pConstantBuffer);
devcon->PSSetShader(pPS, 0, 0);
devcon->PSSetSamplers(0, 1, &TexSamplerState);
model->Draw(devcon);

当我将顶点着色器中的所有骨骼矩阵设置为单位矩阵时,这就是我得到的。

enter image description here

当我使用单位矩阵时,不应该这样吗?

enter image description here

2 个答案:

答案 0 :(得分:1)

因为我有同样的问题。这是类似任务的实验室文档。希望这会有所帮助:

从FBX获取联合动画数据 此过程将允许我们从FBX联合层次结构中获取动画数据。您必须先完成此过程的上一步(&#34;在FBX和#34中获取BindPose的联合变换;)。

  1. 从您的FbxScene实例获取第一个FbxAnimStack 一个。 FbxScene可能有动画层,但我们只关注一个 湾有用的功能 一世。 FbxScene :: GetCurrentAnimationStack()
  2. 获取动画的持续时间 一个。有用的功能 一世。 FbxAnimStack :: GetLocalTimeSpan() II。 FbxTime :: GetDuration()
  3. 获取动画中所需时间模式的帧数 一个。有用的功能 一世。 FbxTime :: GetFrameCount(FbxTime :: EMode)FbxTime :: EMode :: eFrames24每秒提供24帧,建议使用
  4. 其余步骤将引用您的FbxNode * /父索引对的动态数组 一个。例如:struct my_fbx_joint {FbxNode * node; int parent_index;}; 湾例如:std :: vector&lt; my_fbx_joint&gt ;;
  5. 您需要一个关键帧类型 一个。例如:struct keyframe {double time; std :: vector joint; };
  6. 您需要一个动画片段类型 一个。例如:struct anim_clip {double duration; std :: vector frames; };
  7. 创建anim_clip以填充
  8. 对于帧数(跳过第0帧的绑定姿势) 一个。创建一个关键帧对象 湾确定此帧的FbxTime 一世。 FbxTime :: SetFrame(FbxLongLong,FbxMode :: EMode) C。将关键时间存储在当前关键帧中 d。对于第I部分关节数组中的每个FbxNode * 一世。获取该帧的节点评估全局变换
  9. 有用的功能 一个。 FbxNode :: EvaluateGlobalTransform(FbxTime) II。将此变换添加到关键帧的关节
  10. 保持索引位置一致。关节应该在关键帧中具有与绑定姿势中相同的索引 即将关键帧添加到anim_clip
  11. 编辑::我还会添加(取决于你使用的拓扑结构)来检查你正在使用的网格是否被三角化,因为过去对我来说已经解决了这样的问题。

答案 1 :(得分:0)

首先尝试仅用线条绘制骨骼...这可能会有所帮助 绘制完骨骼和角色之后!