2者都是模型格式,不要问我为什么要这么转,也不要问我为什么不走插件,有些情况我也说不清楚
Fbx转化为md5
简述:
fbx和md5都是3d模型格式就不多作介绍了,直接进入主题;我们的目标是把fbx转化为md5,md5的mesh和animator是分开的,mesh和animator会分别介绍。
既然要解析fbx,那么就必须先对其足够的了解,才能对解析出来的数据做正确的处理,少走弯路。
FBX(这里转载翻译后的fbx简单说明):
fbx是一种封闭的模型格式,这不仅说它通常作为二进制文件出现,而且是目前只能使用Autodesk提供的FBX SDK来操控这种文件。事实上,无论是3DS Max还是其SDK内置的Converter工具,都可以把其转换成ASCII的文本格式,虽然看上去有点JSON的样子,可事实上是全自给的“仅供观赏”的数据堆,也没有spec,通常也不会有人使用这种方式输出模型。好了,看来是必须借助其SDK了,所以首先要做的一件不太让人愉快的事情:加入这个SDK的库(lib&dll)。现在我用的是最新的fbxsdk-2013.3,下文仅就这版本兼容的模型而言。(注意,在预处理器选项中加入FBXSDK_NEW_API和FBXSDK_SHARED,如果你不想编译器抱怨一大堆的话。)
这里首先说一下,对于Fbx,它保存的最大集合是一个Scene(场景),跟很多的游戏引擎所使用的概念是一致的,就是用场景节点树来组织成一个场景。以前的3ds虽然也是树状地组织数据,但它是把不同类型数据堆抽象成节点,而Fbx/Dae则是纯粹地表述场景节点。所以对于后者们来说,即使把场景中多个物体/模型保存到同一个模型文件里,也是可以的,只不过是不同名字的节点而已,甚至把灯光、相机也可以抽象成节点而已。当然,对于我们编程者来说,一个模型文件仅仅对应一个模型是最自然的。所以接下来我只会谈及抽取模型和动画本身信息(事实上动画信息并非必须的——fbx和dae文件在没有动画或骨骼信息时,也就是静态模型了),不相关的部分则不涉及也不关心。
因为有其自身的SDK帮我们分析fbx文件,所以对于fbx,只要去获取SDK的FbxImporter读取的结果来为我们所用就可以了——问题是要知道怎么获取我们需要的数据,你要让它直接告诉你每个网格数据的各个直接可用的顶点属性数组,那可为难了。我们还是必须把它分析出来的数据,转换成我们需要的数据的。所以还是先想清楚我们需要什么数据:1.动画信息(如果有的话);2.网格信息;3.关联前两者的骨骼信息(如果有的话)。在导入MD5模型时,其实也是一个寻找这些信息的过程(不过MD5可是把动画信息另外封成一个md5anim文件而已)。
C++代码
1. FbxScene *pScene = FbxScene::Create(pFbxManager, "ImporterScene");
2.
3. pSdkImporter->Import(pScene);
4.
5. PrepareAnimationInfo((Scene*)pScene);
6.
7. FbxNode *pRootNode = pScene->GetRootNode();
8.
9. if (pRootNode)
10. {
11. for (int i = 0; i < pRootNode->GetChildCount(); ++i)
12. {
13. FbxNode *pNode = pRootNode->GetChild(i);
14.
15. ProcessNode((Node*)pNode, szResDirectory);
16. }
17. }
18.
19. SetupJointKeyFrameInfo();
在这里,我首先获取到这个场景(Scene),然后用PrepareAnimationInfo来预先查找场景中存在的动画信息(这里只简单查询动画的基本信息,通过Scene内的各FbxAnimStack下查各FbxAnimLayer,每个Anim Layer保存着一个动画,这里是把这些Layer的地址先存起来),然后获取场景的根节点,逐个处理其下属节点( ProcessNode函数里再轮询处理该节点的下属节点,递归调用 ProcessNode,完成整棵场景树的深度遍历),最后就是SetupJointKeyFrameInfo,获取具体的动画数据并把前两者的信息结合起来。
场景节点除了网格对象(FbxNodeAttribute::eMesh)外还有其他多种类型,前面说过了,省略。对于每个Mesh网格对象,我们要得到它的几个顶点属性数组:位置、纹理坐标、影响的骨骼点(Joint)个数,以及这些骨骼点的索引(Index)和影响因子(Bias),另外对于法线切线这些,也可以顺便获取也可以自行计算。注意除了位置(和顶点属性索引)外其余属性并非每个mesh都有,注意判断了。其中,获取骨骼蒙皮信息(也就是这个Mesh的Skin)还是很值得注意的:
C++代码
1. FbxSkin *pSkinDeformer = (FbxSkin *)pMesh->GetDeformer(0, FbxDeformer::eSkin);
2.
3. for (int i = 0; i < pSkinDeformer->GetClusterCount(); ++i)
4. {
5. FbxCluster *pCluster = pSkinDeformer->GetCluster(i);
6.
7. int nInfluencedPointIndexCount = pCluster->GetControlPointIndicesCount();
8. int *pInfluencedPointIndice = pCluster->GetControlPointIndices();
9. double *pInfluencedPointWeights = pCluster->GetControlPointWeights();
10.
11. if (pLinkingBoneNode && pInfluencedPointIndice && pInfluencedPointWeights)
12. {
13. t3DJoint *pJoint = new t3DJoint((pCluster->GetLink()->GetName());
14.
15. pCluster->GetTransformMatrix(transMatrix);
16. pCluster->GetTransformLinkMatrix(transLinkMatrix);
17.
18. GetMatrixValue(transMatrix.Inverse(), &pJoint->mtPreFramePosed);
19. GetMatrixValue(transLinkMatrix, &pJoint->mtBindPose);
20.
21. transLinkMatrix = transLinkMatrix.Inverse() * transMatrix;
22. GetMatrixValue(transLinkMatrix, &pJoint->mtPostFramePosed);
23.
24. AddJoint(pJoint);
25.
26. for (int iPtIndex = 0; iPtIndex < nInfluencedPointIndexCount; ++iPtIndex)
27. {
28. int nVertIndex = pInfluencedPointIndice[iPtIndex];
29.
30. tVertWeights.nAttachJointIndex = GetJointCount() - 1;
31. tVertWeights.fWeightBias = (float)pInfluencedPointWeights[iPtIndex];
32.
33. std::map<int, std::vector<t3DVertWeights>>::iterator pFind = VertJointInfo.find(nVertIndex);
34.
35. if (VertJointInfo.end() != pFind)
36. pFind->second.push_back(tVertWeights);
37. else
38.