当前位置: 首页 > news >正文

自然资源部网站绿色矿山建设手机网站 兼容

自然资源部网站绿色矿山建设,手机网站 兼容,用群晖如何做公司网站,网页设计师女生工资一段时间以来#xff0c;我一直想制作一个 FBX Exporter 将 FBX 文件转换为我自己的格式。 整个过程不是很顺利#xff0c;主要是FBX的官方文档不是很清楚。 另外#xff0c;由于 FBX 格式被许多应用程序使用#xff0c;而不仅仅是游戏引擎#xff0c;因此提供的示例代码没…一段时间以来我一直想制作一个 FBX Exporter 将 FBX 文件转换为我自己的格式。 整个过程不是很顺利主要是FBX的官方文档不是很清楚。 另外由于 FBX 格式被许多应用程序使用而不仅仅是游戏引擎因此提供的示例代码没有使用我们在游戏开发中使用的更常见的技术术语。 我搜索了互联网上几乎所有的角落来澄清事情以便我可以从 FBX SDK 的数据到我在游戏引擎中需要的数据有一个清晰的映射。 推荐用 NSDT设计器 快速搭建可编程3D场景。 因为我认为没有人发布过关于如何将 FBX 文件转换为自定义格式的清晰而详尽的教程所以我会这样做。 我希望这能帮助人们。 本教程将专门介绍游戏引擎。 基本上我会告诉读者如何获取他们的游戏引擎所需的数据。 对于诸如“如何初始化 FBX SDK”之类的内容请自行查看示例代码“ImportScene”示例在这方面非常有用。 如果你不了解骨骼动画如何工作以及需要哪些数据来使骨骼动画发生请查看 Buckeye 的文章“使用矩阵的蒙皮网格动画”。 这会很有帮助。 链接在这里。 1、网格数据 你要做的第一件事是获取网格数据 如果你可以将静态网格物体导入到引擎中那感觉已经相当不错了。 为了使本节更加清晰我选择首先向你展示如何遍历 FBX 文件中的网格。 这使我能够让你自上而下地了解收集网格数据需要做什么。 你不知道每个函数具体做什么但应该知道我们正在遍历网格每个三角形上的 3 个顶点。 稍后我将回到每个功能。 请注意有一些与动画混合信息相关的代码。 现在可以忽略它。 我们稍后会再讨论这个问题。 Exporter::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.mBlendingIndex; currBlendingInfo.mBlendingWeight currCtrlPoint-mBlendingInfo.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(); 首先请让我解释一下 FBX 如何存储有关网格的所有信息。 在 FBX 中我们有术语控制点Control Point基本上控制点是一个物理顶点。 例如你有一个立方体那么你有 8 个顶点Vertex。 这8个顶点是FBX文件中仅有的8个“控制点”。 因此如果需要你可以互换使用“顶点”和“控制点”。 位置信息存储在控制点中。 以下代码将提供网格所有顶点的位置 // 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(currMesh-GetControlPointAt(i).mData[0]); currPosition.y static_cast(currMesh-GetControlPointAt(i).mData[1]); currPosition.z static_cast(currMesh-GetControlPointAt(i).mData[2]); currCtrlPoint-mPosition currPosition; mControlPoints currCtrlPoint; } } 然后你会问“我如何获得 UV、法线、切线、副法线” 好吧请想象一下这样的网格你有网格的主体但这只是它的几何形状。 该物体没有任何关于其表面的信息。 换句话说你有这个形状但你没有任何关于这个形状的表面看起来如何的信息。 FBX 引入了这种层Layer的感觉它覆盖了网格体的主体。 这就像你有一个盒子然后用礼品纸包裹它。 这张礼品纸是FBX中网格的图层。 在图层中你可以获取UV、法线、切线、副法线的信息。 然而你可能已经问过我了。 如何将控制点与图层中的信息关联起来 好吧这是非常棘手的部分请让我展示一些代码然后逐行解释它。 不失一般性我以 Binormal 为例 在查看该函数之前我们先回顾一下它的参数。 FbxMesh* inMesh我们尝试导出的网格int inCtrlPointIndex控制点的索引。 我们需要这个因为我们想要将图层信息与顶点控制点相关联int inVertexCounter这是我们正在处理的当前顶点的索引。XMFLOAT3 outNormal输出。 我们通过引用传递以便我们可以在该函数内修改此变量并将其用作我们的输出看到这些参数后你可能会问我“既然你说 控制带你基本上是 FBX SDK 中的顶点为什么有 inCtrlPointIndex 和 inVertexCounter 他们不是同一个东西吗” 不它们不一样。 正如我之前所解释的控制点是几何体上的物理顶点。 让我们以四边形为例。 给定一个四边形2 个三角形有多少个控制点 答案是 4。但是我们基于三角形的游戏引擎中有多少个顶点呢 答案是 6因为我们有 2 个三角形每个三角形有 3 个顶点。 2 * 3 6 FBXSDK 的控制点和我们的顶点之间的主要区别在于我们的顶点具有“每个三角形”的感觉但 FBXSDK 的控制点没有。 我们将在下面的代码解释中回到这一点。 因此如果你仍然对游戏引擎中 FBXSDK 的控制点和顶点没有非常清晰的了解请不要担心。 要记住的一件事是在该函数之外我们使用循环来遍历该网格中所有三角形的所有顶点。 如果你感到困惑并且不知道“我们正在使用循环遍历此网格中所有三角形的所有顶点”是什么意思请查看本文开头的内容。 这就是为什么我们可以有像 inCtrlPointIndex 和 inVertexCounter 这样的参数。 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(vertexNormal-GetDirectArray().GetAt(inCtrlPointIndex).mData[0]); outNormal.y static_cast(vertexNormal-GetDirectArray().GetAt(inCtrlPointIndex).mData[1]); outNormal.z static_cast(vertexNormal-GetDirectArray().GetAt(inCtrlPointIndex).mData[2]); } break; case FbxGeometryElement::eIndexToDirect: { int index vertexNormal-GetIndexArray().GetAt(inCtrlPointIndex); outNormal.x static_cast(vertexNormal-GetDirectArray().GetAt(index).mData[0]); outNormal.y static_cast(vertexNormal-GetDirectArray().GetAt(index).mData[1]); outNormal.z static_cast(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(vertexNormal-GetDirectArray().GetAt(inVertexCounter).mData[0]); outNormal.y static_cast(vertexNormal-GetDirectArray().GetAt(inVertexCounter).mData[1]); outNormal.z static_cast(vertexNormal-GetDirectArray().GetAt(inVertexCounter).mData[2]); } break; case FbxGeometryElement::eIndexToDirect: { int index vertexNormal-GetIndexArray().GetAt(inVertexCounter); outNormal.x static_cast(vertexNormal-GetDirectArray().GetAt(index).mData[0]); outNormal.y static_cast(vertexNormal-GetDirectArray().GetAt(index).mData[1]); outNormal.z static_cast(vertexNormal-GetDirectArray().GetAt(index).mData[2]); } break; default: throw std::exception(Invalid Reference); } break; } } 好吧这很长但请不要害怕。 其实很简单。 这为我们获取了图层中的法线信息 FbxGeometryElementNormal* vertexNormal inMesh-GetElementNormal(0); 第一个 switch 语句是关于 MappingMode() 的。 对于游戏引擎我认为我们只需要考虑 FbxGeometryElement::eByControlPoint 和 FbxGeometryElement::eByPolygonVertex 。 让我解释一下这两种模式。 正如我所说控制点基本上就是顶点。 然而有一个问题。 尽管立方体有 8 个控制点但如果你希望立方体看起来正确则其法线将超过 8 个。 原因是如果你有锐利的边缘我们必须为同一控制点分配多个法线以保证锐利的感觉。 这就是我们的游戏引擎中的顶点概念出现的时候因为即使立方体的顶点具有相同的位置在游戏引擎中你也很可能最终得到 3 个位置相同但 3 个顶点 不同的法线。 因此当你没有锋利边缘的情况时 FbxGeometryElement::eByControlPoint 因此每个控制点只有一个法线。 FbxGeometryElement::eByPolygonVertex 是当你有锐边并且需要获取每个面上每个顶点的法线时因为每个面都为同一控制点分配了不同的法线。 因此 FbxGeometryElement::eByControlPoint 意味着我们可以通过控制点的索引来精确定位控制点的法线而 FbxGeometryElement::eByPolygonVertex 意味着我们可以通过顶点的索引来精确定位面上顶点的法线。 这是一个更具体、更深入的例子说明了FBX SDK的控制点和顶点在游戏引擎中的区别以及为什么当我谈论这个函数的参数时我说我们必须同时传入 inCtrlPointIndex和 inVertexCounter。 因为我们不知道需要哪一个来获取我们需要的信息所以我们最好将两者都传入。 现在我们有另一个 switch 语句嵌套在里面我们在 ReferenceMode() 上“切换”。 这是 FBX 正在做的某种优化与计算机图形学中的索引缓冲区相同。 你不想多次使用相同的 Vector3 相反可以使用其索引来引用它。 FbxGeometryElement::eDirect 意味着你可以直接使用控制点索引或面顶点索引来引用我们的法线。 FbxGeometryElement::eIndexToDirect 意味着使用控制点索引或面顶点索引只会给我们一个指向我们想要的法线的索引我们必须使用这个索引来找到实际的法线。 这行代码为我们提供了所需的索引 int index vertexNormal-GetIndexArray().GetAt(inVertexCounter); 这些是提取网格的位置和“层”信息的主要步骤。 现在我们转向动画这是 FBX 导出的难点。 2、动画数据 因此让我们考虑一下 FBX 需要什么才能使动画在我们的渲染器游戏引擎中正常工作。 骨骼层次结构。 哪个关节是哪个关节的父关节对于每个顶点我们需要 4 个 SkinningWeight-JointIndex 对每个关节的绑定姿势矩阵用于计算全局绑定姿势的逆矩阵时间 t 时的变换矩阵以便我们可以将网格变换为该姿势以实现动画 获得骨骼层次结构非常简单基本上我们从场景的根节点执行递归深度优先搜索然后逐层深入。 节点是 FBX 场景的构建块。 FBX 文件中有许多节点每种类型的节点都包含某种类型的信息。 如果一个节点是骨架类型我们将它添加到我们的关节列表中并且它的索引将只是列表的大小。 因此我们可以保证父级的索引始终小于子级的索引。 如果你想要存储本地变换并手动计算子项在时间 t 的变换这是必要的。 但如果你像我一样使用全局转换你不一定需要这样。 注意如果你不熟悉深度优先搜索的概念。 看看这个页面 和这个页面。 阅读这些页面后你可能会问“为什么我们不需要跟踪访问过的节点” 答案是骨骼层次结构是一棵树而不是一张图。 void FBXExporter::ProcessSkeletonHierarchy(FbxNode* inRootNode) { for (int childIndex 0; childIndex inRootNode-GetChildCount(); childIndex) { FbxNode* currNode inRootNode-GetChild(childIndex); ProcessSkeletonHierarchyRecursively(currNode, 0, 0, -1); } } // inDepth is not needed here, I used it for debug but forgot to remove it void FBXExporter::ProcessSkeletonHierarchyRecursively(FbxNode* inNode, int inDepth, int myIndex, int inParentIndex) { if(inNode-GetNodeAttribute() inNode-GetNodeAttribute()-GetAttributeType() inNode-GetNodeAttribute()-GetAttributeType() FbxNodeAttribute::eSkeleton) { Joint currJoint; currJoint.mParentIndex inParentIndex; currJoint.mName inNode-GetName(); mSkeleton.mJoints.push_back(currJoint); } for (int i 0; i inNode-GetChildCount(); i) { ProcessSkeletonHierarchyRecursively(inNode-GetChild(i), inDepth 1, mSkeleton.mJoints.size(), myIndex); } } 现在我们需要获取每个顶点的 SkinningWeight-JointIndex 对。 不幸的是我的动画代码不是很干净所以下面的函数同时执行步骤 2、3、4。 我将仔细检查代码请不要失去耐心。 这主要是因为 FBX 存储信息的方式阻止我有效地在单独的函数中获取数据。 如果我想分离我的代码我需要多次遍历相同的数据。 在看任何代码之前请让我解释一下 FBX SDK 中使用的术语。 我认为这是大多数人感到困惑的部分因为 FBX SDK 的关键字与我们游戏开发人员的关键字不匹配。 在FBX中有一个叫做变形器Deformer的东西。 我将变形器视为使网格变形的一种方式。 在 Maya 中你可以使用骨骼变形器但也可以使用约束Constraints来使网格变形。 我认为你可以将“变形器”视为网格的整个骨架。 在每个“变形器”我认为通常网格只有一个内都有簇Cluster。 每个簇既是又不是一个关节……你可以把一个簇看成一个关节但实际上每个簇内部都有一个“链接”。 这个“链接”实际上是真正的关节它包含了我需要的有用信息。 现在我们深入研究代码 void FBXExporter::ProcessJointsAndAnimations(FbxNode* inNode) { FbxMesh* currMesh inNode-GetMesh(); unsigned int numOfDeformers currMesh-GetDeformerCount(); // This geometry transform is something I cannot understand // I think it is from MotionBuilder // If you are using Maya for your models, 99% this is just an // identity matrix // But I am taking it into account anyways...... FbxAMatrix geometryTransform Utilities::GetGeometryTransformation(inNode); // A deformer is a FBX thing, which contains some clusters // A cluster contains a link, which is basically a joint // Normally, there is only one deformer in a mesh for (unsigned int deformerIndex 0; deformerIndex numOfDeformers; deformerIndex) {// There are many types of deformers in Maya, // We are using only skins, so we see if this is a skin FbxSkin* currSkin reinterpret_cast(currMesh-GetDeformer(deformerIndex, FbxDeformer::eSkin)); if (!currSkin) { continue; } unsigned int numOfClusters currSkin-GetClusterCount(); for (unsigned int clusterIndex 0; clusterIndex numOfClusters; clusterIndex) { FbxCluster* currCluster currSkin-GetCluster(clusterIndex); std::string currJointName currCluster-GetLink()-GetName(); unsigned int currJointIndex FindJointIndexUsingName(currJointName); FbxAMatrix transformMatrix; FbxAMatrix transformLinkMatrix; FbxAMatrix globalBindposeInverseMatrix; currCluster-GetTransformMatrix(transformMatrix); // The transformation of the mesh at binding time currCluster-GetTransformLinkMatrix(transformLinkMatrix); // The transformation of the cluster(joint) at binding time from joint space to world space globalBindposeInverseMatrix transformLinkMatrix.Inverse() * transformMatrix * geometryTransform; // Update the information in mSkeleton mSkeleton.mJoints[currJointIndex].mGlobalBindposeInverse globalBindposeInverseMatrix; mSkeleton.mJoints[currJointIndex].mNode currCluster-GetLink(); // Associate each joint with the control points it affects unsigned int numOfIndices currCluster-GetControlPointIndicesCount(); for (unsigned int i 0; i numOfIndices; i) { BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.mBlendingIndex currJointIndex; currBlendingIndexWeightPair.mBlendingWeight currCluster-GetControlPointWeights(); mControlPoints[currCluster-GetControlPointIndices()]-mBlendingInfo.push_back(currBlendingIndexWeightPair); } // Get animation information // Now only supports one take FbxAnimStack* currAnimStack mFBXScene-GetSrcObject(0); FbxString animStackName currAnimStack-GetName(); mAnimationName animStackName.Buffer(); FbxTakeInfo* takeInfo mFBXScene-GetTakeInfo(animStackName); FbxTime start takeInfo-mLocalTimeSpan.GetStart(); FbxTime end takeInfo-mLocalTimeSpan.GetStop(); mAnimationLength end.GetFrameCount(FbxTime::eFrames24) - start.GetFrameCount(FbxTime::eFrames24) 1; Keyframe** currAnim mSkeleton.mJoints[currJointIndex].mAnimation; for (FbxLongLong i start.GetFrameCount(FbxTime::eFrames24); i end.GetFrameCount(FbxTime::eFrames24); i) { FbxTime currTime; currTime.SetFrame(i, FbxTime::eFrames24);*currAnim new Keyframe(); (*currAnim)-mFrameNum i; FbxAMatrix currentTransformOffset inNode-EvaluateGlobalTransform(currTime) * geometryTransform; (*currAnim)-mGlobalTransform currentTransformOffset.Inverse() * currCluster-GetLink()-EvaluateGlobalTransform(currTime); currAnim ((*currAnim)-mNext); } } } // Some of the control points only have less than 4 joints // affecting them. // For a normal renderer, there are usually 4 joints // I am adding more dummy joints if there isnt enough BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.mBlendingIndex 0; currBlendingIndexWeightPair.mBlendingWeight 0; for(auto itr mControlPoints.begin(); itr ! mControlPoints.end(); itr) { for(unsigned int i itr-second-mBlendingInfo.size(); i 4; i) { itr-second-mBlendingInfo.push_back(currBlendingIndexWeightPair); } } } 一开始我有这个 // This geometry transform is something I cannot understand // I think it is from MotionBuilder // If you are using Maya for your models, 99% this is just an // identity matrix // But I am taking it into account anyways...... FbxAMatrix geometryTransform Utilities::GetGeometryTransformation(inNode); 嗯这是我在 FBX SDK 论坛上看到的。 那里的官员告诉我们应该考虑“几何变换”。 但根据我的经验大多数时候这个“GeometricTransform”只是一个单位矩阵。 无论如何要获得这个“GeometricTransform”请使用以下函数 FbxAMatrix Utilities::GetGeometryTransformation(FbxNode* inNode) { if (!inNode) { throw std::exception(Null for mesh geometry); } const FbxVector4 lT inNode-GetGeometricTranslation(FbxNode::eSourcePivot); const FbxVector4 lR inNode-GetGeometricRotation(FbxNode::eSourcePivot); const FbxVector4 lS inNode-GetGeometricScaling(FbxNode::eSourcePivot); return FbxAMatrix(lT, lR, lS); } 这段代码中最重要的事情是如何获得每个关节的全局绑定姿势的倒数。 这部分非常棘手并且搞砸了很多人。 我将详细解释这一点。 FbxAMatrix transformMatrix; FbxAMatrix transformLinkMatrix; FbxAMatrix globalBindposeInverseMatrix; currCluster-GetTransformMatrix(transformMatrix); // The transformation of the mesh at binding time currCluster-GetTransformLinkMatrix(transformLinkMatrix); // The transformation of the cluster(joint) at binding time from joint space to world space globalBindposeInverseMatrix transformLinkMatrix.Inverse() * transformMatrix * geometryTransform; // Update the information in mSkeleton mSkeleton.mJoints[currJointIndex].mGlobalBindposeInverse globalBindposeInverseMatrix; 那么我们就从这个 GetTransformMatrix开始吧。 TransformMatrix 实际上是一个遗留的东西。 它是整个网格在绑定时的全局变换并且所有簇都具有完全相同的变换矩阵。 如果你的艺术家有良好的习惯并且在装配模型之前他们在模型的所有通道上“冻结变换”则不需要此矩阵。 如果你的艺术家执行“冻结变换”那么这个矩阵将只是一个单位矩阵。 现在我们继续 GetTransformLinkMatrix。 这就是动画导出代码的本质。 这是 Maya 中绑定时簇关节从关节空间到世界空间的变换。 现在我们已经准备好了我们可以得到每个关节的全局绑定姿势的逆。 我们最终想要的是下面代码中的 InverseOfGlobalBindPoseMatrix VertexAtTimeT TransformationOfPoseAtTimeT * InverseOfGlobalBindPoseMatrix * VertexAtBindingTime为了得到这个我们这样做 transformLinkMatrix.Inverse() * transformMatrix * GeometryTransform现在我们距离动画还有两步。 我们需要获取每个顶点的 SkinningWeight-JointIndex 对并且仍然需要获取动画中不同时间的变换。让我们首先处理 SkinningWeight-JointIndex 对。 在我们的游戏引擎中我们有这样的关系 Vertex - 4 SkinningWeight-JointIndex 对。 然而在 FBX SDK 中这种关系是相反的。 每个簇都有一个其影响的所有控制点顶点及其影响程度的列表。 下面的代码以我们喜欢的格式获取关系但请记住当我处理控制点时我根据控制点的索引将所有控制点存储到地图中。 这就是我们可以获利的地方。 有了这个映射我们可以在 O(1) 内查找并更新集群影响的控制点。 // Associate each joint with the control points it affects unsigned int numOfIndices currCluster-GetControlPointIndicesCount(); for (unsigned int i 0; i numOfIndices; i) { BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.mBlendingIndex currJointIndex; currBlendingIndexWeightPair.mBlendingWeight currCluster-GetControlPointWeights(); mControlPoints[currCluster-GetControlPointIndices()]-mBlendingInfo.push_back(currBlendingIndexWeightPair); } 现在我们只需要拼图中的最后一块动画中时间 t 的变换。 请注意这部分是我做得不好的地方我的方式不是很优化因为我得到了每个关键帧。 理想情况下应该做的是获取密钥并在它们之间进行插值但我想这是空间和速度之间的权衡。 另外我没有认真研究 FBX 的动画层次结构。 实际上FBX 文件中存储了一条动画曲线通过一些工作你可以访问它并获得所需的精简和干净。 // Now only supports one take FbxAnimStack* currAnimStack mFBXScene-GetSrcObject(0); FbxString animStackName currAnimStack-GetName(); mAnimationName animStackName.Buffer(); FbxTakeInfo* takeInfo mFBXScene-GetTakeInfo(animStackName); FbxTime start takeInfo-mLocalTimeSpan.GetStart(); FbxTime end takeInfo-mLocalTimeSpan.GetStop(); mAnimationLength end.GetFrameCount(FbxTime::eFrames24) - start.GetFrameCount(FbxTime::eFrames24) 1; Keyframe** currAnim mSkeleton.mJoints[currJointIndex].mAnimation; for (FbxLongLong i start.GetFrameCount(FbxTime::eFrames24); i end.GetFrameCount(FbxTime::eFrames24); i) { FbxTime currTime; currTime.SetFrame(i, FbxTime::eFrames24); *currAnim new Keyframe(); (*currAnim)-mFrameNum i; FbxAMatrix currentTransformOffset inNode-EvaluateGlobalTransform(currTime) * geometryTransform; (*currAnim)-mGlobalTransform currentTransformOffset.Inverse() * currCluster-GetLink()-EvaluateGlobalTransform(currTime); currAnim ((*currAnim)-mNext); } 这部分非常简单 - 唯一需要注意的是 Maya 目前不支持多镜头动画也许 MotionBuilder 支持。 我会根据有多少人阅读这篇文章来决定是否写导出材料但这很简单可以通过“ImportScene”示例来学习 3、DirectX 和 OpenGL 转换 我对此 FBX 导出器的目标是提供一种从 FBX 文件中提取数据的方法并以自定义格式输出数据以便阅读器的渲染器可以获取数据并渲染它。 渲染器内部不需要任何转换因为所有转换工作都落在导出器本身上。 在我说任何内容之前我需要澄清一下只有当你在 Maya 中制作模型/动画并使用其默认坐标系X-Right、Y-Up、 Z-超出屏幕。 如果你想将模型/动画导入OpenGL那么更有可能你不需要执行任何额外的转换步骤因为我认为默认情况下OpenGL具有与Maya相同的右手坐标系即X-Right Y 向上Z 超出屏幕。 在FBX SDK的示例代码“ViewScene”中没有对数据进行任何转换它使用OpenGL作为渲染器并使用OpenGL中的默认坐标系。 因此如果你确实遇到麻烦请查看该代码。 但是如果你指定自己的坐标系则可能需要进行一些转换。 现在是 DirectX 的时代了我在网上看到大多数问题都来自于人们想要在 DirectX 中渲染 FBX 模型/动画的情况。 因此如果你想将模型/动画导入 DirectX很可能需要进行一些转换。 我只会解决左手“X-RightY-UpZ-Into Screen”坐标系与背面剔除的情况因为从我读到的帖子来看大多数人在使用时都使用这个系统 DirectX。 这确实意味着任何一般性的事情 这只是我的经验观察。 你需要执行以下操作将坐标从右手坐标系统“X-RightY-UpZ-Out Of Screen”转换为左手“X-RightY-UpZ-Into Screen”系统 位置、法线、副法线、切线我们需要对 UV 向量的 Z 分量求反 使 V 1.0f - V三角形的顶点顺序从 Vertex0、Vertex1、Vertex2 更改为 Vertex0 Vertex2、Vertex1基本上颠倒剔除顺序矩阵获取矩阵的平移分量对其 Z 分量取反获取矩阵的旋转分量对其 X 和 Y 分量取反我认为如果你使用 XMMath 库则不需要进行转置。 要使用我的转换方式你需要分解矩阵并分别更改其平移、旋转和缩放。 幸运的是FBX SDK 提供了分解矩阵的方法只要你的矩阵是 FbxAMatrix(FBX 仿射矩阵)。 下面的示例代码向你展示了如何操作 FbxAMatrix input; //Assume this matrix is the one to be converted. FbxVector4 translation input.GetT(); FbxVector4 rotation input.GetR(); translation.Set(translation.mData[0], translation.mData[1], -translation.mData[2]); // This negate Z of Translation Component of the matrix rotation.Set(-rotation.mData[0], -rotation.mData[1], rotation.mData[2]); // This negate X,Y of Rotation Component of the matrix // These 2 lines finally set input to the eventual converted result input.SetT(translation); input.SetR(rotation); 如果你的动画有Scaling你需要自己弄清楚需要做什么转换因为我没有遇到过Scaling发生的情况。 4、局限性及超越 因此本教程仅旨在帮助你开始使用 FBXSDK。 我自己是个菜鸟所以我的很多技术可能效率很低。 在这里我把我认为存在的问题列出来。 在此过程中读者可以自行决定是否使用我的技术以及需要注意什么。 该转换方法仅适用于从 Maya 导出的具有 Maya 右手 X-Right、Y-Up、Z-Out 坐标系的模型/动画。 我的转换技术很可能不适用于其他建模软件Blender、MotionBuilder、3ds Max我提取动画的方式效率低下。 我需要在导出动画之前烘焙动画然后以 24 帧/秒的速率获取所有关键帧。 这可能会导致巨大的内存消耗。 如果你知道如何使用关键帧而不是关键帧请通过下面的评论告诉我。我的转换方法不处理动画中的缩放。 正如你从我的代码中看到的当我提取动画时我从不处理转换矩阵中的缩放分量。 因此你需要自己弄清楚你的动画是否具有缩放功能。在本教程中我没有包含删除重复顶点的代码但实际上如果你使用我的方法在不进行任何优化的情况下导出 FBX 文件最终会得到很多重复顶点。 我做了一个比较优化导出可以将文件大小减少 2/3…你会出现重复的原因是如果你遍历网格中每个三角形的每个顶点尽管相同的 Control 法线不同的点会很好处理法线相同的同一个控制点会被计数1次以上 原文链接FBX SDK简明教程 — BimAnt
http://www.tj-hxxt.cn/news/134381.html

相关文章:

  • 电商网站建设步骤东莞资深网站建设
  • 网友让你建网站做商城上海外贸学院现在是什么学校
  • 电子商务网站 功能广州论坛网站建设
  • vue做网站cms企业门户网站什么意思
  • c 网站开发案例大全做推广网站的文章
  • 做网站标题居中代码深圳网站设计哪里好
  • 鄱阳有做百度网站的在线查询网站开发语言
  • 外贸网站 源码网站管理方案
  • 长沙建一个网站多少钱残联网站建设概况
  • 做电影网站程序哪个好为什么不能去外包公司
  • 做网站如何引用头部wordpress商业站
  • 网站建设单位是什么意思四川网站建设设计公司哪家好
  • 菠菜网站建设网店推广有哪些
  • 什么网站做任务可以赚钱茶叶企业建设网站
  • 广州南沙区建设和交通局网站办公室设计平面图
  • 网站升级改版html网页设计作品下载
  • 网站获利模式网站开发相关会议
  • 有的网站域名解析错误js页面wordpress
  • 门户网站 流量云南网络营销文化优化
  • 国外优秀网站建设公司wordpress主机怎么建站
  • 网站黑白了阿里巴巴网站规划
  • 上海网站建设选缘魁 -企查软件开发外包公司
  • 驾校网站模版免费域名注册网站有哪些
  • 网站建设与维护费网站建设的主要内容
  • 沈阳网站设计制作公司淘宝联盟网站推广怎么做
  • 企业网站源码asp怎么做公司免费网站
  • 扬州做机床公司网站百度怎么建网站
  • 吉隆网站建设北京住房和城乡建设部网站首页
  • 天眼查登录入口域名seo站长工具
  • 网站策划运营电子技术培训机构