source: trunk/src/formats/nmd_loader.cc @ 486

Last change on this file since 486 was 486, checked in by epyon, 9 years ago
  • mass update once again...
File size: 9.9 KB
RevLine 
[395]1// Copyright (C) 2014-2015 ChaosForge Ltd
[283]2// http://chaosforge.org/
3//
[395]4// This file is part of Nova libraries.
5// For conditions of distribution and use, see copying.txt file in root folder.
[283]6
7#include "nv/formats/nmd_loader.hh"
[368]8#include "nv/stl/string.hh"
[416]9#include "nv/interface/data_channel_access.hh"
[283]10
11using namespace nv;
12
13bool nv::nmd_loader::load( stream& source )
14{
15        // TODO: proper error handling
16        reset();
17        nmd_header root_header;
18        source.read( &root_header, sizeof( root_header ), 1 );
[420]19        skip_attributes( source, root_header.attributes );
[283]20        for ( uint32 i = 0; i < root_header.elements; ++i )
21        {
22                nmd_element_header element_header;
23                source.read( &element_header, sizeof( element_header ), 1 );
[420]24                skip_attributes( source, element_header.attributes );
[283]25                switch ( element_header.type )
26                {
[284]27                case nmd_type::MESH           : load_mesh( source, element_header ); break;
[485]28                case nmd_type::BONES          : load_bones( source, element_header ); break;
[423]29                case nmd_type::STRINGS        : load_strings( source ); break;
[485]30                case nmd_type::POSES          : load_poses( source, element_header ); break;
[283]31                default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
32                }
33        }
34        return true;
35}
36
[284]37bool nv::nmd_loader::load_mesh( stream& source, const nmd_element_header& e )
[283]38{
[424]39        data_channel_set* mesh = data_channel_set_creator::create_set( e.children );
[482]40        data_node_info info;
41        load_channel_set( source, mesh, info, e );
42        m_infos.push_back( info );
[417]43        m_meshes.push_back( mesh );
[283]44        return true;
45}
46
[482]47data_channel_set* nv::nmd_loader::release_mesh_data( size_t index, data_node_info& info )
[283]48{
[416]49        data_channel_set* result = m_meshes[ index ];
[482]50        info = m_infos[ index ];
[283]51        m_meshes[ index ] = nullptr;
52        return result;
53}
54
55void nv::nmd_loader::reset()
56{
57        for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
[482]58        if ( m_bone_data ) delete m_bone_data;
[485]59        if ( m_pose_data_set ) delete m_pose_data_set;
[283]60        m_meshes.clear();
[287]61
[482]62        m_bone_data = nullptr;
[283]63}
64
[420]65void nv::nmd_loader::skip_attributes( stream& source, uint32 count )
66{
67        if ( count == 0 ) return;
68        source.seek( count * sizeof( nmd_attribute ), origin::CUR );
69}
70
[283]71nv::nmd_loader::~nmd_loader()
72{
73        reset();
74}
75
[423]76bool nv::nmd_loader::load_strings( stream& source )
[283]77{
[425]78        if ( !m_strings ) return true;
[423]79        // TODO: load strings optionally
[425]80        string_table* strings = new string_table( source );
81        m_strings->insert( strings );
82        delete strings;
[283]83        return true;
84}
85
[485]86bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
[283]87{
[485]88        NV_ASSERT( m_bone_data == nullptr, "MULTIPLE NODE ENTRIES!" );
[287]89        nmd_animation_header animation_header;
90        source.read( &animation_header, sizeof( animation_header ), 1 );
[482]91        m_bone_data = new data_node_list( e.name );
[284]92        for ( uint32 i = 0; i < e.children; ++i )
[283]93        {
94                nmd_element_header element_header;
95                source.read( &element_header, sizeof( element_header ), 1 );
[420]96                skip_attributes( source, element_header.attributes );
[287]97                NV_ASSERT( element_header.type == nmd_type::NODE, "NODE expected!" );
[427]98                data_channel_set* set = data_channel_set_creator::create_set( element_header.children );
[482]99                data_node_info info;
100                load_channel_set( source, set, info, element_header );
101                m_bone_data->append( info );
[485]102                delete set;
[283]103        }
104        return true;
105}
106
[420]107bool nv::nmd_loader::load_channel( stream& source, data_channel_set* channel_set )
108{
109        data_channel_set_creator kaccess( channel_set );
110        nmd_channel_header cheader;
111        source.read( &cheader, sizeof( cheader ), 1 );
112        raw_data_channel_access channel( kaccess.add_channel( cheader.format, cheader.count ) );
113        source.read( channel.raw_data(), channel.element_size(), channel.size() );
114        return true;
115}
116
[482]117bool nv::nmd_loader::load_channel_set( stream& source, data_channel_set* channel_set, data_node_info& info, const nmd_element_header& e )
[420]118{
119        data_channel_set_creator kaccess( channel_set );
120        for ( uint32 c = 0; c < e.children; ++c )
121        {
122                load_channel( source, channel_set );
123        }
[482]124        info.name = e.name;
125        info.parent_id = e.parent_id;
126        info.transform = e.transform;
[420]127        return true;
128}
129
[485]130bool nv::nmd_loader::load_poses( stream& source, const nmd_element_header& e )
131{
132        if ( m_pose_data_set == nullptr )
133        {
134                NV_ASSERT_ALWAYS( m_bone_data, "POSES WITHOUT BONES!" );
135                m_pose_data_set = new pose_data_set;
136                m_pose_data_set->initialize( *m_bone_data );
137        }
138
139        uint32  count = e.children;
140        shash64 name  = e.name;
141
142        auto& set = m_pose_data_set->m_sets[ name ];
143        NV_ASSERT_ALWAYS( !set.name, "SET REWRITE!" );
144        set.name = name;
145        set.count = count;
146        set.start = m_pose_data_set->size();
147
148        for ( uint32 i = 0; i < count; ++i )
149        {
150                uint32 length = 0;
151                source.read( &length, sizeof( length ), 1 );
152                m_pose_data_set->m_data.push_back( skeleton_transforms() );
153                auto& data = m_pose_data_set->m_data.back().m_transforms;
154                data.resize( length );
155                source.read( &data[0], length * sizeof( transform ), 1 );
156        }
157        return true;
158}
159
[291]160mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data( size_t )
[283]161{
[485]162        return nullptr;
[283]163}
164
[482]165data_node_list* nv::nmd_loader::release_data_node_list( size_t )
166{
167        data_node_list* result = m_bone_data;
168        m_bone_data = nullptr;
169        return result;
170}
171
172bool nv::nmd_loader::is_animated( size_t /*= 0 */ )
173{
[485]174        return m_pose_data_set != nullptr;
[482]175}
176
[292]177// ----------------------------------------------------------------
178// nmd format dump
179// HACK : TEMPORARY - will go to it's own file, probably nmd_io
[420]180
[423]181void nv::nmd_dump_header( stream& stream_out, uint32 elements, uint64 name )
[292]182{
[423]183        nmd_header header;
184        header.id = four_cc<'n', 'm', 'f', '1'>::value;
185        header.elements = elements; // +1 string array
186        header.name = name;
187        header.version = 1;
188        header.attributes = 0;
189        stream_out.write( &header, sizeof( header ), 1 );
190}
191
[482]192void nv::nmd_dump_element( stream& stream_out, const data_channel_set& data, const data_node_info& info, nmd_type type )
[423]193{
194        uint32 size = 0;
[427]195        for ( auto& chan : data )
[423]196        {
197                size += sizeof( nmd_channel_header );
198                size += chan.raw_size();
199        }
200
201        nmd_element_header eheader;
[427]202        eheader.type       = type;
203        eheader.children   = static_cast<uint16>( data.size() );
[423]204        eheader.size       = size;
[482]205        eheader.name       = info.name;
206        eheader.transform  = info.transform;
207        eheader.parent_id  = info.parent_id;
[423]208        eheader.attributes = 0;
209        stream_out.write( &eheader, sizeof( eheader ), 1 );
[427]210        for ( auto& channel : data )
211        {
212                nmd_channel_header cheader;
213                cheader.format = channel.descriptor();
214                cheader.count = channel.size();
215                stream_out.write( &cheader, sizeof( cheader ), 1 );
216                stream_out.write( channel.raw_data(), channel.element_size(), channel.size() );
217        }
[423]218}
219
[482]220void nv::nmd_dump_bones( stream& stream_out, const data_node_list& nodes )
221{
222        uint32 total = sizeof( nmd_animation_header );
[427]223        for ( auto node : nodes )
[292]224        {
[482]225                total += sizeof( nmd_element_header );
[292]226        }
[482]227
228        nmd_element_header header;
[485]229        header.type = nmd_type::BONES;
[482]230        header.children = static_cast<uint16>( nodes.size() );
231        header.size = total;
232        header.name = nodes.get_name();
233        header.transform = mat4();
234        header.parent_id = -1;
235        header.attributes = 0;
236
237        stream_out.write( &header, sizeof( header ), 1 );
238
239        nmd_animation_header aheader;
240        aheader.frame_rate = 0;
241        aheader.frame_count = 0;
242        aheader.unused = false;
243        stream_out.write( &aheader, sizeof( aheader ), 1 );
244
245        for ( auto node : nodes )
246        {
247                nmd_element_header eheader;
248                eheader.type = nv::nmd_type::NODE;
249                eheader.children = 0;
250                eheader.size = 0;
251                eheader.name = node.name;
252                eheader.transform = node.transform;
253                eheader.parent_id = node.parent_id;
254                eheader.attributes = 0;
255                stream_out.write( &eheader, sizeof( eheader ), 1 );
256        }
[292]257}
258
[485]259void nv::nmd_dump_poses( stream& stream_out, const array_view< skeleton_transforms* > poses, shash64 name )
260{
261        nmd_element_header pheader;
262        pheader.type       = nv::nmd_type::POSES;
[486]263        pheader.children   = uint16( poses.size() );
[485]264        pheader.size       = sizeof( transform ) * poses.size() * ( poses.size() > 0 ? poses[0]->size() : 0 )
265                               + sizeof( uint32 ) * poses.size();
266        pheader.name       = name;
267        pheader.parent_id  = -1;
268        pheader.attributes = 0;
269        stream_out.write( &pheader, sizeof( pheader ), 1 );
270        for ( auto pose : poses )
271        {
272                uint32 count = pose->size();
273                stream_out.write( &count, sizeof( count ), 1 );
274                stream_out.write( pose->xforms(), sizeof( transform ) * count, 1 );
275        }
276}
277
278void nv::nmd_dump_pose( stream& stream_out, const skeleton_transforms& pose, shash64 name )
279{
280        nmd_element_header pheader;
281        pheader.type       = nv::nmd_type::POSES;
282        pheader.children   = 1;
283        pheader.size       = sizeof( transform ) * pose.size();
284        pheader.name       = name;
285        pheader.parent_id  = -1;
286        pheader.attributes = 0;
287        stream_out.write( &pheader, sizeof( pheader ), 1 );
288        uint32 count = pose.size();
289        stream_out.write( &count, sizeof( count ), 1 );
290        stream_out.write( pose.xforms(), sizeof( transform ) * count, 1 );
291}
292
[423]293void nv::nmd_dump_strings( stream& stream_out, const string_table& strings )
[292]294{
[423]295        nmd_element_header sheader;
296        sheader.type       = nv::nmd_type::STRINGS;
297        sheader.children   = 0;
298        sheader.size       = strings.dump_size();
[431]299        sheader.name       = shash64();
[423]300        sheader.parent_id  = -1;
301    sheader.attributes = 0;
302        stream_out.write( &sheader, sizeof( sheader ), 1 );
303        strings.dump( stream_out );
304}
305
[482]306void nv::nmd_dump( stream& stream_out, array_view< data_channel_set* > meshes, array_view< data_node_info > infos, const nv::data_node_list* nodes, const string_table* strings /*= nullptr*/, uint64 name /*= 0 */ )
307{
308        uint32 elements = ( strings ? 1 : 0 ) // +1 string array
309                + meshes.size() // meshes
310                + ( nodes && nodes->size() > 0 ? 1 : 0 ); // nodes
311        nmd_dump_header( stream_out, elements, name );
312
313        for ( uint32 i = 0; i < meshes.size(); ++i )
314        {
315                NV_ASSERT( meshes[i], "mesh is null!" );
316                nmd_dump_element( stream_out, *meshes[i], infos[i], nv::nmd_type::MESH );
317        }
318
319        if ( nodes && nodes->size() > 0 )
320        {
321                nmd_dump_bones( stream_out, *nodes );
322        }
323
324        if ( strings )
325        {
326                nmd_dump_strings( stream_out, *strings );
327        }
328}
Note: See TracBrowser for help on using the repository browser.