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

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