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

Last change on this file since 277 was 277, checked in by epyon, 11 years ago
  • fixed and corrected the transformation mess in the assimp loader
  • fixed set_opt_uniform_array
File size: 14.9 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        vec3 vertex_offset     = glm::vec3();
90        mat3 vertex_transform  = scaled_rotatation;
91        mat3 normal_transform  = glm::mat3( m_rotate_transform );
92
93        bool skinned = mesh->mNumBones > 0;
94        mesh_raw_channel* channel = nullptr;
95        if ( skinned )
96                channel = mesh_raw_channel::create< assimp_skinned_vtx >( mesh->mNumVertices );
97        else
98                channel = mesh_raw_channel::create< assimp_plain_vtx >( mesh->mNumVertices );
99
100        result->add_channel( channel );
101        for (unsigned int i=0; i<mesh->mNumVertices; i++)
102        {
103                vec3 v = vertex_transform * assimp_vec3_cast( mesh->mVertices[ i ] ) + vertex_offset;
104                vec3 n = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mNormals[ i ] ) );
105                vec3 t = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mTangents[ i ] ) );
106                vec3 b = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mBitangents[ i ] ) );
107                vec2 s = assimp_st_cast( mesh->mTextureCoords[ 0 ][ i ] );
108
109                glm::vec3 t_i = glm::normalize (t - n * glm::dot (n, t));
110                float det = (glm::dot (glm::cross (n, t), b));
111                det = (det < 0.0f ? -1.0f : 1.0f );
112                nv::vec4 vt ( t_i[0], t_i[1], t_i[2], det );
113                if ( skinned )
114                        ((assimp_skinned_vtx*)channel->data)[i] = assimp_skinned_vtx( v, s, n, vt );
115                else
116                        ((assimp_plain_vtx*)channel->data)[i] = assimp_plain_vtx( v, s, n, vt );
117        }
118
119        if ( skinned )
120        {
121                assimp_skinned_vtx* vtx = (assimp_skinned_vtx*)channel->data;
122                for (unsigned int m=0; m<mesh->mNumBones; m++)
123                {
124                        aiBone* bone  = mesh->mBones[m];
125                        for (unsigned int w=0; w<bone->mNumWeights; w++)
126                        {
127                                assimp_skinned_vtx& v = vtx[ bone->mWeights[w].mVertexId ];
128                                bool found = false;
129                                for (nv::uint32 i = 0 ; i < 4; ++i)
130                                {
131                                        if ( v.boneweight[i] <= 0.0f )
132                                        {
133                                                v.boneindex[i] = m;
134                                                v.boneweight[i] = bone->mWeights[w].mWeight;
135                                                found = true;
136                                                break;
137                                        }
138                                }
139                                NV_ASSERT( found, "Too many weights!" );
140                        }
141                }
142        }
143
144        mesh_raw_index_channel* ichannel = mesh_raw_index_channel::create( USHORT, mesh->mNumFaces * 3 );
145        result->set_index_channel( ichannel );
146        uint16* indices = (uint16*)ichannel->data;
147        for (unsigned int i=0; i<mesh->mNumFaces; i++)
148        {
149                const aiFace* face = &mesh->mFaces[i];
150                for (unsigned int j=0; j<face->mNumIndices; j++)
151                {
152                        indices[ i*3 + j ] = (uint16)face->mIndices[j];
153                }
154        }
155
156        return result;
157}
158
159nv::assimp_loader::~assimp_loader()
160{
161        if ( m_scene != nullptr ) aiReleaseImport( (const aiScene*)m_scene );
162}
163
164bool nv::assimp_loader::load_bones( size_t index, std::vector< assimp_bone >& bones )
165{
166        if ( m_scene == nullptr ) return false;
167        const aiScene* scene = (const aiScene*)m_scene;
168        const aiMesh*  mesh  = scene->mMeshes[ index ];
169        if ( mesh->mNumBones == 0 ) return false;
170
171        mat4 bone_transform    = glm::inverse( glm::scale( m_scale, m_scale, m_scale ) * glm::mat4(m_rotate_transform) ); //glm::scale( 1.f/m_scale, 1.f/m_scale, 1.f/m_scale ) * m_rotate_transform;
172
173        for (unsigned int m=0; m<mesh->mNumBones; m++)
174        {
175                aiBone* bone   = mesh->mBones[m];
176                mat4    offset = assimp_mat4_cast( bone->mOffsetMatrix ) * bone_transform;
177                bones.emplace_back( bone->mName.data, offset );
178        }
179        return true;
180}
181
182void nv::assimp_loader::scene_report() const
183{
184        const aiScene* scene = (const aiScene*)m_scene;
185        if ( scene == nullptr ) return;
186
187        NV_LOG( nv::LOG_NOTICE, "------------------------" );
188        NV_LOG( nv::LOG_NOTICE, "Texture   count - " << scene->mNumTextures );
189        NV_LOG( nv::LOG_NOTICE, "Animation count - " << scene->mNumAnimations );
190        NV_LOG( nv::LOG_NOTICE, "Material  count - " << scene->mNumMaterials );
191        NV_LOG( nv::LOG_NOTICE, "Meshes    count - " << scene->mNumMeshes );
192        NV_LOG( nv::LOG_NOTICE, "------------------------" );
193
194        aiNode* root = scene->mRootNode;
195        if (root)
196        {
197                NV_LOG( nv::LOG_NOTICE, "Root node  - " << root->mName.data );
198                NV_LOG( nv::LOG_NOTICE, "  meshes   - " << root->mNumMeshes );
199                NV_LOG( nv::LOG_NOTICE, "  children - " << root->mNumChildren );
200        }
201        else
202        {
203                NV_LOG( nv::LOG_NOTICE, "No root node!" );
204        }
205        NV_LOG( nv::LOG_NOTICE, "------------------------" );
206
207        if ( scene->mNumMeshes > 0 )
208        {
209                for ( nv::uint32 mc = 0; mc < scene->mNumMeshes; mc++ )
210                {
211                        aiMesh* mesh = scene->mMeshes[mc];
212
213                        NV_LOG( nv::LOG_NOTICE, "Mesh #"<<mc<<"   - " << mesh->mName.data );
214                        NV_LOG( nv::LOG_NOTICE, "  bones   - " << mesh->mNumBones );
215                        NV_LOG( nv::LOG_NOTICE, "  uvs     - " << mesh->mNumUVComponents[0] );
216                        NV_LOG( nv::LOG_NOTICE, "  verts   - " << mesh->mNumVertices );
217                        NV_LOG( nv::LOG_NOTICE, "  faces   - " << mesh->mNumFaces );
218
219                        //                      NV_LOG( nv::LOG_NOTICE, "Bones:" );
220                        //                      for (unsigned int m=0; m<mesh->mNumBones; m++)
221                        //                      {
222                        //                              aiBone* bone  = mesh->mBones[m];
223                        //                              NV_LOG( nv::LOG_DEBUG, bone->mName.C_Str() );
224                        //                      }
225                }
226        }
227        else
228        {
229                NV_LOG( nv::LOG_NOTICE, "No meshes!" );
230        }
231        NV_LOG( nv::LOG_NOTICE, "------------------------" );
232
233
234        //      if ( scene->mNumMaterials > 0 )
235        //      {
236        //              for (unsigned int m=0; m < scene->mNumMaterials; m++)
237        //              {
238        //                      int texIndex = 0;
239        //                      aiReturn texFound = aiReturn_SUCCESS;
240        //                      aiString path;  // filename
241        //                      while (texFound == aiReturn_SUCCESS)
242        //                      {
243        //                              texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
244        //                              NV_LOG( nv::LOG_NOTICE, "  material - " << path.data );
245        //                              texIndex++;
246        //                      }
247        //              }
248        //      }
249        //      else
250        //      {
251        //              NV_LOG( nv::LOG_NOTICE, "No materials" );
252        //      }
253        //      NV_LOG( nv::LOG_NOTICE, "------------------------" );
254
255}
256
257assimp_model* nv::assimp_loader::release_merged_model()
258{
259        if ( m_scene == nullptr || m_mesh_count == 0 ) return nullptr;
260        assimp_model* model = new assimp_model;
261
262        for ( size_t m = 0; m < m_mesh_count; ++m )
263        {
264                model->meshes.push_back( release_mesh_data(m) );
265        }
266
267        std::unordered_map< std::string, uint16 > names;
268        for ( unsigned int m = 0; m < model->meshes.size(); ++m )
269        {
270                sint16 translate[MAX_BONES];
271                std::vector< assimp_bone > bones;
272                load_bones( m, bones );
273                for ( unsigned int b = 0; b < bones.size(); ++b )
274                {
275
276                        assimp_bone& bone = bones[b];
277                        auto iname = names.find( bone.name );
278                        if ( iname == names.end() )
279                        {
280                                NV_ASSERT( model->bones.size() < MAX_BONES, "Too many bones to merge!" );
281                                sint16 index = (sint16)model->bones.size();
282                                model->bones.push_back( bone );
283                                names[ bone.name ] = index;
284                                translate[b] = index;
285                        }
286                        else
287                        {
288                                translate[b] = (sint16)iname->second;
289                        }
290                }
291                if ( m > 0 )
292                {
293                        mesh_data* mesh = model->meshes[m];
294                        mesh_raw_channel* channel = mesh->get_channel_data()[0];
295                        assimp_skinned_vtx* va = (assimp_skinned_vtx*)channel->data;
296                        for ( unsigned v = 0; v < channel->count; ++v )
297                        {
298                                assimp_skinned_vtx& vertex = va[v];
299
300                                for (uint32 i = 0 ; i < 4; ++i)
301                                {
302                                        if ( vertex.boneweight[i] > 0.0f )
303                                        {
304                                                vertex.boneindex[i] = translate[vertex.boneindex[i]];
305                                        }
306                                }
307                        }
308                }
309        }
310        return model;
311}
312
313assimp_animation* nv::assimp_loader::release_animation( size_t, bool pre_transform, const std::vector< assimp_bone >* bone_data )
314{
315        if ( m_scene == nullptr ) return nullptr;
316        const aiScene* scene = (const aiScene*)m_scene;
317        if ( scene->mRootNode == nullptr || scene->mAnimations[0] == nullptr ) return nullptr;
318        assimp_animation* result = new assimp_animation;
319
320        // need resize not to copy!
321        result->nodes.resize( count_nodes( scene->mRootNode ) );
322
323        const aiNode*      root = scene->mRootNode;
324        const aiAnimation* anim = scene->mAnimations[0]; // if implemented, change in load_node also
325
326        result->fps            = (float)anim->mTicksPerSecond;
327        result->duration       = (float)anim->mDuration;
328        result->pretransformed = pre_transform;
329
330        load_node( result, root, 0, -1 );
331        result->nodes[0].transform = glm::scale( m_scale, m_scale, m_scale ) * result->nodes[0].transform;
332
333        if ( bone_data )
334        {
335                std::unordered_map< std::string, uint16 > names;
336                for ( uint16 bi = 0; bi < bone_data->size(); ++bi )
337                {
338                        names[ (*bone_data)[bi].name ] = bi;
339                }
340
341                for ( unsigned i = 0; i < result->nodes.size(); ++i )
342                {
343                        assimp_animated_node_data& node = result->nodes[i];
344                        node.bone_id = -1;
345                        auto bi = names.find( node.name );
346                        if ( bi != names.end() )
347                        {
348                                node.bone_id = bi->second;
349                        }
350                        if ( node.parent_id != -1 )
351                        {
352                                result->nodes[ node.parent_id ].children.push_back( &node );
353                        }
354                }
355        }
356
357        return result;
358}
359
360nv::uint32 nv::assimp_loader::count_nodes( const void* node ) const
361{
362        const aiNode* ainode = (const aiNode*)node;
363        nv::uint32 count = 1;
364        for ( unsigned i = 0; i < ainode->mNumChildren; ++i )
365        {
366                count += count_nodes( ainode->mChildren[i] );
367        }
368        return count;
369}
370
371nv::uint32 nv::assimp_loader::load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id )
372{
373        const aiScene* scene = (const aiScene*)m_scene;
374        const aiNode*  node  = (const aiNode*)vnode;
375        string name( node->mName.data );
376        const aiAnimation* anim  = scene->mAnimations[0];
377        const aiNodeAnim*  anode = nullptr;
378
379        for ( unsigned i = 0 ; i < anim->mNumChannels ; i++ )
380        {
381                anode = anim->mChannels[i];
382                if ( std::string( anode->mNodeName.data ) == name ) break;
383                anode = nullptr;
384        }
385
386        assimp_animated_node_data& a_data = data->nodes[ this_id ];
387
388        a_data.name      = name;
389        a_data.parent_id = parent_id;
390        a_data.bone_id = -1;
391        a_data.transform = nv::assimp_mat4_cast( node->mTransformation );
392
393        if (anode)
394        {
395                if ( data->pretransformed )
396                {
397                        a_data.keys = create_transformed_keys( anode, parent_id >= 0 ? data->nodes[ parent_id ].keys : nullptr );
398                }
399                else
400                {
401                        a_data.keys = create_direct_keys( anode );
402                }
403        }
404
405        nv::uint32 next = this_id + 1;
406        for ( unsigned i = 0; i < node->mNumChildren; ++i )
407        {
408                next = load_node( data, node->mChildren[i], next, this_id );
409        }
410        return next;
411}
412
413key_animation_data* nv::assimp_loader::create_transformed_keys( const void* vnode, const key_animation_data* parent_keys )
414{
415        const aiNodeAnim* node = (const aiNodeAnim*)vnode;
416        size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
417        nv::transform_vector* keys = new nv::transform_vector;
418        for ( unsigned n = 0; n < max_keys; ++n )
419        {
420                size_t pn = glm::min( node->mNumPositionKeys - 1, n );
421                size_t rn = glm::min( node->mNumRotationKeys - 1, n );
422                nv::vec3 pos = nv::assimp_vec3_cast(node->mPositionKeys[pn].mValue);
423                nv::quat rot = nv::assimp_quat_cast(node->mRotationKeys[rn].mValue);
424                nv::transform ptr;
425                if ( parent_keys )
426                {
427                        const nv::transform_vector* pv = (const nv::transform_vector*)parent_keys;
428                        if ( pv && pv->size() > 0 ) ptr = pv->get( glm::min( n, pv->size()-1 ) );
429                }
430
431                nv::vec3 rot_pos = ptr.get_orientation() * pos;
432                pos = ptr.get_position() + rot_pos;
433                rot = glm::normalize( ptr.get_orientation() * rot );
434                keys->insert( nv::transform( pos, rot ) );
435        }
436        return keys;
437}
438
439key_animation_data* nv::assimp_loader::create_direct_keys( const void* vnode )
440{
441        const aiNodeAnim* node = (const aiNodeAnim*)vnode;
442        if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 ) return nullptr;
443        key_vectors_prs* keys = new key_vectors_prs;
444
445        for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
446        {
447                keys->insert_position( (float)node->mPositionKeys[np].mTime, assimp_vec3_cast(node->mPositionKeys[np].mValue) );
448        }
449        for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
450        {
451                keys->insert_rotation( (float)node->mRotationKeys[np].mTime, assimp_quat_cast(node->mRotationKeys[np].mValue) );
452        }
453        if ( node->mNumScalingKeys > 0 )
454        {
455                nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
456                float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
457                if ( node->mNumScalingKeys > 1 || scale_value > 0.001 )
458                {
459                        NV_LOG( nv::LOG_WARNING, "scale key significant!" );
460                        for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
461                        {
462                                keys->insert_scale( (float)node->mScalingKeys[np].mTime, assimp_vec3_cast(node->mScalingKeys[np].mValue) );
463                        }
464                }
465        }
466        return keys;
467}
468
Note: See TracBrowser for help on using the repository browser.