iMSTK
Interactive Medical Simulation Toolkit
imstkAssimpMeshIO.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkAssimpMeshIO.h"
8 #include "imstkLineMesh.h"
9 #include "imstkLogger.h"
10 #include "imstkMeshIO.h"
11 #include "imstkSurfaceMesh.h"
12 #include "imstkVecDataArray.h"
13 
14 #include <assimp/Importer.hpp>
15 #include <assimp/mesh.h>
16 #include <assimp/postprocess.h>
17 #include <assimp/scene.h>
18 
19 namespace imstk
20 {
21 std::shared_ptr<PointSet>
23  const std::string& filePath,
24  MeshFileType type)
25 {
26  switch (type)
27  {
28  case MeshFileType::OBJ:
29  case MeshFileType::DAE:
30  case MeshFileType::FBX:
31  case MeshFileType::_3DS:
32  return AssimpMeshIO::readMeshData(filePath);
33  break;
34  default:
35  LOG(WARNING) << "Error: file type not supported for input " << filePath;
36  return nullptr;
37  break;
38  }
39 }
40 
41 std::shared_ptr<PointSet>
42 AssimpMeshIO::readMeshData(const std::string& filePath)
43 {
44  // Import mesh(es) and apply some clean-up operations
45  Assimp::Importer importer;
46  auto scene = importer.ReadFile(filePath, AssimpMeshIO::getDefaultPostProcessSteps());
47 
48  // Check if there is actually a mesh or if the file can be read
49  CHECK(scene != nullptr && scene->HasMeshes()) << "Error: could not read with Assimp reader for input " << filePath;
50 
51  // Get first mesh
52  aiMesh* importedMesh = scene->mMeshes[0];
53  if (scene->mNumMeshes > 1)
54  {
55  LOG(WARNING) << "Warning: file " << filePath << " contains more than one mesh. Using the first, dropping the rest.";
56  }
57 
58  std::shared_ptr<PointSet> pointSet = AssimpMeshIO::convertAssimpMesh(importedMesh);
59  if (!pointSet)
60  {
61  LOG(WARNING) << "Error: Invalid mesh. Input: " << filePath;
62  }
63 
64  return pointSet;
65 }
66 
67 std::shared_ptr<PointSet>
68 AssimpMeshIO::convertAssimpMesh(aiMesh* importedMesh)
69 {
70  // Get mesh information
71  auto numVertices = importedMesh->mNumVertices;
72  auto numFaces = importedMesh->mNumFaces;
73 
74  if (numVertices == 0)
75  {
76  LOG(WARNING) << "Error: mesh has no vertices.";
77  return nullptr;
78  }
79 
80  // Vertex positions
81  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = std::make_shared<VecDataArray<double, 3>>(numVertices);
82  VecDataArray<double, 3>& vertices = *verticesPtr;
83 
84  for (unsigned int i = 0; i < numVertices; i++)
85  {
86  auto positionX = importedMesh->mVertices[i].x;
87  auto positionY = importedMesh->mVertices[i].y;
88  auto positionZ = importedMesh->mVertices[i].z;
89  vertices[i] = Vec3d(positionX, positionY, positionZ);
90  }
91 
92  // Count cell types
93  int numLines = 0;
94  int numTris = 0;
95  // \todo: Quad mesh support in iMSTK
96  //int numQuads = 0;
97  bool hasGreaterThanTriangle = false;
98  for (unsigned int i = 0; i < numFaces; i++)
99  {
100  aiFace cell = importedMesh->mFaces[i];
101  numTris += static_cast<int>(cell.mNumIndices == 3);
102  numLines += static_cast<int>(cell.mNumIndices == 2);
103  if (cell.mNumIndices > 3)
104  {
105  hasGreaterThanTriangle = true;
106  }
107  //numQuads += static_cast<int>(cell.mNumIndices == 4);
108  }
109 
110  if (hasGreaterThanTriangle)
111  {
112  LOG(WARNING) << "assimp reader found file with unsupported index counts. Dropping those cells.";
113  }
114 
115  // If there are no cells in this mesh, read PointSet (assimp does not support)
116  /*if (importedMesh->mNumFaces == 0)
117  {
118  auto ptMesh = std::make_shared<PointSet>(std::string(importedMesh->mName.C_Str()));
119  ptMesh->initialize(verticesPtr);
120  return ptMesh;
121  }*/
122  // If no triangles or quads, but lines, read LineMesh
123  if (numTris == 0 && numLines > 0)
124  {
125  auto cellsPtr = std::make_shared<VecDataArray<int, 2>>(numLines);
126  VecDataArray<int, 2>& cells = *cellsPtr;
127 
128  unsigned int j = 0;
129  for (unsigned int i = 0; i < numFaces; i++)
130  {
131  aiFace triangle = importedMesh->mFaces[i];
132  unsigned int* indices = triangle.mIndices;
133  if (triangle.mNumIndices == 2)
134  {
135  cells[j++] = Vec2i(indices[0], indices[1]);
136  }
137  }
138  auto lineMesh = std::make_shared<LineMesh>();
139  lineMesh->initialize(verticesPtr, cellsPtr);
140  return lineMesh;
141  }
142  // Otherwise just read SurfaceMesh
143  else
144  {
145  auto cellsPtr = std::make_shared<VecDataArray<int, 3>>(numTris);
146  VecDataArray<int, 3>& cells = *cellsPtr;
147 
148  unsigned int j = 0;
149  for (unsigned int i = 0; i < numFaces; i++)
150  {
151  aiFace triangle = importedMesh->mFaces[i];
152  unsigned int* indices = triangle.mIndices;
153  if (triangle.mNumIndices == 3)
154  {
155  cells[j++] = Vec3i(indices[0], indices[1], indices[2]);
156  }
157  }
158 
159  // Vertex normals, tangents, and bitangents
160  std::shared_ptr<VecDataArray<double, 3>> normalsPtr = std::make_shared<VecDataArray<double, 3>>(numVertices);
161  VecDataArray<double, 3>& normals = *normalsPtr;
162  std::shared_ptr<VecDataArray<float, 3>> tangentsPtr = std::make_shared<VecDataArray<float, 3>>(numVertices);
163  VecDataArray<float, 3>& tangents = *tangentsPtr;
164  std::shared_ptr<VecDataArray<double, 3>> bitangentsPtr = std::make_shared<VecDataArray<double, 3>>(numVertices);
165  VecDataArray<double, 3>& bitangents = *bitangentsPtr;
166 
167  if (importedMesh->HasNormals())
168  {
169  for (unsigned int i = 0; i < numVertices; i++)
170  {
171  auto normalX = importedMesh->mNormals[i].x;
172  auto normalY = importedMesh->mNormals[i].y;
173  auto normalZ = importedMesh->mNormals[i].z;
174  normals[i] = Vec3d(normalX, normalY, normalZ);
175  }
176  }
177 
178  auto surfMesh = std::make_shared<SurfaceMesh>();
179  surfMesh->initialize(verticesPtr, cellsPtr, normalsPtr, false);
180  surfMesh->setVertexNormals("normals", normalsPtr);
181 
182  if (importedMesh->HasTangentsAndBitangents() && importedMesh->HasTextureCoords(0))
183  {
184  for (unsigned int i = 0; i < numVertices; i++)
185  {
186  auto tangentX = importedMesh->mTangents[i].x;
187  auto tangentY = importedMesh->mTangents[i].y;
188  auto tangentZ = importedMesh->mTangents[i].z;
189  tangents[i] = Vec3f(tangentX, tangentY, tangentZ);
190 
191  auto bitangentX = importedMesh->mBitangents[i].x;
192  auto bitangentY = importedMesh->mBitangents[i].y;
193  auto bitangentZ = importedMesh->mBitangents[i].z;
194  bitangents[i] = Vec3d(bitangentX, bitangentY, bitangentZ);
195  }
196  surfMesh->setVertexTangents("tangents", tangentsPtr);
197  }
198 
199  // UV coordinates
200  if (importedMesh->HasTextureCoords(0))
201  {
202  std::shared_ptr<VecDataArray<float, 2>> UVs = std::make_shared<VecDataArray<float, 2>>(numVertices);
203  VecDataArray<float, 2>& UVData = *UVs;
204 
205  auto texcoords = importedMesh->mTextureCoords[0];
206  for (unsigned int i = 0; i < numVertices; i++)
207  {
208  UVData[i][0] = texcoords[i].x;
209  UVData[i][1] = texcoords[i].y;
210  }
211  surfMesh->setVertexTCoords("tCoords", UVs);
212  }
213  return surfMesh;
214  }
215 }
216 
217 unsigned int
219 {
220  unsigned int postProcessSteps =
221  aiPostProcessSteps::aiProcess_GenSmoothNormals |
222  aiPostProcessSteps::aiProcess_CalcTangentSpace |
223  aiPostProcessSteps::aiProcess_JoinIdenticalVertices |
224  aiPostProcessSteps::aiProcess_Triangulate |
225  aiPostProcessSteps::aiProcess_ImproveCacheLocality;
226 
227  return postProcessSteps;
228 }
229 } // namespace imstk
static std::shared_ptr< PointSet > read(const std::string &filePath, MeshFileType type)
Ensures file can be read and reads it if possible.
MeshFileType
Enumeration the mesh file type.
Definition: imstkMeshIO.h:19
Compound Geometry.
static unsigned int getDefaultPostProcessSteps()
Helper function for getting default post processing flags.
static std::shared_ptr< PointSet > convertAssimpMesh(aiMesh *importedMesh)
Convert from Assimp mesh to iMSTK SurfaceMesh. May convert to LineMesh if no triangles & only lines a...
static std::shared_ptr< PointSet > readMeshData(const std::string &filePath)
Reads mesh data and returns mesh. May read a LineMesh if no triangles & only lines are present...