前言


三维模型格式种类繁多,相互之间的格式转换费时费力。本文介绍一个三维格式转换神器assimp,可以非常简单的实现各种三维模型格式的互相转换。

3f8b9a09475677430e2ff1ba463e5f8c.png

assimp简介


Asset-Importer-Lib(简称 assimp)是一个库,用于加载和处理来自各种 3D 数据格式的几何场景。该库不是为速度而设计的,它主要用于从各种来源导入资产一次并将其存储为特定于引擎的格式,以便于轻松快速地加载。assimp 还能够对导入的数据应用各种后处理步骤,例如转换为索引网格、计算法线或切线/双切线或从右手坐标系转换为左手坐标系。

github地址:https://github.com/assimp/assimp。其拥有7.7K的Star,并且社区也极其活跃,最新一次分支合并在3天前。

c70d77819880833e431a1b62a1447882.png

Assimp支持多达40多种三维格式,列举部分如下:

  • 3D Manufacturing Format (.3mf)

  • Collada (.dae, .xml)

  • Blender (.blend)

  • Biovision BVH (.bvh)

  • 3D Studio Max 3DS (.3ds)

  • 3D Studio Max ASE (.ase)

  • glTF (.glTF)

  • glTF2.0 (.glTF)

  • FBX-Format, as ASCII and binary (.fbx)

  • Stanford Polygon Library (.ply)

  • AutoCAD DXF (.dxf)

  • IFC-STEP (.ifc)

  • Neutral File Format (.nff)

  • Sense8 WorldToolkit (.nff)

  • Valve Model (.smd, .vta)

  • Quake I (.mdl)

  • Quake II (.md2)

  • Quake III (.md3)

  • Quake 3 BSP (.pk3)

  • RtCW (.mdc)

  • Doom 3 (.md5mesh, .md5anim, .md5camera)

  • DirectX X (.x)

  • Quick3D (.q3o, .q3s)

  • Raw Triangles (.raw)

  • AC3D (.ac, .ac3d)

  • Stereolithography (.stl)

  • Autodesk DXF (.dxf)

  • Irrlicht Mesh (.irrmesh, .xml)

  • Irrlicht Scene (.irr, .xml)

  • Object File Format ( .off )

  • Wavefront Object (.obj)

  • Terragen Terrain ( .ter )

  • 3D GameStudio Model ( .mdl )

  • 3D GameStudio Terrain ( .hmp )

  • Ogre ( .mesh.xml, .skeleton.xml, .material )

  • OpenGEX-Fomat (.ogex)

  • Milkshape 3D ( .ms3d )

  • LightWave Model ( .lwo )

  • LightWave Scene ( .lws )

  • Modo Model ( .lxo )

  • CharacterStudio Motion ( .csm )

  • Stanford Ply ( .ply )

  • TrueSpace (.cob, .scn)

  • XGL-3D-Format (.xgl)

Assimp命令行


我们使用Assimp,正常情况下都不需要下载编译源码,可以直接采用其命令行工具进行格式转换。

命令行工具源码在如下目录

assimp-5.2.3\tools\assimp_cmd

编译出来之后,是一个assimp.exe程序

通过--format可以指定生成模型格式,例如生成ascii的fbx格式如下示例

.\assimpd.exe export box.fbx box2.fbx --format=fbxa

生成纹理合并的glb格式,如下命令

.\assimp.exe export input.fbx output.glb -embtex

另外还可以添加各种模型预处理的命令,格式如下:

// -ptv    --pretransform-vertices
  // -gsn    --gen-smooth-normals
  // -gn     --gen-normals
  // -cts    --calc-tangent-space
  // -jiv    --join-identical-vertices
  // -rrm    --remove-redundant-materials
  // -fd     --find-degenerates
  // -slm    --split-large-meshes
  // -lbw    --limit-bone-weights
  // -vds    --validate-data-structure
  // -icl    --improve-cache-locality
  // -sbpt   --sort-by-ptype
  // -lh     --convert-to-lh
  // -fuv    --flip-uv
  // -fwo    --flip-winding-order
  // -tuv    --transform-uv-coords
  // -guv    --gen-uvcoords
  // -fid    --find-invalid-data
  // -fixn   --fix normals
  // -tri    --triangulate
  // -fi     --find-instances
  // -og     --optimize-graph
  // -om     --optimize-meshes
  // -db     --debone
  // -sbc    --split-by-bone-count
  // -gs     --global-scale
  //
  // -c<file> --config-file=<file>

Assimp库调用


如果我们需要获取更加详尽的数据,我们可以直接调用Assimp库。这里主要介绍基于vscode的cmake进行组织代码。

代码结构如下:

fb71d36fd6aa86db10358a64cf99341d.png

说明:

1、extern目录放置assimp源码

2、src目录放置调用代码

这里需要手动写两个CMakeLists文件,extern目录下内容如下:

add_subdirectory(assimp-5.2.3)


set(EXTERN_ASSIMP_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/assimp-5.2.3/include" CACHE INTERNAL
    "Include directory for assimp"
)

这里作用主要是包含assimp库以及设置assimp头文件目录给变量

工程目录下的内容如下:

cmake_minimum_required(VERSION 3.0.0)
project(FbxParsing VERSION 0.1.0)


add_subdirectory(extern)


add_executable(FbxParsing ./src/FbxParsing.cpp)




target_include_directories(FbxParsing SYSTEM PUBLIC ${EXTERN_ASSIMP_INCLUDE_DIR})


target_link_libraries(FbxParsing PUBLIC assimp )

说明:

1、设置assimp的头文件目录,不然业务代码找不到头文件,通过

target_include_directories实现。

2、需要设置库依赖,不然编译过不去,通过

target_link_libraries实现。

之后可以写一个简单的测试demo,实现对各类数据的查看,以及格式的转换。

#include <iostream>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/Exporter.hpp>


void FindMeshInfo(const aiScene *scene, aiNode *node)
{
  std::cout << node->mName.C_Str() << std::endl;
  if (0 == node->mNumMeshes)
  {
    return;
  }
  auto mesh = node->mMeshes;
}
void FindMesh(const aiScene *scene, aiNode *node)
{
  FindMeshInfo(scene, node);
  for (unsigned int m = 0; m < node->mNumChildren; ++m)
  {
    FindMesh(scene, node->mChildren[m]);
  }
}


int main()
{
  std::cout << "Hello, from FbxParsing!\n";


  auto inFile = R"(C:\Users\41132\Desktop\box.fbx)";
  Assimp::Importer mImporter;
  const aiScene *mScenePtr = mImporter.ReadFile(inFile, aiProcess_Triangulate);
  if (nullptr == mScenePtr)
  {
    std::cout << "nullptr == mScenePtr" << std::endl;
    return -1;
  }


  auto rootNode = mScenePtr->mRootNode;
  FindMesh(mScenePtr, rootNode);
  auto outFile = R"(C:\Users\41132\Desktop\box2.fbx)";
  Assimp::Exporter mExporter;


  mExporter.Export(mScenePtr, "fbxa", outFile);


  return 0;
}

一些概念


aiScene和aiNode

aiScene 作为AssImp加载模型的根数据结构,保存了从模型加载的顶点位置、法向量、纹理、光照等数据,可以这么说,我们的所有模型数据都存在这里。

aiNode 模型通过层次结构存储,根节点mRootNode保存在aiScene中,根节点下面有0至多个子节点,每个节点通过aiNode类表达,aiNode用来表示模型的层次结构,但是不存储实际数据只有索引。获取数据需要通过索引到aiScene中获取。

双精度


assimp默认的数据存储精度为float,所以大场景模型会有精度偏差。其提供了一个编译开关,来支持双精度。打开如下:

OPTION( ASSIMP_DOUBLE_PRECISION
  "Set to ON to enable double precision processing"
  OFF
)

不过打开之后,会编译报错,我使用的5.2.3版本应该还没有修复,不过最新版应该已经修复了这个bug。

可以参考:

https://github.com/assimp/assimp/issues/4468

4e634824b5757bf254b0cca690885309.png

总结


本文主要介绍了三维模型格式转换神器Assimp的主要能力,并重点介绍了命令行工具和如何进行assimp的二次开发,最后介绍了aiScene和aiNode两个重要概念,以及双精度的问题。

获取示例代码途径:

关注公众号,加技术交流群即可获取本工程示例代码。

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐