iMSTK
Interactive Medical Simulation Toolkit
imstkVisualObjectImporter.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 "imstkVisualObjectImporter.h"
8 #include "imstkAssimpMeshIO.h"
9 #include "imstkLineMesh.h"
10 #include "imstkLogger.h"
11 #include "imstkRenderMaterial.h"
12 #include "imstkSceneObject.h"
13 #include "imstkSurfaceMesh.h"
14 #include "imstkVecDataArray.h"
15 #include "imstkVisualModel.h"
16 
17 #include <assimp/Importer.hpp>
18 #include <assimp/scene.h>
19 #include <fstream>
20 #include <stack>
21 #include <unordered_map>
22 
23 namespace imstk
24 {
25 static Mat4d
26 aiMatToMat4d(const aiMatrix4x4& m)
27 {
28  Mat4d results;
29  for (int i = 0; i < 4; i++)
30  {
31  for (int j = 0; j < 4; j++)
32  {
33  results(i, j) = static_cast<double>(m[i][j]);
34  }
35  }
36  return results;
37 }
38 
39 static aiMatrix4x4
40 mat4dToAiMat(const Mat4d& m)
41 {
42  aiMatrix4x4 results;
43  for (int i = 0; i < 4; i++)
44  {
45  for (int j = 0; j < 4; j++)
46  {
47  results[i][j] = static_cast<float>(m(i, j));
48  }
49  }
50  return results;
51 }
52 
53 std::shared_ptr<SceneObject>
55  const std::string& objName,
56  const std::string& modelFilePath,
57  const std::string& textureFolderPath,
58  const Mat4d& transform)
59 {
60  auto type = MeshIO::getFileType(modelFilePath);
61 
62  // Check if mesh is supported by Assimp
63  if (type != MeshFileType::_3DS
64  && type != MeshFileType::OBJ
65  && type != MeshFileType::FBX
66  && type != MeshFileType::DAE)
67  {
68  LOG(FATAL) << "Error: File type not supported! Input model file path: " << modelFilePath;
69  return nullptr;
70  }
71 
72  auto visualObject = std::make_shared<SceneObject>(objName);
73 
74  // Import mesh(es) and apply some clean-up operations
75  Assimp::Importer importer;
76  const aiScene* scene = importer.ReadFile(modelFilePath, AssimpMeshIO::getDefaultPostProcessSteps());
77 
78  // Check if there is actually a mesh or if the file can be read
79  CHECK(scene != nullptr && scene->HasMeshes()) << "Error: could not read model with Assimp reader! Input model file path: "
80  << modelFilePath;
81 
82  // Iterate over each material
83  std::vector<std::shared_ptr<RenderMaterial>> materials(scene->mNumMaterials);
84  for (unsigned int i = 0; i < scene->mNumMaterials; i++)
85  {
86  materials[i] = readMaterial(scene->mMaterials[i], textureFolderPath);
87  }
88 
89  // Read all meshes
90  std::vector<std::shared_ptr<PointSet>> meshes(scene->mNumMeshes);
91  std::vector<std::shared_ptr<RenderMaterial>> meshMaterials(scene->mNumMeshes);
92  for (unsigned int i = 0; i < scene->mNumMeshes; i++)
93  {
94  aiMesh* importedMesh = scene->mMeshes[i];
95  meshes[i] = AssimpMeshIO::convertAssimpMesh(importedMesh);
96  meshMaterials[i] = materials[importedMesh->mMaterialIndex];
97  }
98 
99  // Iterate through assimp graph
100  std::stack<aiNode*> nodes;
101  nodes.push(scene->mRootNode);
102  scene->mRootNode->mTransformation = mat4dToAiMat(transform) * scene->mRootNode->mTransformation;
103  std::unordered_map<aiNode*, Mat4d> worldTransforms;
104  while (!nodes.empty())
105  {
106  aiNode* currNode = nodes.top();
107  nodes.pop();
108 
109  Mat4d parentWorldTransform = Mat4d::Identity();
110  if (currNode->mParent != nullptr)
111  {
112  parentWorldTransform = worldTransforms[currNode->mParent];
113  }
114  Mat4d localTransform = aiMatToMat4d(currNode->mTransformation);
115  Mat4d currWorldTransform = parentWorldTransform * localTransform;
116  worldTransforms[currNode] = currWorldTransform;
117 
118  for (unsigned int i = 0; i < currNode->mNumMeshes; i++)
119  {
120  const unsigned int meshIndex = currNode->mMeshes[i];
121 
122  // Copy, transform, and insert the mesh (\todo: Better deep copy support)
123  std::shared_ptr<PointSet> copyMesh = meshes[meshIndex]->clone();
124 
125  auto visualModel = visualObject->addComponent<VisualModel>();
126  visualModel->setGeometry(copyMesh);
127  visualModel->setName(std::string(currNode->mName.C_Str()));
128 
129  copyMesh->transform(currWorldTransform, Geometry::TransformType::ApplyToData);
130  visualModel->setRenderMaterial(meshMaterials[meshIndex]);
131  }
132 
133  for (unsigned int i = 0; i < currNode->mNumChildren; i++)
134  {
135  nodes.push(currNode->mChildren[i]);
136  }
137  }
138 
139  return visualObject;
140 }
141 
142 std::shared_ptr<Texture>
143 ObjectIO::createTexture(std::string textureFolderPath, std::string textureFilePath, Texture::Type textureType)
144 {
145  textureFilePath = getSubstringGivenString(textureFilePath, "/", true);
146  textureFilePath = getSubstringGivenString(textureFilePath, "\\", true);
147 
148  std::string fileName = getSubstringGivenString(textureFilePath, ".", false);
149 
150  const std::string fileExt = getSubstringGivenString(textureFilePath, ".", true);
151 
152  const std::string filePath = textureFolderPath + fileName + "." + fileExt;
153 
154  // Check if file exists
155  std::ifstream file(filePath);
156  if (file.good())
157  {
158  file.close();
159  return std::make_shared<Texture>(filePath, textureType);
160  }
161  return nullptr;
162 }
163 
164 std::shared_ptr<RenderMaterial>
165 ObjectIO::readMaterial(aiMaterial* material, std::string textureFolderPath)
166 {
167  // Create our material
168  auto renderMaterial = std::make_shared<RenderMaterial>();
169  renderMaterial->setShadingModel(RenderMaterial::ShadingModel::Phong);
170 
171  // Go through all the ai properties, not all included here
172  aiString name;
173  aiReturn ret = material->Get(AI_MATKEY_NAME, name);
174  if (ret == AI_SUCCESS)
175  {
176  renderMaterial->setName(std::string(name.C_Str()));
177  }
178 
179  aiColor3D ambientColor;
180  ret = material->Get(AI_MATKEY_COLOR_AMBIENT, ambientColor);
181  if (ret == AI_SUCCESS)
182  {
183  renderMaterial->setAmbientColor(Color(ambientColor.r, ambientColor.g, ambientColor.b));
184  }
185 
186  aiColor3D diffuseColor;
187  ret = material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);
188  if (ret == AI_SUCCESS)
189  {
190  renderMaterial->setDiffuseColor(Color(diffuseColor.r, diffuseColor.g, diffuseColor.b));
191  }
192 
193  aiColor3D specularColor;
194  ret = material->Get(AI_MATKEY_COLOR_SPECULAR, specularColor);
195  if (ret == AI_SUCCESS)
196  {
197  renderMaterial->setSpecularColor(Color(specularColor.r, specularColor.g, specularColor.b));
198  }
199 
200  int useWireframe;
201  ret = material->Get(AI_MATKEY_ENABLE_WIREFRAME, useWireframe);
202  if (ret == AI_SUCCESS)
203  {
204  if (useWireframe == 1)
205  {
206  renderMaterial->setDisplayMode(RenderMaterial::DisplayMode::Wireframe);
207  }
208  }
209 
210  int useBackfaceRendering;
211  ret = material->Get(AI_MATKEY_TWOSIDED, useBackfaceRendering);
212  if (ret == AI_SUCCESS)
213  {
214  renderMaterial->setBackFaceCulling(static_cast<bool>(useBackfaceRendering));
215  }
216 
217  float opacity;
218  ret = material->Get(AI_MATKEY_OPACITY, opacity);
219  if (ret == AI_SUCCESS)
220  {
221  renderMaterial->setOpacity(opacity);
222  }
223 
224  float shininess;
225  ret = material->Get(AI_MATKEY_SHININESS, shininess);
226  if (ret == AI_SUCCESS)
227  {
228  renderMaterial->setSpecular(shininess);
229  }
230 
231  float shininessStrength;
232  ret = material->Get(AI_MATKEY_SHININESS_STRENGTH, shininessStrength);
233  if (ret == AI_SUCCESS)
234  {
235  renderMaterial->setSpecularPower(shininessStrength);
236  }
237 
238  float reflectivity;
239  ret = material->Get(AI_MATKEY_REFLECTIVITY, reflectivity);
240  if (ret == AI_SUCCESS)
241  {
242  // Not supported yet
243  }
244 
245  renderMaterial->setRecomputeVertexNormals(false);
246 
247  aiString texFilePath;
248  ret = material->GetTexture(aiTextureType::aiTextureType_AMBIENT, 0, &texFilePath);
249  if (ret == AI_SUCCESS)
250  {
251  std::shared_ptr<Texture> tex = createTexture(textureFolderPath, std::string(texFilePath.C_Str()), Texture::Type::AmbientOcclusion);
252  if (tex != nullptr)
253  {
254  renderMaterial->addTexture(tex);
255  }
256  }
257  ret = material->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texFilePath);
258  if (ret == AI_SUCCESS)
259  {
260  std::shared_ptr<Texture> tex = createTexture(textureFolderPath, std::string(texFilePath.C_Str()), Texture::Type::Diffuse);
261  if (tex != nullptr)
262  {
263  renderMaterial->addTexture(tex);
264  }
265  }
266  ret = material->GetTexture(aiTextureType::aiTextureType_EMISSIVE, 0, &texFilePath);
267  if (ret == AI_SUCCESS)
268  {
269  std::shared_ptr<Texture> tex = createTexture(textureFolderPath, std::string(texFilePath.C_Str()), Texture::Type::Emissive);
270  if (tex != nullptr)
271  {
272  renderMaterial->addTexture(tex);
273  }
274  }
275  ret = material->GetTexture(aiTextureType::aiTextureType_NORMALS, 0, &texFilePath);
276  if (ret == AI_SUCCESS)
277  {
278  std::shared_ptr<Texture> tex = createTexture(textureFolderPath, std::string(texFilePath.C_Str()), Texture::Type::Normal);
279  if (tex != nullptr)
280  {
281  renderMaterial->addTexture(tex);
282  }
283  }
284  ret = material->GetTexture(aiTextureType::aiTextureType_SPECULAR, 0, &texFilePath);
285  if (ret == AI_SUCCESS)
286  {
287  std::shared_ptr<Texture> tex = createTexture(textureFolderPath, std::string(texFilePath.C_Str()), Texture::Type::Metalness);
288  if (tex != nullptr)
289  {
290  renderMaterial->addTexture(tex);
291  }
292  }
293  return renderMaterial;
294 }
295 
296 std::string
297 ObjectIO::getSubstringGivenString(
298  const std::string& input,
299  const std::string& delimiter,
300  const bool lastInstance /*= false*/)
301 {
302  unsigned long long index = 0;
303  unsigned long long tempIndex;
304 
305  if (lastInstance)
306  {
307  tempIndex = input.rfind(delimiter) + 1;
308  if (tempIndex >= input.length())
309  {
310  return input;
311  }
312  }
313  else
314  {
315  tempIndex = input.find(delimiter);
316  }
317 
318  if (tempIndex == std::string::npos)
319  {
320  index = lastInstance ? 0 : input.length();
321  }
322  else
323  {
324  index = tempIndex;
325  }
326 
327  if (lastInstance)
328  {
329  return input.substr(index);
330  }
331 
332  return input.substr(0, index);
333 }
334 } // namespace imstk
Phong shading model (default)
Type
Texture type - determines filtering.
Definition: imstkTexture.h:30
static std::shared_ptr< SceneObject > importSceneObject(const std::string &objName, const std::string &modelFilePath, const std::string &textureFolderPath, const Mat4d &transform=Mat4d::Identity())
Import a scene object.
Compound Geometry.
Color in RGB space.
Definition: imstkColor.h:24
static const MeshFileType getFileType(const std::string &filePath)
Returns the type of the file.
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...
Contains geometric, material, and render information.