source: trunk/src/formats/assimp_loader.cc @ 280

Last change on this file since 280 was 280, checked in by epyon, 11 years ago
  • unified mesh_raw_channel and mesh_raw_index_channel
  • string_table cleaned up and implementation of creator split into a cc file
File size: 15.4 KB
Line 
1// Copyright (C) 2014 ChaosForge Ltd
2// http://chaosforge.org/
3//
4// This file is part of NV Libraries.
5// For conditions of distribution and use, see copyright notice in nv.hh
6
7#include "nv/formats/assimp_loader.hh"
8#include <unordered_map>
9#include <glm/gtx/transform.hpp>
10#include "nv/io/std_stream.hh"
11#include "nv/lib/assimp.hh"
12
13using namespace nv;
14
15const int MAX_BONES = 64;
16
17nv::assimp_loader::assimp_loader( const string& a_ext, const mat4& a_rotate_transform, float a_scale, uint32 a_assimp_flags /*= 0 */ ) : m_ext( a_ext ), m_rotate_transform( a_rotate_transform ), m_scale( a_scale ), m_assimp_flags( a_assimp_flags ), m_mesh_count(0), m_scene( nullptr )
18{
19        if ( m_assimp_flags == 0 )
20        {
21                m_assimp_flags = (
22                        aiProcess_CalcTangentSpace                              | 
23                        aiProcess_GenSmoothNormals                              | 
24                        aiProcess_JoinIdenticalVertices                 |   
25                        aiProcess_ImproveCacheLocality                  | 
26                        aiProcess_LimitBoneWeights                              | 
27                        aiProcess_RemoveRedundantMaterials      | 
28                        aiProcess_SplitLargeMeshes                              | 
29                        aiProcess_Triangulate                                   | 
30                        aiProcess_GenUVCoords                   | 
31                        aiProcess_SortByPType                   | 
32                        aiProcess_FindDegenerates               | 
33                        aiProcess_FindInvalidData               | 
34                        0 );
35        }
36}
37
38nv::assimp_loader::assimp_loader( const string& a_ext, uint32 a_assimp_flags /*= 0 */ ) : m_ext( a_ext ), m_rotate_transform(), m_scale( 1.0f ), m_assimp_flags( a_assimp_flags ), m_mesh_count(0), m_scene( nullptr )
39{
40        if ( m_assimp_flags == 0 )
41        {
42                m_assimp_flags = (
43                        aiProcess_CalcTangentSpace                              | 
44                        aiProcess_GenSmoothNormals                              | 
45                        aiProcess_JoinIdenticalVertices                 |   
46                        aiProcess_ImproveCacheLocality                  | 
47                        aiProcess_LimitBoneWeights                              | 
48                        aiProcess_RemoveRedundantMaterials      | 
49                        aiProcess_SplitLargeMeshes                              | 
50                        aiProcess_Triangulate                                   | 
51                        aiProcess_GenUVCoords                   | 
52                        aiProcess_SortByPType                   | 
53                        aiProcess_FindDegenerates               | 
54                        aiProcess_FindInvalidData               | 
55                        0 );
56        }
57}
58bool nv::assimp_loader::load( stream& source )
59{
60        if ( m_scene != nullptr ) aiReleaseImport( (const aiScene*)m_scene );
61        m_scene = nullptr;
62        m_mesh_count = 0;
63        load_assimp_library();
64        NV_LOG( nv::LOG_NOTICE, "AssImp loading file..." );
65        int size = (int)source.size();
66        char* data  = new char[ size ];
67        source.read( data, size, 1 );
68        const aiScene* scene = aiImportFileFromMemory( data, size, m_assimp_flags, m_ext.c_str() );
69
70        if( !scene)
71        {
72                NV_LOG( nv::LOG_ERROR, aiGetErrorString() );
73                return false;
74        }
75        m_scene      = scene;
76        m_mesh_count = scene->mNumMeshes;
77        NV_LOG( nv::LOG_NOTICE, "Loading successfull" );
78        return true;
79}
80
81mesh_data* nv::assimp_loader::release_mesh_data( size_t index /*= 0 */ )
82{
83        if ( index >= m_mesh_count ) return nullptr;
84        mesh_data* result = new mesh_data();
85        const aiScene* scene = (const aiScene*)m_scene;
86        const aiMesh*  mesh  = scene->mMeshes[ index ];
87
88        mat3 scaled_rotatation = glm::mat3( glm::scale( m_scale, m_scale, m_scale ) * m_rotate_transform );
89        //mat3 scaled_rotatation = glm::mat3( m_rotate_transform );
90        vec3 vertex_offset     = glm::vec3();
91        mat3 vertex_transform  = scaled_rotatation;
92        mat3 normal_transform  = glm::mat3( m_rotate_transform );
93
94        bool skinned = mesh->mNumBones > 0;
95        mesh_raw_channel* channel = nullptr;
96        if ( skinned )
97                channel = mesh_raw_channel::create< assimp_skinned_vtx >( mesh->mNumVertices );
98        else
99                channel = mesh_raw_channel::create< assimp_plain_vtx >( mesh->mNumVertices );
100
101        result->add_channel( channel );
102        for (unsigned int i=0; i<mesh->mNumVertices; i++)
103        {
104                vec3 v = vertex_transform * assimp_vec3_cast( mesh->mVertices[ i ] ) + vertex_offset;
105                vec3 n = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mNormals[ i ] ) );
106                vec3 t = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mTangents[ i ] ) );
107                vec3 b = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mBitangents[ i ] ) );
108                vec2 s = assimp_st_cast( mesh->mTextureCoords[ 0 ][ i ] );
109
110                glm::vec3 t_i = glm::normalize (t - n * glm::dot (n, t));
111                float det = (glm::dot (glm::cross (n, t), b));
112                det = (det < 0.0f ? -1.0f : 1.0f );
113                nv::vec4 vt ( t_i[0], t_i[1], t_i[2], det );
114                if ( skinned )
115                        ((assimp_skinned_vtx*)channel->data)[i] = assimp_skinned_vtx( v, s, n, vt );
116                else
117                        ((assimp_plain_vtx*)channel->data)[i] = assimp_plain_vtx( v, s, n, vt );
118        }
119
120        if ( skinned )
121        {
122                assimp_skinned_vtx* vtx = (assimp_skinned_vtx*)channel->data;
123                for (unsigned int m=0; m<mesh->mNumBones; m++)
124                {
125                        aiBone* bone  = mesh->mBones[m];
126                        for (unsigned int w=0; w<bone->mNumWeights; w++)
127                        {
128                                assimp_skinned_vtx& v = vtx[ bone->mWeights[w].mVertexId ];
129                                bool found = false;
130                                for (nv::uint32 i = 0 ; i < 4; ++i)
131                                {
132                                        if ( v.boneweight[i] <= 0.0f )
133                                        {
134                                                v.boneindex[i] = m;
135                                                v.boneweight[i] = bone->mWeights[w].mWeight;
136                                                found = true;
137                                                break;
138                                        }
139                                }
140                                NV_ASSERT( found, "Too many weights!" );
141                        }
142                }
143        }
144
145        mesh_raw_channel* ichannel = mesh_raw_channel::create_index( USHORT, mesh->mNumFaces * 3 );
146        result->add_channel( ichannel );
147        uint16* indices = (uint16*)ichannel->data;
148        for (unsigned int i=0; i<mesh->mNumFaces; i++)
149        {
150                const aiFace* face = &mesh->mFaces[i];
151                for (unsigned int j=0; j<face->mNumIndices; j++)
152                {
153                        indices[ i*3 + j ] = (uint16)face->mIndices[j];
154                }
155        }
156
157        return result;
158}
159
160nv::assimp_loader::~assimp_loader()
161{
162        if ( m_scene != nullptr ) aiReleaseImport( (const aiScene*)m_scene );
163}
164
165bool nv::assimp_loader::load_bones( size_t index, std::vector< assimp_bone >& bones )
166{
167        if ( m_scene == nullptr ) return false;
168        const aiScene* scene = (const aiScene*)m_scene;
169        const aiMesh*  mesh  = scene->mMeshes[ index ];
170        if ( mesh->mNumBones == 0 ) return false;
171
172        mat4 bone_transform    = glm::scale( 1.f/m_scale, 1.f/m_scale, 1.f/m_scale ) *  glm::inverse( glm::mat4(m_rotate_transform) );
173        mat4 bone_pre_transform    = glm::scale( m_scale, m_scale, m_scale );
174
175        for (unsigned int m=0; m<mesh->mNumBones; m++)
176        {
177                aiBone* bone   = mesh->mBones[m];
178                mat4    offset = bone_pre_transform * assimp_mat4_cast( bone->mOffsetMatrix ) * bone_transform;
179                bones.emplace_back( bone->mName.data, offset );
180        }
181        return true;
182}
183
184void nv::assimp_loader::scene_report() const
185{
186        const aiScene* scene = (const aiScene*)m_scene;
187        if ( scene == nullptr ) return;
188
189        NV_LOG( nv::LOG_NOTICE, "------------------------" );
190        NV_LOG( nv::LOG_NOTICE, "Texture   count - " << scene->mNumTextures );
191        NV_LOG( nv::LOG_NOTICE, "Animation count - " << scene->mNumAnimations );
192        NV_LOG( nv::LOG_NOTICE, "Material  count - " << scene->mNumMaterials );
193        NV_LOG( nv::LOG_NOTICE, "Meshes    count - " << scene->mNumMeshes );
194        NV_LOG( nv::LOG_NOTICE, "------------------------" );
195
196        aiNode* root = scene->mRootNode;
197        if (root)
198        {
199                NV_LOG( nv::LOG_NOTICE, "Root node  - " << root->mName.data );
200                NV_LOG( nv::LOG_NOTICE, "  meshes   - " << root->mNumMeshes );
201                NV_LOG( nv::LOG_NOTICE, "  children - " << root->mNumChildren );
202        }
203        else
204        {
205                NV_LOG( nv::LOG_NOTICE, "No root node!" );
206        }
207        NV_LOG( nv::LOG_NOTICE, "------------------------" );
208
209        if ( scene->mNumMeshes > 0 )
210        {
211                for ( nv::uint32 mc = 0; mc < scene->mNumMeshes; mc++ )
212                {
213                        aiMesh* mesh = scene->mMeshes[mc];
214
215                        NV_LOG( nv::LOG_NOTICE, "Mesh #"<<mc<<"   - " << std::string( mesh->mName.data ) );
216                        NV_LOG( nv::LOG_NOTICE, "  bones   - " << mesh->mNumBones );
217                        NV_LOG( nv::LOG_NOTICE, "  uvs     - " << mesh->mNumUVComponents[0] );
218                        NV_LOG( nv::LOG_NOTICE, "  verts   - " << mesh->mNumVertices );
219                        NV_LOG( nv::LOG_NOTICE, "  faces   - " << mesh->mNumFaces );
220
221                        //                      NV_LOG( nv::LOG_NOTICE, "Bones:" );
222                        //                      for (unsigned int m=0; m<mesh->mNumBones; m++)
223                        //                      {
224                        //                              aiBone* bone  = mesh->mBones[m];
225                        //                              NV_LOG( nv::LOG_DEBUG, bone->mName.C_Str() );
226                        //                      }
227                }
228        }
229        else
230        {
231                NV_LOG( nv::LOG_NOTICE, "No meshes!" );
232        }
233        NV_LOG( nv::LOG_NOTICE, "------------------------" );
234
235
236        //      if ( scene->mNumMaterials > 0 )
237        //      {
238        //              for (unsigned int m=0; m < scene->mNumMaterials; m++)
239        //              {
240        //                      int texIndex = 0;
241        //                      aiReturn texFound = aiReturn_SUCCESS;
242        //                      aiString path;  // filename
243        //                      while (texFound == aiReturn_SUCCESS)
244        //                      {
245        //                              texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
246        //                              NV_LOG( nv::LOG_NOTICE, "  material - " << path.data );
247        //                              texIndex++;
248        //                      }
249        //              }
250        //      }
251        //      else
252        //      {
253        //              NV_LOG( nv::LOG_NOTICE, "No materials" );
254        //      }
255        //      NV_LOG( nv::LOG_NOTICE, "------------------------" );
256
257}
258
259assimp_model* nv::assimp_loader::release_merged_model()
260{
261        if ( m_scene == nullptr || m_mesh_count == 0 ) return nullptr;
262        assimp_model* model = new assimp_model;
263
264        for ( size_t m = 0; m < m_mesh_count; ++m )
265        {
266                model->meshes.push_back( release_mesh_data(m) );
267        }
268
269        std::unordered_map< std::string, uint16 > names;
270        for ( unsigned int m = 0; m < model->meshes.size(); ++m )
271        {
272                sint16 translate[MAX_BONES];
273                std::vector< assimp_bone > bones;
274                load_bones( m, bones );
275                for ( unsigned int b = 0; b < bones.size(); ++b )
276                {
277
278                        assimp_bone& bone = bones[b];
279                        auto iname = names.find( bone.name );
280                        if ( iname == names.end() )
281                        {
282                                NV_ASSERT( model->bones.size() < MAX_BONES, "Too many bones to merge!" );
283                                sint16 index = (sint16)model->bones.size();
284                                model->bones.push_back( bone );
285                                names[ bone.name ] = index;
286                                translate[b] = index;
287                        }
288                        else
289                        {
290                                translate[b] = (sint16)iname->second;
291                        }
292                }
293                if ( m > 0 )
294                {
295                        mesh_data* mesh = model->meshes[m];
296                        mesh_raw_channel* channel = mesh->get_raw_channels()[0];
297                        NV_ASSERT( !channel->is_index(), "index channel in release_merged!" );
298                        assimp_skinned_vtx* va = (assimp_skinned_vtx*)channel->data;
299                        for ( unsigned v = 0; v < channel->count; ++v )
300                        {
301                                assimp_skinned_vtx& vertex = va[v];
302
303                                for (uint32 i = 0 ; i < 4; ++i)
304                                {
305                                        if ( vertex.boneweight[i] > 0.0f )
306                                        {
307                                                vertex.boneindex[i] = translate[vertex.boneindex[i]];
308                                        }
309                                }
310                        }
311                }
312        }
313        return model;
314}
315
316assimp_animation* nv::assimp_loader::release_animation( size_t, bool pre_transform, const std::vector< assimp_bone >* bone_data )
317{
318        if ( m_scene == nullptr ) return nullptr;
319        const aiScene* scene = (const aiScene*)m_scene;
320        if ( scene->mRootNode == nullptr || scene->mAnimations[0] == nullptr ) return nullptr;
321        assimp_animation* result = new assimp_animation;
322
323        // need resize not to copy!
324        result->nodes.resize( count_nodes( scene->mRootNode ) );
325
326        const aiNode*      root = scene->mRootNode;
327        const aiAnimation* anim = scene->mAnimations[0]; // if implemented, change in load_node also
328
329        result->fps            = (float)anim->mTicksPerSecond;
330        result->duration       = (float)anim->mDuration;
331        result->pretransformed = pre_transform;
332
333        load_node( result, root, 0, -1 );
334        // TODO: this is not used when pretransformed, is it used otherwise?
335
336        if ( bone_data )
337        {
338                std::unordered_map< std::string, uint16 > names;
339                for ( uint16 bi = 0; bi < bone_data->size(); ++bi )
340                {
341                        names[ (*bone_data)[bi].name ] = bi;
342                }
343
344                for ( unsigned i = 0; i < result->nodes.size(); ++i )
345                {
346                        assimp_animated_node_data& node = result->nodes[i];
347                        node.bone_id = -1;
348                        auto bi = names.find( node.name );
349                        if ( bi != names.end() )
350                        {
351                                node.bone_id = bi->second;
352                        }
353                        if ( node.parent_id != -1 )
354                        {
355                                result->nodes[ node.parent_id ].children.push_back( &node );
356                        }
357                }
358        }
359
360        return result;
361}
362
363nv::uint32 nv::assimp_loader::count_nodes( const void* node ) const
364{
365        const aiNode* ainode = (const aiNode*)node;
366        nv::uint32 count = 1;
367        for ( unsigned i = 0; i < ainode->mNumChildren; ++i )
368        {
369                count += count_nodes( ainode->mChildren[i] );
370        }
371        return count;
372}
373
374nv::uint32 nv::assimp_loader::load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id )
375{
376        const aiScene* scene = (const aiScene*)m_scene;
377        const aiNode*  node  = (const aiNode*)vnode;
378        string name( node->mName.data );
379        const aiAnimation* anim  = scene->mAnimations[0];
380        const aiNodeAnim*  anode = nullptr;
381
382        for ( unsigned i = 0 ; i < anim->mNumChannels ; i++ )
383        {
384                anode = anim->mChannels[i];
385                if ( std::string( anode->mNodeName.data ) == name ) break;
386                anode = nullptr;
387        }
388
389        assimp_animated_node_data& a_data = data->nodes[ this_id ];
390
391        a_data.name      = name;
392        a_data.parent_id = parent_id;
393        a_data.bone_id = -1;
394        // This value is ignored by the create_transformed_keys, but needed by create_direct_keys!
395        // TODO: find a common solution!
396        //       This is bad because create_transformed_keys never uses node-transformations for
397        //       node's without keys
398        a_data.transform = nv::assimp_mat4_cast( node->mTransformation );
399
400        if (anode)
401        {
402                if ( data->pretransformed )
403                {
404                        a_data.keys = create_transformed_keys( anode, parent_id >= 0 ? data->nodes[ parent_id ].keys : nullptr );
405                }
406                else
407                {
408                        a_data.keys = create_direct_keys( anode );
409                }
410        }
411
412        nv::uint32 next = this_id + 1;
413        for ( unsigned i = 0; i < node->mNumChildren; ++i )
414        {
415                next = load_node( data, node->mChildren[i], next, this_id );
416        }
417        return next;
418}
419
420key_animation_data* nv::assimp_loader::create_transformed_keys( const void* vnode, const key_animation_data* parent_keys )
421{
422        const aiNodeAnim* node = (const aiNodeAnim*)vnode;
423        size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
424        nv::transform_vector* keys = new nv::transform_vector;
425        for ( unsigned n = 0; n < max_keys; ++n )
426        {
427                size_t pn = glm::min( node->mNumPositionKeys - 1, n );
428                size_t rn = glm::min( node->mNumRotationKeys - 1, n );
429                nv::vec3 pos = nv::assimp_vec3_cast(node->mPositionKeys[pn].mValue);
430                nv::quat rot = nv::assimp_quat_cast(node->mRotationKeys[rn].mValue);
431                // TODO: only do the calculation when a rotate transform is present!
432                nv::transform ptr( vec3(), glm::quat_cast( m_rotate_transform ) );
433                if ( parent_keys )
434                {
435                        const nv::transform_vector* pv = (const nv::transform_vector*)parent_keys;
436                        if ( pv && pv->size() > 0 ) ptr = pv->get( glm::min( n, pv->size()-1 ) );
437                }
438                nv::transform key( ptr * nv::transform( pos * m_scale, rot ) );
439                keys->insert( key );
440        }
441        return keys;
442}
443
444key_animation_data* nv::assimp_loader::create_direct_keys( const void* vnode )
445{
446        // TODO : support for m_rotate_transform and m_scale! ( should be easy )
447        const aiNodeAnim* node = (const aiNodeAnim*)vnode;
448        if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 ) return nullptr;
449        key_vectors_prs* keys = new key_vectors_prs;
450
451        for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
452        {
453                keys->insert_position( (float)node->mPositionKeys[np].mTime, m_scale * assimp_vec3_cast(node->mPositionKeys[np].mValue) );
454        }
455        for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
456        {
457                keys->insert_rotation( (float)node->mRotationKeys[np].mTime, assimp_quat_cast(node->mRotationKeys[np].mValue) );
458        }
459        if ( node->mNumScalingKeys > 0 )
460        {
461                nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
462                float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
463                if ( node->mNumScalingKeys > 1 || scale_value > 0.001 )
464                {
465                        NV_LOG( nv::LOG_WARNING, "scale key significant!" );
466                        for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
467                        {
468                                keys->insert_scale( (float)node->mScalingKeys[np].mTime, assimp_vec3_cast(node->mScalingKeys[np].mValue) );
469                        }
470                }
471        }
472        return keys;
473}
474
Note: See TracBrowser for help on using the repository browser.