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

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