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

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