Radium Engine  1.5.20
Loading...
Searching...
No Matches
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
10namespace Ra {
11namespace IO {
12
13using namespace Core::Utils; // log
14using namespace Core::Asset; // log
15
16AssimpGeometryDataLoader::AssimpGeometryDataLoader( const std::string& filepath,
17 const bool VERBOSE_MODE ) :
18 DataLoader<GeometryData>( VERBOSE_MODE ), m_filepath( filepath ) {}
19
20AssimpGeometryDataLoader::~AssimpGeometryDataLoader() = default;
21
22void AssimpGeometryDataLoader::loadData( const aiScene* scene,
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
46bool AssimpGeometryDataLoader::sceneHasGeometry( const aiScene* scene ) const {
47 return ( sceneGeometrySize( scene ) != 0 );
48}
49
50uint 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
62void 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
138void AssimpGeometryDataLoader::loadMeshFrame(
139 const aiNode* node,
140 const Core::Transform& parentFrame,
141 const std::map<uint, size_t>& indexTable,
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
159void 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
170void 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
200void AssimpGeometryDataLoader::fetchPolyhedron( const aiMesh& mesh, GeometryData& data ) const {
201 CORE_UNUSED( mesh );
202 CORE_UNUSED( data );
203 // TO DO
204}
205
206void 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
282void AssimpGeometryDataLoader::loadGeometryData(
283 const aiScene* scene,
285 const uint size = scene->mNumMeshes;
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
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:3
T size(T... args)