模型重心坐标就是在模型正中心那个点的坐标。本文介绍一种方法,可以通过代码的方式自动获取模型重心坐标。本方式适用于常用的所有模型。
我们都学过初中几何,可以知道三角形重心是三角形三条中线的交点。当几何体为匀质物体时,重心与形心重合。下图中O为三角形的重心。
换算成笛卡尔坐标系 三角形三个顶点为(x1,y1),(x2,y2),(x3,y3) 那么重心坐标 =((X1+X2+X3)/3,(Y1+Y2+Y3)/3) 到了我们三维模型中,重心坐标依旧和这个公式类似,等于所有三角面重心点之和的平均值。 模型重心如下图所示:
这里我们通过assimp库来获取模型的重心。关于assimp,参考我们上一篇文章。三维模型格式转换神器-assimp
这里我们以fbx模型为例,来说明获取重心的步骤。分为以下几个步骤: 1、加载模型获取aiScene 2、遍历aiScene下RootNode下的所有节点 3、获取aiMesh来计算模型的重心
加载模型示例代码如下:
auto inFile = R"(tt2.fbx)";
Assimp::Importer mImporter;
const aiScene *mScenePtr = mImporter.ReadFile(inFile, aiProcess_MakeLeftHanded);
if (nullptr == mScenePtr)
{
std::cout << "nullptr == mScenePtr" << std::endl;
return -1;
}
整个assimp的场景是一个树状结构,从root节点开始,如下图
我们通过递归进行遍历
void FindMesh(const aiScene *scene, aiNode *node)
{
FindMeshInfo(scene, node);
for (unsigned int m = 0; m < node->mNumChildren; ++m)
{
FindMesh(scene, node->mChildren[m]);
}
}
调用递归遍历函数如下:
auto rootNode = mScenePtr->mRootNode;
FindMesh(mScenePtr, rootNode);
重心的计算是最复杂的部分,分为两个步骤。 第一步:计算网格体的重心 第二步:加上变换矩阵 变换矩阵是指增加在模型上面的平移旋转缩放的变换矩阵,从而导致模型的重心位置发生变化。
计算网格体的重心又细分为一下两步: 1、计算每个三角面的重心点 2、计算所有三角面重心之和的平均值
代码示例
aiVector3D nodeCenter(0, 0, 0);
for (unsigned int meshi = 0; meshi < node->mNumMeshes; meshi++)
{
auto meshIndex = node->mMeshes[meshi];
auto mesh = scene->mMeshes[meshIndex];
aiVector3D meshCenter(0, 0, 0);
for (unsigned int facei = 0; facei < mesh->mNumFaces; facei++)
{
aiVector3D faceCenter(0, 0, 0);
auto face = mesh->mFaces[facei];
for (unsigned int indicei = 0; indicei < face.mNumIndices; indicei++)
{
auto indice = face.mIndices[indicei];
faceCenter = faceCenter + mesh->mVertices[indice];
}
faceCenter = faceCenter /= (ai_real)face.mNumIndices;
meshCenter = meshCenter + faceCenter;
}
meshCenter = meshCenter /= (ai_real)mesh->mNumFaces;
nodeCenter = nodeCenter + meshCenter;
}
nodeCenter = nodeCenter /= (ai_real)node->mNumMeshes;
加上旋转变换矩阵示例如下:
nodeCenter = nodeCenter /= (ai_real)node->mNumMeshes;
nodeCenter = nodeCenter *= node->mTransformation;
auto parent = node->mParent;
while (true)
{
if (nullptr == parent)
{
break;
}
std::string name = parent->mName.C_Str();
nodeCenter = nodeCenter *= parent->mTransformation;
parent = parent->mParent;
}
std::cout << node->mName.C_Str() << std::endl;
std::cout << "x:" << nodeCenter.x << " y:" << nodeCenter.y << " z:" << nodeCenter.z << std::endl;
由于模型存在右手坐标系以及Y轴向上和Z轴向上,所以求出的模型重心坐标在各自坐标系下都有稍许区别。这里与3dmax进行比较结论如下:
1、当3dmax导出的fbx为Z轴向上时 3dmax坐标如下: box01:中心点坐标(0,-0.5,1) box02:中心点坐标(0,1.5,1) box03:中心点坐标(2,-0.5,0)
assimp计算结果,采用左手坐标系 box01:中心点坐标 (0,-0.5,-1) box02:中心点坐标(0,1.5,-1) box03:中心点坐标(2,-0.5,0)
结论:整个重心点坐标,只需要Z轴取反,即可和3dmax一致
1、当3dmax导出的fbx为Y轴向上时 3dmax坐标如下: box01:中心点坐标(0,-0.5,1) box02:中心点坐标(0,1.5,1) box03:中心点坐标(2,-0.5,0)
assimp计算结果,依旧采用左手坐标系 box01:中心点坐标 (0,1,-0.5) box02:中心点坐标(0,1,1.5) box03:中心点坐标(2,0,-0.5)
结论:整个重心点坐标,只需要交换Y轴和Z轴,即可和3dmax一致
本文主要介绍了如何通过assimp获取模型的重心坐标。项目开源地址: https://github.com/inveta/ModelProcess
IN VETA是一支由建模、美术、UE5组成的年轻团队。
我们的开源项目: https://github.com/inveta
我们致力于三维数字孪生技术分享与研发。