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

Last change on this file since 278 was 278, checked in by epyon, 11 years ago
  • assimp module now creates fully pre-transformed meshes with both merged and separate animation
File size: 15.1 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_index_channel* ichannel = mesh_raw_index_channel::create( USHORT, mesh->mNumFaces * 3 );
146        result->set_index_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_channel_data()[0];
297                        assimp_skinned_vtx* va = (assimp_skinned_vtx*)channel->data;
298                        for ( unsigned v = 0; v < channel->count; ++v )
299                        {
300                                assimp_skinned_vtx& vertex = va[v];
301
302                                for (uint32 i = 0 ; i < 4; ++i)
303                                {
304                                        if ( vertex.boneweight[i] > 0.0f )
305                                        {
306                                                vertex.boneindex[i] = translate[vertex.boneindex[i]];
307                                        }
308                                }
309                        }
310                }
311        }
312        return model;
313}
314
315assimp_animation* nv::assimp_loader::release_animation( size_t, bool pre_transform, const std::vector< assimp_bone >* bone_data )
316{
317        if ( m_scene == nullptr ) return nullptr;
318        const aiScene* scene = (const aiScene*)m_scene;
319        if ( scene->mRootNode == nullptr || scene->mAnimations[0] == nullptr ) return nullptr;
320        assimp_animation* result = new assimp_animation;
321
322        // need resize not to copy!
323        result->nodes.resize( count_nodes( scene->mRootNode ) );
324
325        const aiNode*      root = scene->mRootNode;
326        const aiAnimation* anim = scene->mAnimations[0]; // if implemented, change in load_node also
327
328        result->fps            = (float)anim->mTicksPerSecond;
329        result->duration       = (float)anim->mDuration;
330        result->pretransformed = pre_transform;
331
332        load_node( result, root, 0, -1 );
333        // TODO: this is not used when pretransformed, is it used otherwise?
334
335        if ( bone_data )
336        {
337                std::unordered_map< std::string, uint16 > names;
338                for ( uint16 bi = 0; bi < bone_data->size(); ++bi )
339                {
340                        names[ (*bone_data)[bi].name ] = bi;
341                }
342
343                for ( unsigned i = 0; i < result->nodes.size(); ++i )
344                {
345                        assimp_animated_node_data& node = result->nodes[i];
346                        node.bone_id = -1;
347                        auto bi = names.find( node.name );
348                        if ( bi != names.end() )
349                        {
350                                node.bone_id = bi->second;
351                        }
352                        if ( node.parent_id != -1 )
353                        {
354                                result->nodes[ node.parent_id ].children.push_back( &node );
355                        }
356                }
357        }
358
359        return result;
360}
361
362nv::uint32 nv::assimp_loader::count_nodes( const void* node ) const
363{
364        const aiNode* ainode = (const aiNode*)node;
365        nv::uint32 count = 1;
366        for ( unsigned i = 0; i < ainode->mNumChildren; ++i )
367        {
368                count += count_nodes( ainode->mChildren[i] );
369        }
370        return count;
371}
372
373nv::uint32 nv::assimp_loader::load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id )
374{
375        const aiScene* scene = (const aiScene*)m_scene;
376        const aiNode*  node  = (const aiNode*)vnode;
377        string name( node->mName.data );
378        const aiAnimation* anim  = scene->mAnimations[0];
379        const aiNodeAnim*  anode = nullptr;
380
381        for ( unsigned i = 0 ; i < anim->mNumChannels ; i++ )
382        {
383                anode = anim->mChannels[i];
384                if ( std::string( anode->mNodeName.data ) == name ) break;
385                anode = nullptr;
386        }
387
388        assimp_animated_node_data& a_data = data->nodes[ this_id ];
389
390        a_data.name      = name;
391        a_data.parent_id = parent_id;
392        a_data.bone_id = -1;
393        a_data.transform = nv::assimp_mat4_cast( node->mTransformation );
394
395        if (anode)
396        {
397                if ( data->pretransformed )
398                {
399                        a_data.keys = create_transformed_keys( anode, parent_id >= 0 ? data->nodes[ parent_id ].keys : nullptr );
400                }
401                else
402                {
403                        a_data.keys = create_direct_keys( anode );
404                }
405        }
406
407        nv::uint32 next = this_id + 1;
408        for ( unsigned i = 0; i < node->mNumChildren; ++i )
409        {
410                next = load_node( data, node->mChildren[i], next, this_id );
411        }
412        return next;
413}
414
415key_animation_data* nv::assimp_loader::create_transformed_keys( const void* vnode, const key_animation_data* parent_keys )
416{
417        const aiNodeAnim* node = (const aiNodeAnim*)vnode;
418        size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
419        nv::transform_vector* keys = new nv::transform_vector;
420        for ( unsigned n = 0; n < max_keys; ++n )
421        {
422                size_t pn = glm::min( node->mNumPositionKeys - 1, n );
423                size_t rn = glm::min( node->mNumRotationKeys - 1, n );
424                nv::vec3 pos = nv::assimp_vec3_cast(node->mPositionKeys[pn].mValue);
425                nv::quat rot = nv::assimp_quat_cast(node->mRotationKeys[rn].mValue);
426                // TODO: only do the calculation when a rotate transform is present!
427                nv::transform ptr( vec3(), glm::quat_cast( m_rotate_transform ) );
428                if ( parent_keys )
429                {
430                        const nv::transform_vector* pv = (const nv::transform_vector*)parent_keys;
431                        if ( pv && pv->size() > 0 ) ptr = pv->get( glm::min( n, pv->size()-1 ) );
432                }
433                nv::transform key( ptr * nv::transform( pos * m_scale, rot ) );
434                keys->insert( key );
435        }
436        return keys;
437}
438
439key_animation_data* nv::assimp_loader::create_direct_keys( const void* vnode )
440{
441        // TODO : support for m_rotate_transform and m_scale! ( should be easy )
442        const aiNodeAnim* node = (const aiNodeAnim*)vnode;
443        if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 ) return nullptr;
444        key_vectors_prs* keys = new key_vectors_prs;
445
446        for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
447        {
448                keys->insert_position( (float)node->mPositionKeys[np].mTime, m_scale * assimp_vec3_cast(node->mPositionKeys[np].mValue) );
449        }
450        for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
451        {
452                keys->insert_rotation( (float)node->mRotationKeys[np].mTime, assimp_quat_cast(node->mRotationKeys[np].mValue) );
453        }
454        if ( node->mNumScalingKeys > 0 )
455        {
456                nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
457                float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
458                if ( node->mNumScalingKeys > 1 || scale_value > 0.001 )
459                {
460                        NV_LOG( nv::LOG_WARNING, "scale key significant!" );
461                        for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
462                        {
463                                keys->insert_scale( (float)node->mScalingKeys[np].mTime, assimp_vec3_cast(node->mScalingKeys[np].mValue) );
464                        }
465                }
466        }
467        return keys;
468}
469
Note: See TracBrowser for help on using the repository browser.