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