前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开源篇--精准定位 模型重心坐标

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

作者头像
Jean
发布2022-11-25 19:26:36
1.5K0
发布2022-11-25 19:26:36
举报
文章被收录于专栏:Web行业观察

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebHub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Part1前言
  • Part2重心坐标
  • Part3代码获取模型重心
    • 1加载模型
      • 2遍历node
        • 3计算重心
          • 4与3dmax重心坐标比较
          • Part4总结
          • Part5关于IN VETA
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档