开源篇--精准定位 模型重心坐标

2022-11-25 19:26:36 浏览数 (1)

Part1前言

模型重心坐标就是在模型正中心那个点的坐标。本文介绍一种方法,可以通过代码的方式自动获取模型重心坐标。本方式适用于常用的所有模型。

Part2重心坐标

我们都学过初中几何,可以知道三角形重心是三角形三条中线的交点。当几何体为匀质物体时,重心与形心重合。下图中O为三角形的重心。

换算成笛卡尔坐标系 三角形三个顶点为(x1,y1),(x2,y2),(x3,y3) 那么重心坐标 =((X1 X2 X3)/3,(Y1 Y2 Y3)/3) 到了我们三维模型中,重心坐标依旧和这个公式类似,等于所有三角面重心点之和的平均值。 模型重心如下图所示:

Part3代码获取模型重心

这里我们通过assimp库来获取模型的重心。关于assimp,参考我们上一篇文章。三维模型格式转换神器-assimp

这里我们以fbx模型为例,来说明获取重心的步骤。分为以下几个步骤: 1、加载模型获取aiScene 2、遍历aiScene下RootNode下的所有节点 3、获取aiMesh来计算模型的重心

1加载模型

加载模型示例代码如下:

代码语言:javascript复制
  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;
  }

2遍历node

整个assimp的场景是一个树状结构,从root节点开始,如下图

我们通过递归进行遍历

代码语言:javascript复制
void FindMesh(const aiScene *scene, aiNode *node)
{
  FindMeshInfo(scene, node);
  for (unsigned int m = 0; m < node->mNumChildren;   m)
  {
    FindMesh(scene, node->mChildren[m]);
  }
}

调用递归遍历函数如下:

代码语言:javascript复制
  auto rootNode = mScenePtr->mRootNode;
  FindMesh(mScenePtr, rootNode);

3计算重心

重心的计算是最复杂的部分,分为两个步骤。 第一步:计算网格体的重心 第二步:加上变换矩阵 变换矩阵是指增加在模型上面的平移旋转缩放的变换矩阵,从而导致模型的重心位置发生变化。

计算网格体的重心又细分为一下两步: 1、计算每个三角面的重心点 2、计算所有三角面重心之和的平均值

代码示例

代码语言:javascript复制
  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;

加上旋转变换矩阵示例如下:

代码语言:javascript复制
  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;

4与3dmax重心坐标比较

由于模型存在右手坐标系以及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一致

Part4总结

本文主要介绍了如何通过assimp获取模型的重心坐标。项目开源地址: https://github.com/inveta/ModelProcess

Part5关于IN VETA

IN VETA是一支由建模、美术、UE5组成的年轻团队。

我们的开源项目: https://github.com/inveta

我们致力于三维数字孪生技术分享与研发。

0 人点赞