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

Last change on this file since 291 was 291, checked in by epyon, 11 years ago
  • assimp_loader now uses standard mesh_loader structures
  • assimp_loader now supports multiple animation tracks
  • mesh_loader now supports multiple mesh_nodes data sets
File size: 14.9 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"
12#include <cstring>
13
14using namespace nv;
15
16static void next_line( std::istream& stream )
17{
18        stream.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
19}
20
21static inline void discard( std::istream& stream, const std::string& token )
22{
[226]23//      stream.ignore( std::numeric_limits<std::streamsize>::max(), ' ' );
[190]24        std::string discarded;
25        stream >> discarded;
26        assert( discarded == token );
27}
28
29static void remove_quotes( std::string& str )
30{
31        size_t n;
32        while ( ( n = str.find('\"') ) != std::string::npos ) str.erase(n,1);
33}
34
[198]35static void unit_quat_w( glm::quat& quat )
[190]36{
37        float t = 1.0f - ( quat.x * quat.x ) - ( quat.y * quat.y ) - ( quat.z * quat.z );
38        quat.w = ( t < 0.0f ? 0.0f : -sqrtf(t) );
39}
40
41bool md5_loader::load( stream& source )
42{
[289]43        reset();
[190]44        std_stream sstream( &source );
45        std::string command;
[289]46        mesh_node_data* nodes = nullptr;
47        size_t num_joints = 0;
48
49        // MESH data
[261]50        dynamic_array< md5_weight > weights;
51        dynamic_array< md5_weight_info > weight_info;
[289]52        size_t num_meshes = 0;
[190]53
[289]54        // MESH data
55        std::vector<md5_joint_info> joint_infos;
56        std::vector<transform>      base_frames;
57        size_t num_animated_components = 0;
58        size_t frame_rate = 0;
59        size_t num_frames = 0;
60
[190]61        sstream >> command;
62        while ( !sstream.eof() )
63        {
64                if ( command == "MD5Version" )
65                {
66                        sstream >> m_md5_version;
67                        assert( m_md5_version == 10 );
68                }
69                else if ( command == "commandline" )
70                {
71                        next_line( sstream );
72                }
73                else if ( command == "numJoints" )
74                {
[289]75                        sstream >> num_joints;
76                        next_line( sstream );
[190]77                }
78                else if ( command == "numMeshes" )
79                {
[289]80                        assert( m_type == UNKNOWN );
81                        m_type = MESH;
82                        sstream >> num_meshes;
83                        m_meshes.resize( num_meshes );
84                        num_meshes = 0;
[190]85                }
[289]86                else if ( command == "numFrames" )
87                {
88                        assert( m_type == UNKNOWN || m_type == ANIMATION );
89                        m_type = ANIMATION;
90                        sstream >> num_frames;
91                        next_line( sstream );
92                }
93                else if ( command == "frameRate" )
94                {
95                        assert( m_type == UNKNOWN || m_type == ANIMATION );
96                        m_type = ANIMATION;
97                        sstream >> frame_rate;
98                        next_line( sstream );
99                }
100                else if ( command == "numAnimatedComponents" )
101                {
102                        assert( m_type == UNKNOWN || m_type == ANIMATION );
103                        m_type = ANIMATION;
104                        sstream >> num_animated_components;
105                        next_line( sstream );
106                }
[190]107                else if ( command == "joints" )
108                {
[289]109                        assert( m_type == MESH );
110                        assert( m_nodes == nullptr );
111                        nodes = new mesh_node_data[ num_joints ];
112                        m_nodes = new mesh_nodes_data( "md5_bones", num_joints, nodes );
[190]113                        discard( sstream, "{" );
[289]114                        for ( size_t i = 0; i < m_nodes->get_count(); ++i )
[190]115                        {
[289]116                                sstream >> nodes[i].name >> nodes[i].parent_id;
117                                vec3 pos;
118                                quat orient;
[190]119                                discard( sstream, "(" );
[289]120                                sstream >> pos.x >> pos.y >> pos.z;
[190]121                                discard( sstream, ")" );
122                                discard( sstream, "(" );
[289]123                                sstream >> orient.x >> orient.y >> orient.z;
124                                unit_quat_w( orient );
125                                remove_quotes( nodes[i].name );
126                                nodes[i].target_id       = -1;
127                                nodes[i].parent_id       = -1;
128                                nodes[i].transform       = transform( pos, orient ).inverse().extract();
129                                nodes[i].data            = nullptr;
[190]130                                next_line( sstream );
131                        }
132                        discard( sstream, "}" );
133                }
134                else if ( command == "mesh" )
135                {
[289]136                        assert( m_type == MESH );
[287]137                        mesh_data* mesh = new mesh_data("md5_mesh");
[239]138
[287]139                        int num_verts   = 0;
140                        int num_tris    = 0;
141                        int num_weights = 0;
[190]142
143                        discard( sstream, "{" );
144                        sstream >> command;
145                        while ( command != "}" )
146                        {
147                                if ( command == "shader" )
148                                {
[287]149                                        std::string shader;
150                                        sstream >> shader;
151                                        remove_quotes( shader );
[190]152                                        next_line( sstream );
153                                }
154                                else if ( command == "numverts")
155                                {
156                                        sstream >> num_verts;
[239]157
[287]158                                        md5_vtx_t* tdata = nullptr;
[239]159                                        {
160                                                mesh_raw_channel* ch_pnt = mesh_raw_channel::create<md5_vtx_pnt>( num_verts );
161                                                mesh_raw_channel* ch_t   = mesh_raw_channel::create<md5_vtx_t>( num_verts );
[287]162                                                mesh_raw_channel* ch_pntiw = mesh_raw_channel::create<md5_vtx_pntiw>( num_verts );
163                                                tdata = (md5_vtx_t*)ch_t->data;
[239]164                                                mesh->add_channel( ch_pnt );
165                                                mesh->add_channel( ch_t );
[287]166                                                // TODO: hack to prevent rendering
167                                                ch_pntiw->count = 0;
168                                                mesh->add_channel( ch_pntiw );
[239]169                                        }
[259]170                                        weight_info.resize( num_verts );
[239]171
[190]172                                        next_line( sstream );
[226]173                                        std::string line;
[190]174                                        for ( int i = 0; i < num_verts; ++i )
175                                        {
[239]176                                                size_t weight_count;
177                                                size_t start_weight;
178                                                vec2 texcoord;
[190]179
[226]180                                                std::getline( sstream, line );
[239]181                                                sscanf( line.c_str(), "%*s %*u ( %f %f ) %u %u", &(texcoord.x), &(texcoord.y), &(start_weight), &(weight_count) );
[259]182                                                weight_info[i].start_weight = start_weight;
183                                                weight_info[i].weight_count = weight_count;
[287]184                                                tdata[i].texcoord = texcoord;
[190]185                                        } 
186                                }
187                                else if ( command == "numtris" )
188                                {
189                                        sstream >> num_tris;
[239]190
[280]191                                        mesh_raw_channel* ch_i = mesh_raw_channel::create_index<uint32>( num_tris * 3 );
[239]192                                        uint32* vtx_i                = (uint32*)ch_i->data;
193                                        uint32 idx = 0;
[280]194                                        mesh->add_channel( ch_i );
[239]195
[190]196                                        next_line( sstream );
[226]197                                        std::string line;
[190]198                                        for ( int i = 0; i < num_tris; ++i )
199                                        {
[239]200                                                size_t ti0;
201                                                size_t ti1;
202                                                size_t ti2;
[190]203
[226]204                                                std::getline( sstream, line );
[239]205                                                sscanf( line.c_str(), "%*s %*u %u %u %u )", &(ti0), &(ti1), &(ti2));
[226]206
[239]207                                                vtx_i[idx++] = (uint32)ti0;
208                                                vtx_i[idx++] = (uint32)ti1;
209                                                vtx_i[idx++] = (uint32)ti2;
[190]210                                        }             
211                                }
212                                else if ( command == "numweights" )
213                                {
214                                        sstream >> num_weights;
[261]215                                        weights.resize( num_weights );
[190]216                                        next_line( sstream );
[226]217                                        std::string line;
[190]218                                        for ( int i = 0; i < num_weights; ++i )
219                                        {
220                                                md5_weight weight;
[226]221
222                                                std::getline( sstream, line );
223                                                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]224                                                weights[i] = weight;
[190]225                                        }
226                                }
227                                else
228                                {
229                                        next_line( sstream );
230                                }
231
232                                sstream >> command;
233                        }
234
[289]235                        prepare_mesh( nodes, weight_info.size(), mesh, weights.data(), weight_info.data() );
[190]236
[289]237                        m_meshes[ num_meshes ] = mesh;
238                        num_meshes++;
239                } // mesh
240                else if ( command == "hierarchy" )
241                {
242                        assert( m_type == ANIMATION );
243                        assert( nodes == nullptr );
244                        nodes = new mesh_node_data[ num_joints ];
245                        m_nodes = new mesh_nodes_data( "md5_animation", num_joints, nodes, (nv::uint16)frame_rate, (float)num_frames, true );
246                        joint_infos.resize( num_joints );
247
248                        discard( sstream, "{" );
249                        for ( size_t i = 0; i < m_nodes->get_count(); ++i )
250                        {
251                                std::string    name;
252                                sstream >> nodes[i].name >> nodes[i].parent_id >> joint_infos[i].flags >> joint_infos[i].start_index;
253                                remove_quotes( name );
254                                nodes[i].transform = mat4();
255                                nodes[i].target_id = -1;
256                                nodes[i].data      = new key_data;
257                                nodes[i].data->add_channel( key_raw_channel::create< md5_key_t >( num_frames ) );
258                                next_line( sstream );
259                        }
260                        discard( sstream, "}" );
[190]261                }
[289]262                else if ( command == "bounds" )
263                {
264                        assert( m_type == ANIMATION );
265                        discard( sstream, "{" );
266                        next_line( sstream );
267                        for ( size_t i = 0; i < num_frames; ++i )
268                        {
269                                //                              vec3 min;
270                                //                              vec3 max;
271                                //                              discard( sstream, "(" );
272                                //                              sstream >> min.x >> min.y >> min.z;
273                                //                              discard( sstream, ")" );
274                                //                              discard( sstream, "(" );
275                                //                              sstream >> max.x >> max.y >> max.z;
276                                //                              m_bounds.push_back( bound );
277                                next_line( sstream );
278                        }
279
280                        discard( sstream, "}" );
281                        next_line( sstream );
282                }
283                else if ( command == "baseframe" )
284                {
285                        assert( m_type == ANIMATION );
286                        discard( sstream, "{" );
287                        next_line( sstream );
288
289                        for ( size_t i = 0; i < m_nodes->get_count(); ++i )
290                        {
291                                transform base_frame;
292                                vec3 pos;
293                                quat orient;
294                                discard( sstream, "(" );
295                                sstream >> pos.x >> pos.y >> pos.z;
296                                discard( sstream, ")" );
297                                discard( sstream, "(" );
298                                sstream >> orient.x >> orient.y >> orient.z;
299                                next_line( sstream );
300
301                                base_frames.emplace_back( pos, orient );
302                        }
303                        discard( sstream, "}" );
304                        next_line( sstream );
305                }
306                else if ( command == "frame" )
307                {
308                        std::vector<float> frame;
309                        uint32 frame_id;
310                        sstream >> frame_id;
311                        discard( sstream, "{" );
312                        next_line( sstream );
313
314                        frame.reserve( num_animated_components );
315                        char buf[50];
316                        for ( size_t i = 0; i < num_animated_components; ++i )
317                        {
318                                sstream >> buf;
319                                frame.push_back((float)atof(buf));
320                        }
321
322                        build_frame_skeleton( nodes, frame_id, joint_infos, base_frames, frame );
323
324                        discard( sstream, "}" );
325                        next_line( sstream );
326                }
327
[190]328                sstream >> command;
329        }
330
331        return true;
332}
333
[289]334bool md5_loader::prepare_mesh( mesh_node_data* nodes, uint32 vtx_count, mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info )
[190]335{
[289]336        assert( m_type == MESH );
[287]337        md5_vtx_pnt* vtcs = (md5_vtx_pnt*)mdata->get_channel< md5_vtx_pnt >()->data;
338        md5_vtx_pntiw* vtx_data = (md5_vtx_pntiw*)mdata->get_channel< md5_vtx_pntiw >()->data;
[190]339
[239]340        for ( uint32 i = 0; i < vtx_count; ++i )
[190]341        {
[259]342                size_t start_weight = weight_info[i].start_weight;
343                size_t weight_count = weight_info[i].weight_count;
[287]344                md5_vtx_pntiw& vdata = vtx_data[i];
[239]345                md5_vtx_pnt& vtc = vtcs[i];
[190]346
[239]347                vtc.position = glm::vec3(0);
348                vtc.normal   = glm::vec3(0);
349                vtc.tangent  = glm::vec3(0);
[190]350
[261]351                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]352
[259]353                if ( weight_count > 4 )
[258]354                {
355                        float sum = 0.0f;
356                        for ( size_t j = 0; j < 4; ++j )
357                        {
[259]358                                sum += weights[start_weight + j].bias;
[258]359                        }
360                        float ratio = 1.0f / sum;
361                        for ( size_t j = 0; j < 4; ++j )
362                        {
[259]363                                weights[start_weight + j].bias = ratio * weights[start_weight + j].bias;
[258]364                        }
[259]365                        weight_count = 4;
[258]366                }
367
[259]368                for ( size_t j = 0; j < 4; ++j )
[190]369                {
[259]370                        if ( j < weight_count )
371                        {
372                                vdata.boneindex[j]  = weights[start_weight + j].joint_id;
373                                vdata.boneweight[j] = weights[start_weight + j].bias;
374                        }
375                        else
376                        {
377                                vdata.boneindex[j]  = 0;
378                                vdata.boneweight[j] = 0.0f;
379                        }
380                }
[190]381
[259]382                for ( size_t j = 0; j < 4; ++j )
383                {
384                        if ( j < weight_count )
385                        {
[289]386                                md5_weight& weight           = weights[start_weight + j];
387                                const mesh_node_data&  joint = nodes[weight.joint_id];
388                                const transform tr = transform( joint.transform ).inverse();
389                                glm::vec3 rot_pos = tr.get_orientation() * weight.pos;
[190]390
[289]391                                vtc.position += ( tr.get_position() + rot_pos ) * weight.bias;
[259]392                        }
[190]393                }
394        }
395
[287]396        const uint32*    idata = (uint32*)mdata->get_index_channel()->data;
397        const md5_vtx_t* tdata = mdata->get_channel_data<md5_vtx_t>();
398
[239]399        // Prepare normals
400        uint32 tri_count = mdata->get_count() / 3;
401        for ( unsigned int i = 0; i < tri_count; ++i )
[190]402        {
[287]403                uint32 ti0 = idata[ i * 3 ];
404                uint32 ti1 = idata[ i * 3 + 1 ];
405                uint32 ti2 = idata[ i * 3 + 2 ];
[239]406 
407                glm::vec3 v1 = vtcs[ ti0 ].position;
408                glm::vec3 v2 = vtcs[ ti1 ].position;
409                glm::vec3 v3 = vtcs[ ti2 ].position;
[190]410                glm::vec3 xyz1 = v3 - v1;
411                glm::vec3 xyz2 = v2 - v1;
412
413                glm::vec3 normal = glm::cross( xyz1, xyz2 );
414
[239]415                vtcs[ ti0 ].normal += normal;
416                vtcs[ ti1 ].normal += normal;
417                vtcs[ ti2 ].normal += normal;
[190]418
[287]419                const vec2& w1 = tdata[ ti0 ].texcoord;
420                const vec2& w2 = tdata[ ti1 ].texcoord;
421                const vec2& w3 = tdata[ ti2 ].texcoord;
[190]422
423                vec2 st1 = w3 - w1;
424                vec2 st2 = w2 - w1;
425
426                float coef = 1.0f / (st1.x * st2.y - st2.x * st1.y);
427
428                vec3 tangent = (( xyz1 * st2.y ) - ( xyz2 * st1.y )) * coef;
429
[239]430                vtcs[ ti0 ].tangent += tangent;
431                vtcs[ ti1 ].tangent += tangent;
432                vtcs[ ti2 ].tangent += tangent;
[190]433        }
434
[239]435        for ( size_t i = 0; i < vtx_count; ++i )
[190]436        {
[287]437                md5_vtx_pntiw& vdata = vtx_data[i];
[190]438
[239]439                glm::vec3 normal  = glm::normalize( vtcs[i].normal );
440                glm::vec3 tangent = glm::normalize( vtcs[i].tangent );
441                vtcs[i].normal   = normal;
442                vtcs[i].tangent  = tangent;
[190]443
[259]444                vdata.position = vtcs[i].position;
445                vdata.normal   = glm::vec3(0);
446                vdata.tangent  = glm::vec3(0);
[239]447 
[259]448                for ( size_t j = 0; j < 4; ++j )
[239]449                {
[289]450                        const mesh_node_data&  joint = nodes[vdata.boneindex[j]];
451                        const transform tr = transform( joint.transform ).inverse();
452                        vdata.normal  += ( normal  * tr.get_orientation() ) * vdata.boneweight[j];
453                        vdata.tangent += ( tangent * tr.get_orientation() ) * vdata.boneweight[j];
[239]454                }
[190]455        }
456
457        return true;
458}
459
[289]460void md5_loader::build_frame_skeleton( mesh_node_data* nodes, uint32 index, const std::vector<md5_joint_info>& joint_infos, const std::vector<transform>& base_frames, const std::vector<float>& frame_data )
[191]461{
[289]462        assert( m_type == ANIMATION );
[191]463        for ( unsigned int i = 0; i < joint_infos.size(); ++i )
464        {
465                unsigned int j = 0;
466
467                const md5_joint_info& jinfo = joint_infos[i];
[289]468                mesh_node_data& joint = nodes[i];
469                int parent_id         = joint.parent_id;
[191]470
[241]471                vec3 pos    = base_frames[i].get_position();
472                quat orient = base_frames[i].get_orientation();
473                if ( jinfo.flags & 1 )  pos.x    = frame_data[ jinfo.start_index + j++ ];
474                if ( jinfo.flags & 2 )  pos.y    = frame_data[ jinfo.start_index + j++ ];
475                if ( jinfo.flags & 4 )  pos.z    = frame_data[ jinfo.start_index + j++ ];
476                if ( jinfo.flags & 8 )  orient.x = frame_data[ jinfo.start_index + j++ ];
477                if ( jinfo.flags & 16 ) orient.y = frame_data[ jinfo.start_index + j++ ];
478                if ( jinfo.flags & 32 ) orient.z = frame_data[ jinfo.start_index + j++ ];
479                unit_quat_w( orient );
[191]480
[241]481                if ( parent_id >= 0 ) // Has a parent joint
[191]482                {
[289]483                        const mesh_node_data& pjoint = nodes[parent_id];
484                        const transform* ptv = (const transform*)pjoint.data->get_channel(0)->data;
[241]485                        transform ptr;
[289]486                        if ( pjoint.data->get_channel(0)->count > index ) ptr = ptv[ index ];
[241]487                        glm::vec3 rot_pos = ptr.get_orientation() * pos;
[191]488
[241]489                        pos    = ptr.get_position() + rot_pos;
490                        orient = ptr.get_orientation() * orient;
[191]491
[241]492                        orient = glm::normalize( orient );
[191]493                }
494
[289]495                ((transform*)joint.data->get_channel(0)->data)[index] = transform( pos, orient );
[191]496        }
[241]497}
[191]498
[241]499mesh_data* nv::md5_loader::release_mesh_data( size_t index )
500{
501        mesh_data* result = m_meshes[ index ];
502        m_meshes[ index ] = nullptr;
503        return result;
[191]504}
505
[291]506mesh_nodes_data* nv::md5_loader::release_mesh_nodes_data( size_t )
[191]507{
[289]508        mesh_nodes_data* nodes = m_nodes;
509        m_nodes = nullptr;
510        return nodes;
[191]511}
512
[287]513mesh_data_pack* nv::md5_loader::release_mesh_data_pack()
[191]514{
[287]515        uint32 size = m_meshes.size();
516        mesh_data* meshes = new mesh_data[ size ];
517        for ( uint32 i = 0; i < size; ++i )
518        {
519                m_meshes[i]->move_to( meshes[i] );
520                delete m_meshes[i];
521                m_meshes[i] = nullptr;
522        }
523        return new mesh_data_pack( size, meshes, release_mesh_nodes_data() );
[191]524}
525
[287]526
527nv::md5_loader::~md5_loader()
[191]528{
[289]529        reset();
530}
531
532void nv::md5_loader::reset()
533{
534        if ( m_nodes ) delete m_nodes;
[287]535        for ( auto m : m_meshes ) { if (m) delete m; }
[289]536        m_meshes.resize(0);
537        m_nodes = nullptr;
[239]538}
539
Note: See TracBrowser for help on using the repository browser.