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

Last change on this file since 277 was 261, checked in by epyon, 11 years ago
  • md5_loader using dynamic_array
  • skeleton animation via pointer, not vector
  • minor fixes
File size: 15.6 KB
RevLine 
[190]1// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
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/md5_loader.hh"
8
9#include <glm/gtc/constants.hpp>
10#include "nv/logging.hh"
11#include "nv/io/std_stream.hh"
[258]12#include "nv/profiler.hh"
[190]13#include <cstring>
14
15using namespace nv;
16
[191]17// based on http://tfc.duke.free.fr/coding/md5-specs-en.html
18
[190]19static void next_line( std::istream& stream )
20{
21        stream.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
22}
23
24static inline void discard( std::istream& stream, const std::string& token )
25{
[226]26//      stream.ignore( std::numeric_limits<std::streamsize>::max(), ' ' );
[190]27        std::string discarded;
28        stream >> discarded;
29        assert( discarded == token );
30}
31
32
33static void remove_quotes( std::string& str )
34{
35        size_t n;
36        while ( ( n = str.find('\"') ) != std::string::npos ) str.erase(n,1);
37}
38
[198]39static void unit_quat_w( glm::quat& quat )
[190]40{
41        float t = 1.0f - ( quat.x * quat.x ) - ( quat.y * quat.y ) - ( quat.z * quat.z );
42        quat.w = ( t < 0.0f ? 0.0f : -sqrtf(t) );
43}
44
45bool md5_loader::load( stream& source )
46{
[261]47        NV_PROFILE( "Load MD5" ); // 16XXms original
[190]48        std_stream sstream( &source );
49        std::string command;
[261]50        dynamic_array< md5_weight > weights;
51        dynamic_array< md5_weight_info > weight_info;
[190]52
53        sstream >> command;
54        while ( !sstream.eof() )
55        {
56                if ( command == "MD5Version" )
57                {
58                        sstream >> m_md5_version;
59                        assert( m_md5_version == 10 );
60                }
61                else if ( command == "commandline" )
62                {
63                        next_line( sstream );
64                }
65                else if ( command == "numJoints" )
66                {
67                        sstream >> m_num_joints;
[261]68                        m_joints.resize( m_num_joints );
[190]69                }
70                else if ( command == "numMeshes" )
71                {
72                        sstream >> m_num_meshes;
[261]73                        m_meshes.resize( m_num_meshes );
74                        m_num_meshes = 0;
[190]75                }
76                else if ( command == "joints" )
77                {
78                        discard( sstream, "{" );
79                        md5_joint joint;
[191]80                        for ( size_t i = 0; i < m_num_joints; ++i )
[190]81                        {
[241]82                                int parent_id;
83                                sstream >> joint.name >> parent_id;
[190]84                                discard( sstream, "(" );
85                                sstream >> joint.pos.x >> joint.pos.y >> joint.pos.z;
86                                discard( sstream, ")" );
87                                discard( sstream, "(" );
88                                sstream >> joint.orient.x >> joint.orient.y >> joint.orient.z;
89                                remove_quotes( joint.name );
90                                unit_quat_w( joint.orient );
[261]91                                m_joints[i] = joint;
[190]92                                next_line( sstream );
93                        }
94                        discard( sstream, "}" );
95                }
96                else if ( command == "mesh" )
97                {
[239]98                        md5_mesh_data* mesh = new md5_mesh_data();
99
[190]100                        int num_verts, num_tris, num_weights;
101
102                        discard( sstream, "{" );
103                        sstream >> command;
104                        while ( command != "}" )
105                        {
106                                if ( command == "shader" )
107                                {
[239]108                                        sstream >> mesh->m_shader;
109                                        remove_quotes( mesh->m_shader );
[190]110                                        // texturePath.replace_extension( ".tga" );
111                                        next_line( sstream );
112                                }
113                                else if ( command == "numverts")
114                                {
115                                        sstream >> num_verts;
[239]116
117                                        {
118                                                mesh_raw_channel* ch_pnt = mesh_raw_channel::create<md5_vtx_pnt>( num_verts );
119                                                mesh_raw_channel* ch_t   = mesh_raw_channel::create<md5_vtx_t>( num_verts );
120                                                mesh->m_pntdata          = (md5_vtx_pnt*)ch_pnt->data;
121                                                mesh->m_tdata            = (md5_vtx_t*)ch_t->data;
122                                                mesh->add_channel( ch_pnt );
123                                                mesh->add_channel( ch_t );
124                                        }
125                                        mesh->m_vtx_data.resize( num_verts );
[259]126                                        weight_info.resize( num_verts );
[239]127
[190]128                                        next_line( sstream );
[226]129                                        std::string line;
[190]130                                        for ( int i = 0; i < num_verts; ++i )
131                                        {
[239]132                                                size_t weight_count;
133                                                size_t start_weight;
134                                                vec2 texcoord;
[190]135
[226]136                                                std::getline( sstream, line );
[239]137                                                sscanf( line.c_str(), "%*s %*u ( %f %f ) %u %u", &(texcoord.x), &(texcoord.y), &(start_weight), &(weight_count) );
[259]138                                                weight_info[i].start_weight = start_weight;
139                                                weight_info[i].weight_count = weight_count;
[239]140                                                mesh->m_tdata[i].texcoord = texcoord;
[190]141                                        } 
142                                }
143                                else if ( command == "numtris" )
144                                {
145                                        sstream >> num_tris;
[239]146
147                                        mesh_raw_index_channel* ch_i = mesh_raw_index_channel::create<uint32>( num_tris * 3 );
148                                        uint32* vtx_i                = (uint32*)ch_i->data;
149                                        mesh->m_idata                = vtx_i;
150                                        uint32 idx = 0;
151                                        mesh->set_index_channel( ch_i );
152
[190]153                                        next_line( sstream );
[226]154                                        std::string line;
[190]155                                        for ( int i = 0; i < num_tris; ++i )
156                                        {
[239]157                                                size_t ti0;
158                                                size_t ti1;
159                                                size_t ti2;
[190]160
[226]161                                                std::getline( sstream, line );
[239]162                                                sscanf( line.c_str(), "%*s %*u %u %u %u )", &(ti0), &(ti1), &(ti2));
[226]163
[239]164                                                vtx_i[idx++] = (uint32)ti0;
165                                                vtx_i[idx++] = (uint32)ti1;
166                                                vtx_i[idx++] = (uint32)ti2;
[190]167                                        }             
168                                }
169                                else if ( command == "numweights" )
170                                {
171                                        sstream >> num_weights;
[261]172                                        weights.resize( num_weights );
[190]173                                        next_line( sstream );
[226]174                                        std::string line;
[190]175                                        for ( int i = 0; i < num_weights; ++i )
176                                        {
177                                                md5_weight weight;
[226]178
179                                                std::getline( sstream, line );
180                                                sscanf( line.c_str(), "%*s %*u %u %f ( %f %f %f )", &(weight.joint_id), &(weight.bias), &(weight.pos.x), &(weight.pos.y), &(weight.pos.z));
[261]181                                                weights[i] = weight;
[190]182                                        }
183                                }
184                                else
185                                {
186                                        next_line( sstream );
187                                }
188
189                                sstream >> command;
190                        }
191
[261]192                        prepare_mesh( mesh, weights.data(), weight_info.data() );
[190]193
[261]194                        m_meshes[ m_num_meshes ] = mesh;
195                        m_num_meshes++;
[190]196                }
197                sstream >> command;
198        }
199
200        return true;
201}
202
[261]203bool md5_loader::prepare_mesh( md5_mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info )
[190]204{
[239]205        uint32 vtx_count = mdata->m_vtx_data.size();
206        md5_vtx_pnt* vtcs = mdata->m_pntdata;
[190]207
[261]208        mdata->m_bone_offset.resize( m_joints.size() );
209        for ( uint32 i = 0; i < m_joints.size(); ++i )
[259]210        {
[261]211                transform j( m_joints[i].pos, m_joints[i].orient );
212                mdata->m_bone_offset[i] = j.inverse();
[259]213        }
214
[239]215        for ( uint32 i = 0; i < vtx_count; ++i )
[190]216        {
[259]217                size_t start_weight = weight_info[i].start_weight;
218                size_t weight_count = weight_info[i].weight_count;
[239]219                md5_vtx_data& vdata = mdata->m_vtx_data[i];
220                md5_vtx_pnt& vtc = vtcs[i];
[190]221
[239]222                vtc.position = glm::vec3(0);
223                vtc.normal   = glm::vec3(0);
224                vtc.tangent  = glm::vec3(0);
[190]225
[261]226                std::sort( weights + start_weight, weights + start_weight + weight_count, [](const md5_weight& a, const md5_weight& b) -> bool { return a.bias > b.bias; } );
[258]227
[259]228                if ( weight_count > 4 )
[258]229                {
230                        float sum = 0.0f;
231                        for ( size_t j = 0; j < 4; ++j )
232                        {
[259]233                                sum += weights[start_weight + j].bias;
[258]234                        }
235                        float ratio = 1.0f / sum;
236                        for ( size_t j = 0; j < 4; ++j )
237                        {
[259]238                                weights[start_weight + j].bias = ratio * weights[start_weight + j].bias;
[258]239                        }
[259]240                        weight_count = 4;
[258]241                }
242
[259]243                for ( size_t j = 0; j < 4; ++j )
[190]244                {
[259]245                        if ( j < weight_count )
246                        {
247                                vdata.boneindex[j]  = weights[start_weight + j].joint_id;
248                                vdata.boneweight[j] = weights[start_weight + j].bias;
249                        }
250                        else
251                        {
252                                vdata.boneindex[j]  = 0;
253                                vdata.boneweight[j] = 0.0f;
254                        }
255                }
[190]256
[259]257                for ( size_t j = 0; j < 4; ++j )
258                {
259                        if ( j < weight_count )
260                        {
261                                md5_weight& weight = weights[start_weight + j];
262                                md5_joint&  joint  = m_joints[weight.joint_id];
263                                glm::vec3 rot_pos = joint.orient * weight.pos;
[190]264
[259]265                                vtc.position += ( joint.pos + rot_pos ) * weight.bias;
266                        }
[190]267                }
268        }
269
[239]270        // Prepare normals
271        uint32 tri_count = mdata->get_count() / 3;
272        for ( unsigned int i = 0; i < tri_count; ++i )
[190]273        {
[239]274                uint32 ti0 = mdata->m_idata[ i * 3 ];
275                uint32 ti1 = mdata->m_idata[ i * 3 + 1 ];
276                uint32 ti2 = mdata->m_idata[ i * 3 + 2 ];
277 
278                glm::vec3 v1 = vtcs[ ti0 ].position;
279                glm::vec3 v2 = vtcs[ ti1 ].position;
280                glm::vec3 v3 = vtcs[ ti2 ].position;
[190]281                glm::vec3 xyz1 = v3 - v1;
282                glm::vec3 xyz2 = v2 - v1;
283
284                glm::vec3 normal = glm::cross( xyz1, xyz2 );
285
[239]286                vtcs[ ti0 ].normal += normal;
287                vtcs[ ti1 ].normal += normal;
288                vtcs[ ti2 ].normal += normal;
[190]289
[239]290                const vec2& w1 = mdata->m_tdata[ ti0 ].texcoord;
291                const vec2& w2 = mdata->m_tdata[ ti1 ].texcoord;
292                const vec2& w3 = mdata->m_tdata[ ti2 ].texcoord;
[190]293
294                vec2 st1 = w3 - w1;
295                vec2 st2 = w2 - w1;
296
297                float coef = 1.0f / (st1.x * st2.y - st2.x * st1.y);
298
299                vec3 tangent = (( xyz1 * st2.y ) - ( xyz2 * st1.y )) * coef;
300
[239]301                vtcs[ ti0 ].tangent += tangent;
302                vtcs[ ti1 ].tangent += tangent;
303                vtcs[ ti2 ].tangent += tangent;
[190]304        }
305
[239]306        for ( size_t i = 0; i < vtx_count; ++i )
[190]307        {
[239]308                md5_vtx_data& vdata = mdata->m_vtx_data[i];
[190]309
[239]310                glm::vec3 normal  = glm::normalize( vtcs[i].normal );
311                glm::vec3 tangent = glm::normalize( vtcs[i].tangent );
312                vtcs[i].normal   = normal;
313                vtcs[i].tangent  = tangent;
[190]314
[259]315                vdata.position = vtcs[i].position;
316                vdata.normal   = glm::vec3(0);
317                vdata.tangent  = glm::vec3(0);
[239]318 
[259]319                for ( size_t j = 0; j < 4; ++j )
[239]320                {
[259]321                        const md5_joint&  joint  = m_joints[vdata.boneindex[j]];
322                        vdata.normal  += ( normal  * joint.orient ) * vdata.boneweight[j];
323                        vdata.tangent += ( tangent * joint.orient ) * vdata.boneweight[j];
[239]324                }
[190]325        }
326
327        return true;
328}
329
[224]330
[191]331md5_animation::md5_animation()
332        : m_md5_version( 0 )
333        , m_num_frames( 0 )
334        , m_num_joints( 0 )
335        , m_frame_rate( 0 )
336        , m_num_animated_components( 0 )
337        , m_anim_duration( 0 )
338        , m_frame_duration( 0 )
339{
340
341}
342
343md5_animation::~md5_animation()
344{
345
346}
347
348bool md5_animation::load_animation( stream& source )
349{
[241]350        std::vector<md5_joint_info> joint_infos;
351        std::vector<transform>      base_frames;
[191]352        m_num_frames = 0;
353
354        std_stream sstream( &source );
355        std::string command;
356
357        sstream >> command;
358        while ( !sstream.eof() )
359        {
360                if ( command == "MD5Version" )
361                {
362                        sstream >> m_md5_version;
363                        assert( m_md5_version == 10 );
364                }
365                else if ( command == "commandline" )
366                {
367                        next_line( sstream );
368                }
369                else if ( command == "numFrames" )
370                {
371                        sstream >> m_num_frames;
372                        next_line( sstream );
373                }
374                else if ( command == "numJoints" )
375                {
376                        sstream >> m_num_joints;
[241]377                        m_joints.reserve( m_num_joints );
[191]378                        next_line( sstream );
379                }
380                else if ( command == "frameRate" )
381                {
382                        sstream >> m_frame_rate;
383                        next_line( sstream );
384                }
385                else if ( command == "numAnimatedComponents" )
386                {
387                        sstream >> m_num_animated_components;
388                        next_line( sstream );
389                }
390                else if ( command == "hierarchy" )
391                {
392                        discard( sstream, "{" );
[198]393                        for ( size_t i = 0; i < m_num_joints; ++i )
[191]394                        {
395                                md5_joint_info joint;
396                                sstream >> joint.name >> joint.parent_id >> joint.flags >> joint.start_index;
397                                remove_quotes( joint.name );
[241]398                                joint_infos.push_back( joint );
399                                m_joints.push_back( md5_joint( joint.parent_id, m_num_frames ) );
[191]400                                next_line( sstream );
401                        }
402                        discard( sstream, "}" );
403                }
404                else if ( command == "bounds" )
405                {
406                        discard( sstream, "{" );
407                        next_line( sstream );
[198]408                        for ( size_t i = 0; i < m_num_frames; ++i )
[191]409                        {
[241]410//                              vec3 min;
411//                              vec3 max;
412//                              discard( sstream, "(" );
413//                              sstream >> min.x >> min.y >> min.z;
414//                              discard( sstream, ")" );
415//                              discard( sstream, "(" );
416//                              sstream >> max.x >> max.y >> max.z;
417//                              m_bounds.push_back( bound );
[191]418                                next_line( sstream );
419                        }
420
421                        discard( sstream, "}" );
422                        next_line( sstream );
423                }
424                else if ( command == "baseframe" )
425                {
426                        discard( sstream, "{" );
427                        next_line( sstream );
428
[198]429                        for ( size_t i = 0; i < m_num_joints; ++i )
[191]430                        {
[241]431                                transform base_frame;
432                                vec3 pos;
433                                quat orient;
[191]434                                discard( sstream, "(" );
[241]435                                sstream >> pos.x >> pos.y >> pos.z;
[191]436                                discard( sstream, ")" );
437                                discard( sstream, "(" );
[241]438                                sstream >> orient.x >> orient.y >> orient.z;
[191]439                                next_line( sstream );
440
[241]441                                base_frames.emplace_back( pos, orient );
[191]442                        }
443                        discard( sstream, "}" );
444                        next_line( sstream );
445                }
446                else if ( command == "frame" )
447                {
[241]448                        std::vector<float> frame;
449                        int frame_id;
450                        sstream >> frame_id;
[191]451                        discard( sstream, "{" );
452                        next_line( sstream );
453
[241]454                        frame.reserve( m_num_animated_components );
[226]455                        char buf[50];
[198]456                        for ( size_t i = 0; i < m_num_animated_components; ++i )
[191]457                        {
[226]458                                sstream >> buf;
[241]459                                frame.push_back((float)atof(buf));
[191]460                        }
461
[241]462                        build_frame_skeleton( joint_infos, base_frames, frame );
[191]463
464                        discard( sstream, "}" );
465                        next_line( sstream );
466                }
467
468                sstream >> command;
469        }
470
471
472        m_frame_duration = 1.0f / (float)m_frame_rate;
473        m_anim_duration = ( m_frame_duration * (float)m_num_frames );
474
475        return true;
476}
477
[241]478
[261]479void nv::md5_animation::update_skeleton( transform* skeleton, float anim_time ) const
[191]480{
[241]481        anim_time = glm::clamp( anim_time, 0.0f, m_anim_duration );
482        float frame_num = anim_time * (float)m_frame_rate;
[200]483        size_t frame0 = (size_t)floorf( frame_num );
484        size_t frame1 = (size_t)ceilf( frame_num );
[191]485        frame0 = frame0 % m_num_frames;
486        frame1 = frame1 % m_num_frames;
487
[241]488        float interpolation = fmodf( anim_time, m_frame_duration ) / m_frame_duration;
[191]489
[241]490        for ( size_t i = 0; i < m_num_joints; ++i )
491        {
492                const transform_vector& keys = m_joints[i].keys;
493                skeleton[i] = interpolate( keys.get(frame0), keys.get(frame1), interpolation );
494        }
[191]495}
496
[241]497void md5_animation::build_frame_skeleton( const std::vector<md5_joint_info>& joint_infos, const std::vector<transform>& base_frames, const std::vector<float>& frame_data )
[191]498{
[241]499        size_t index = m_joints[0].keys.size();
[191]500        for ( unsigned int i = 0; i < joint_infos.size(); ++i )
501        {
502                unsigned int j = 0;
503
504                const md5_joint_info& jinfo = joint_infos[i];
505
506
[241]507                int parent_id = jinfo.parent_id;
[191]508
[241]509                vec3 pos    = base_frames[i].get_position();
510                quat orient = base_frames[i].get_orientation();
511                if ( jinfo.flags & 1 )  pos.x    = frame_data[ jinfo.start_index + j++ ];
512                if ( jinfo.flags & 2 )  pos.y    = frame_data[ jinfo.start_index + j++ ];
513                if ( jinfo.flags & 4 )  pos.z    = frame_data[ jinfo.start_index + j++ ];
514                if ( jinfo.flags & 8 )  orient.x = frame_data[ jinfo.start_index + j++ ];
515                if ( jinfo.flags & 16 ) orient.y = frame_data[ jinfo.start_index + j++ ];
516                if ( jinfo.flags & 32 ) orient.z = frame_data[ jinfo.start_index + j++ ];
517                unit_quat_w( orient );
[191]518
[241]519                if ( parent_id >= 0 ) // Has a parent joint
[191]520                {
[241]521                        const transform_vector& ptv = m_joints[ size_t( parent_id ) ].keys;
522                        transform ptr;
523                        if ( ptv.size() > index ) ptr = ptv.get( index );
524                        glm::vec3 rot_pos = ptr.get_orientation() * pos;
[191]525
[241]526                        pos    = ptr.get_position() + rot_pos;
527                        orient = ptr.get_orientation() * orient;
[191]528
[241]529                        orient = glm::normalize( orient );
[191]530                }
531
[241]532                m_joints[i].keys.insert( transform( pos, orient ) );
[191]533        }
[241]534}
[191]535
[241]536mesh_data* nv::md5_loader::release_mesh_data( size_t index )
537{
538        mesh_data* result = m_meshes[ index ];
539        m_meshes[ index ] = nullptr;
540        return result;
[191]541}
542
[241]543md5_mesh_instance* nv::md5_mesh_data::spawn() const
[191]544{
[241]545        return new md5_mesh_instance( this );
[191]546}
547
[241]548nv::md5_loader::~md5_loader()
[191]549{
[241]550        for ( auto m : m_meshes ) { if (m) delete m; }
[191]551}
552
[241]553nv::md5_mesh_instance::md5_mesh_instance( const md5_mesh_data* a_data )
[261]554        : m_data( a_data ), m_indices( 0 )
[191]555{
[241]556        m_indices = m_data->get_count();
[261]557        m_pntdata.assign( m_data->m_pntdata, m_data->m_vtx_data.size() );
[239]558}
559
[261]560void nv::md5_mesh_instance::apply( const transform* skeleton )
[239]561{
[258]562        NV_PROFILE("md5::apply");
[261]563        size_t skeleton_size = m_data->m_bone_offset.size();
564        size_t vertex_count  = m_pntdata.size();
565        m_pos_offset.resize( skeleton_size );
566        for ( unsigned int i = 0; i < skeleton_size; ++i )
[259]567        {
568                m_pos_offset[i] = skeleton[i] * m_data->m_bone_offset[i];
569        }
570
[261]571        std::fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
572        for ( unsigned int i = 0; i < vertex_count; ++i )
[191]573        {
[241]574                const md5_vtx_data& vert = m_data->m_vtx_data[i];
[239]575                md5_vtx_pnt& result = m_pntdata[i];
[191]576
[259]577                for ( size_t j = 0; j < 4; ++j )
[191]578                {
[259]579                        int   index  = vert.boneindex[j];
580                        float weight = vert.boneweight[j];
[261]581                        const quat& orient      = skeleton[index].get_orientation();
582                        const transform& offset = m_pos_offset[index];
[259]583                        result.position += offset.transformed( vert.position )        * weight;
[261]584                        result.normal   += ( orient * vert.normal  ) * weight;
585                        result.tangent  += ( orient * vert.tangent ) * weight;
[191]586                }
587        }
588}
Note: See TracBrowser for help on using the repository browser.