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

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