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

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