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

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