Loading [MathJax]/extensions/TeX/AMSsymbols.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
AssimpGeometryDataLoader.cpp
1 #include <IO/AssimpLoader/AssimpGeometryDataLoader.hpp>
2 
3 #include <assimp/mesh.h>
4 #include <assimp/scene.h>
5 
6 #include <Core/Utils/Log.hpp>
7 
8 #include <Core/Asset/BlinnPhongMaterialData.hpp>
9 
10 namespace Ra {
11 namespace IO {
12 
13 using namespace Core::Utils; // log
14 using namespace Core::Asset; // log
15 
16 AssimpGeometryDataLoader::AssimpGeometryDataLoader( const std::string& filepath,
17  const bool VERBOSE_MODE ) :
18  DataLoader<GeometryData>( VERBOSE_MODE ), m_filepath( filepath ) {}
19 
20 AssimpGeometryDataLoader::~AssimpGeometryDataLoader() = default;
21 
22 void AssimpGeometryDataLoader::loadData( const aiScene* scene,
23  std::vector<std::unique_ptr<GeometryData>>& data ) {
24  data.clear();
25 
26  if ( scene == nullptr ) {
27  LOG( logINFO ) << "AssimpGeometryDataLoader : scene is nullptr.";
28  return;
29  }
30 
31  if ( !sceneHasGeometry( scene ) ) {
32  LOG( logINFO ) << "AssimpGeometryDataLoader : scene has no mesh.";
33  return;
34  }
35 
36  if ( m_verbose ) {
37  LOG( logINFO ) << "File contains geometry.";
38  LOG( logINFO ) << "Geometry Loading begin...";
39  }
40 
41  loadGeometryData( scene, data );
42 
43  if ( m_verbose ) { LOG( logINFO ) << "Geometry Loading end.\n"; }
44 }
45 
46 bool AssimpGeometryDataLoader::sceneHasGeometry( const aiScene* scene ) const {
47  return ( sceneGeometrySize( scene ) != 0 );
48 }
49 
50 uint AssimpGeometryDataLoader::sceneGeometrySize( const aiScene* scene ) const {
51  uint mesh_size = 0;
52  if ( scene->HasMeshes() ) {
53  const uint size = scene->mNumMeshes;
54  for ( uint i = 0; i < size; ++i ) {
55  aiMesh* mesh = scene->mMeshes[i];
56  if ( mesh->HasPositions() ) { ++mesh_size; }
57  }
58  }
59  return mesh_size;
60 }
61 
62 void AssimpGeometryDataLoader::loadMeshAttrib( const aiMesh& mesh,
63  GeometryData& data,
64  std::set<std::string>& usedNames ) {
65  // Translate general identification of the mesh
66  fetchName( mesh, data, usedNames );
67  fetchType( mesh, data );
68 
69  // Translate Geometry data
70  auto& geo = data.getGeometry();
71 
72  // Translate all vertex attributes
73  fetchAttribute(
74  mesh.mVertices, mesh.mNumVertices, geo, Core::Geometry::MeshAttrib::VERTEX_POSITION );
75  if ( mesh.HasNormals() ) {
76  fetchAttribute( mesh.mNormals,
77  mesh.mNumVertices,
78  data.getGeometry(),
79  Core::Geometry::MeshAttrib::VERTEX_NORMAL );
80  }
81  if ( mesh.HasTangentsAndBitangents() ) {
82  fetchAttribute( mesh.mTangents,
83  mesh.mNumVertices,
84  data.getGeometry(),
85  Core::Geometry::MeshAttrib::VERTEX_TANGENT );
86  fetchAttribute( mesh.mBitangents,
87  mesh.mNumVertices,
88  data.getGeometry(),
89  Core::Geometry::MeshAttrib::VERTEX_BITANGENT );
90  }
91  // Radium V2 : allow to have several UV channels
92  // use MATKEY_UVWSRC to know if any
93  if ( mesh.GetNumUVChannels() > 1 ) {
94  LOG( logWARNING )
95  << "Assimp loader : several UV channels are set, Radium will use only the 1st";
96  }
97  if ( mesh.HasTextureCoords( 0 ) ) {
98  fetchAttribute( mesh.mTextureCoords[0],
99  mesh.mNumVertices,
100  data.getGeometry(),
101  Core::Geometry::MeshAttrib::VERTEX_TEXCOORD );
102  }
103  // Radium V2 : allow to have several color channels
104  if ( mesh.HasVertexColors( 0 ) ) {
105  fetchAttribute( mesh.mColors[0],
106  mesh.mNumVertices,
107  data.getGeometry(),
108  Core::Geometry::MeshAttrib::VERTEX_COLOR );
109  }
110 
111  // Translate mesh topology
112  data.setPrimitiveCount( mesh.mNumFaces );
113  switch ( data.getType() ) {
114  case GeometryData::GeometryType::LINE_MESH:
115  fetchIndexLayer<Core::Geometry::LineIndexLayer>( mesh.mFaces, mesh.mNumFaces, geo );
116  break;
117  case GeometryData::GeometryType::TRI_MESH:
118  fetchIndexLayer<Core::Geometry::TriangleIndexLayer>( mesh.mFaces, mesh.mNumFaces, geo );
119  break;
120  case GeometryData::GeometryType::QUAD_MESH:
121  fetchIndexLayer<Core::Geometry::QuadIndexLayer>( mesh.mFaces, mesh.mNumFaces, geo );
122  break;
123  case GeometryData::GeometryType::POLY_MESH:
124  fetchIndexLayer<Core::Geometry::PolyIndexLayer>( mesh.mFaces, mesh.mNumFaces, geo );
125  break;
126  case GeometryData::GeometryType::POINT_CLOUD:
127  default: {
128  auto pil =
129  std::make_unique<Core::Geometry::PointCloudIndexLayer>( size_t( mesh.mNumVertices ) );
130  geo.addLayer( std::move( pil ) );
131  data.setPrimitiveCount( mesh.mNumVertices );
132  } break;
133  }
134  // TODO : Polyhedrons are not supported yet in Radium core geometry
135  if ( data.isTetraMesh() || data.isHexMesh() ) { fetchPolyhedron( mesh, data ); }
136 }
137 
138 void AssimpGeometryDataLoader::loadMeshFrame(
139  const aiNode* node,
140  const Core::Transform& parentFrame,
141  const std::map<uint, size_t>& indexTable,
142  std::vector<std::unique_ptr<GeometryData>>& data ) const {
143  const uint child_size = node->mNumChildren;
144  const uint mesh_size = node->mNumMeshes;
145  if ( ( child_size == 0 ) && ( mesh_size == 0 ) ) { return; }
146 
147  Core::Transform frame = parentFrame * assimpToCore( node->mTransformation );
148  for ( uint i = 0; i < mesh_size; ++i ) {
149  const uint ID = node->mMeshes[i];
150  auto it = indexTable.find( ID );
151  if ( it != indexTable.end() ) { data[it->second]->setFrame( frame ); }
152  }
153 
154  for ( uint i = 0; i < child_size; ++i ) {
155  loadMeshFrame( node->mChildren[i], frame, indexTable, data );
156  }
157 }
158 
159 void AssimpGeometryDataLoader::fetchName( const aiMesh& mesh,
160  GeometryData& data,
161  std::set<std::string>& usedNames ) const {
162  std::string name = assimpToCore( mesh.mName );
163  while ( usedNames.find( name ) != usedNames.end() ) {
164  name.append( "_" );
165  }
166  usedNames.insert( name );
167  data.setName( name );
168 }
169 
170 void AssimpGeometryDataLoader::fetchType( const aiMesh& mesh, GeometryData& data ) const {
171  data.setType( GeometryData::UNKNOWN );
172  uint face_min = std::numeric_limits<uint>::max();
173  uint face_max = 0;
174 
175  for ( uint i = 0; i < mesh.mNumFaces; ++i ) {
176  face_max = std::max( face_max, mesh.mFaces[i].mNumIndices );
177  face_min = std::min( face_min, mesh.mFaces[i].mNumIndices );
178  }
179  switch ( face_max ) {
180  case 0:
181  case 1:
182  data.setType( GeometryData::POINT_CLOUD );
183  break;
184  case 2:
185  data.setType( GeometryData::LINE_MESH );
186  break;
187  case 3:
188  data.setType( GeometryData::TRI_MESH );
189  break;
190  case 4:
191  if ( face_max - face_min == 0 ) { data.setType( GeometryData::QUAD_MESH ); }
192  else { data.setType( GeometryData::POLY_MESH ); }
193  break;
194  default: {
195  data.setType( GeometryData::POLY_MESH );
196  } break;
197  }
198 }
199 
200 void AssimpGeometryDataLoader::fetchPolyhedron( const aiMesh& mesh, GeometryData& data ) const {
201  CORE_UNUSED( mesh );
202  CORE_UNUSED( data );
203  // TO DO
204 }
205 
206 void AssimpGeometryDataLoader::loadMaterial( const aiMaterial& material,
207  GeometryData& data ) const {
208 
209  std::string matName;
210  aiString assimpName;
211  if ( AI_SUCCESS == material.Get( AI_MATKEY_NAME, assimpName ) ) {
212  matName = assimpName.C_Str();
213  }
214  // Radium V2 : use AI_MATKEY_SHADING_MODEL to select the apropriate model
215  // (http://assimp.sourceforge.net/lib_html/material_8h.html#a93e23e0201d6ed86fb4287e15218e4cf)
216  auto blinnPhongMaterial = new BlinnPhongMaterialData( matName );
217  if ( AI_DEFAULT_MATERIAL_NAME != matName ) {
218  aiColor4D color;
219  float shininess;
220  float opacity;
221  aiString name;
222 
223  if ( AI_SUCCESS == material.Get( AI_MATKEY_COLOR_DIFFUSE, color ) ) {
224  blinnPhongMaterial->m_hasDiffuse = true;
225  blinnPhongMaterial->m_diffuse = assimpToCore( color );
226  }
227 
228  if ( AI_SUCCESS == material.Get( AI_MATKEY_COLOR_SPECULAR, color ) ) {
229  blinnPhongMaterial->m_hasSpecular = true;
230  blinnPhongMaterial->m_specular = assimpToCore( color );
231  }
232 
233  if ( AI_SUCCESS == material.Get( AI_MATKEY_SHININESS, shininess ) ) {
234  blinnPhongMaterial->m_hasShininess = true;
235  // Assimp gives the Phong exponent, we use the Blinn-Phong exponent
236  blinnPhongMaterial->m_shininess = shininess * 4;
237  }
238 
239  if ( AI_SUCCESS == material.Get( AI_MATKEY_OPACITY, opacity ) ) {
240  blinnPhongMaterial->m_hasOpacity = true;
241  // NOTE(charly): Due to collada way of handling objects that have an alpha map, we must
242  // ensure
243  // we do not have zeros in here.
244  blinnPhongMaterial->m_opacity = opacity < 1e-5 ? 1 : opacity;
245  }
246 
247  if ( AI_SUCCESS == material.Get( AI_MATKEY_TEXTURE( aiTextureType_DIFFUSE, 0 ), name ) ) {
248  blinnPhongMaterial->m_texDiffuse = m_filepath + "/" + assimpToCore( name );
249  blinnPhongMaterial->m_hasTexDiffuse = true;
250  }
251 
252  if ( AI_SUCCESS == material.Get( AI_MATKEY_TEXTURE( aiTextureType_SPECULAR, 0 ), name ) ) {
253  blinnPhongMaterial->m_texSpecular = m_filepath + "/" + assimpToCore( name );
254  blinnPhongMaterial->m_hasTexSpecular = true;
255  }
256 
257  if ( AI_SUCCESS == material.Get( AI_MATKEY_TEXTURE( aiTextureType_SHININESS, 0 ), name ) ) {
258  blinnPhongMaterial->m_texShininess = m_filepath + "/" + assimpToCore( name );
259  blinnPhongMaterial->m_hasTexShininess = true;
260  }
261 
262  if ( AI_SUCCESS == material.Get( AI_MATKEY_TEXTURE( aiTextureType_NORMALS, 0 ), name ) ) {
263  blinnPhongMaterial->m_texNormal = m_filepath + "/" + assimpToCore( name );
264  blinnPhongMaterial->m_hasTexNormal = true;
265  }
266 
267  // Assimp loads objs bump maps as height maps, gj bro
268  if ( AI_SUCCESS == material.Get( AI_MATKEY_TEXTURE( aiTextureType_HEIGHT, 0 ), name ) ) {
269  blinnPhongMaterial->m_texNormal = m_filepath + "/" + assimpToCore( name );
270  blinnPhongMaterial->m_hasTexNormal = true;
271  }
272 
273  if ( AI_SUCCESS == material.Get( AI_MATKEY_TEXTURE( aiTextureType_OPACITY, 0 ), name ) ) {
274  blinnPhongMaterial->m_texOpacity = m_filepath + "/" + assimpToCore( name );
275  blinnPhongMaterial->m_hasTexOpacity = true;
276  }
277  }
278  else { LOG( logINFO ) << "Found assimp default material " << matName; }
279  data.setMaterial( blinnPhongMaterial );
280 }
281 
282 void AssimpGeometryDataLoader::loadGeometryData(
283  const aiScene* scene,
284  std::vector<std::unique_ptr<GeometryData>>& data ) {
285  const uint size = scene->mNumMeshes;
286  std::map<uint, std::size_t> indexTable;
287  std::set<std::string> usedNames;
288  for ( uint i = 0; i < size; ++i ) {
289  aiMesh* mesh = scene->mMeshes[i];
290  if ( mesh->HasPositions() ) {
291  if ( m_verbose ) { LOG( logINFO ) << "Loading mesh attribs..."; }
292  auto geometry = new GeometryData();
293  loadMeshAttrib( *mesh, *geometry, usedNames );
294  if ( m_verbose ) { LOG( logINFO ) << "Mesh attribs loaded."; }
295  // This returns always true (see assimp documentation)
296  if ( scene->HasMaterials() ) {
297  const uint matID = mesh->mMaterialIndex;
298  if ( matID < scene->mNumMaterials ) {
299  aiMaterial* material = scene->mMaterials[matID];
300  loadMaterial( *material, *geometry );
301  }
302  }
303  data.push_back( std::unique_ptr<GeometryData>( geometry ) );
304  indexTable[i] = data.size() - 1;
305 
306  if ( m_verbose ) { geometry->displayInfo(); }
307  }
308  }
309  loadMeshFrame( scene->mRootNode, Core::Transform::Identity(), indexTable, data );
310 }
311 
312 } // namespace IO
313 } // namespace Ra
bool isTetraMesh() const
Return true if the object is a Tetrahedron Mesh.
bool isHexMesh() const
Return true if the object is a Hexahedron Mesh.
void setMaterial(MaterialData *material)
Set the MaterialData for the object.
void setPrimitiveCount(int n)
Used to track easily the number of primitives in the geometry data.
GeometryType getType() const
Return the type of geometry.
Geometry::MultiIndexedGeometry & getGeometry()
Read/write access to the multiIndexedGeometry;.
void setFrame(const Transform &frame)
Set the Transform of the object.
void setName(const std::string &name)
Return the name of the object.
Definition: Cage.cpp:3