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
Line 
1// Copyright (C) 2014-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/nmd_loader.hh"
8#include "nv/stl/string.hh"
9#include "nv/interface/data_channel_access.hh"
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 );
19        skip_attributes( source, root_header.attributes );
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 );
24                skip_attributes( source, element_header.attributes );
25                switch ( element_header.type )
26                {
27                case nmd_type::MESH           : load_mesh( source, element_header ); break;
28                case nmd_type::BONES          : load_bones( source, element_header ); break;
29                case nmd_type::STRINGS        : load_strings( source ); break;
30                case nmd_type::POSES          : load_poses( source, element_header ); break;
31                default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
32                }
33        }
34        return true;
35}
36
37bool nv::nmd_loader::load_mesh( stream& source, const nmd_element_header& e )
38{
39        data_channel_set* mesh = data_channel_set_creator::create_set( e.children );
40        data_node_info info;
41        load_channel_set( source, mesh, info, e );
42        m_infos.push_back( info );
43        m_meshes.push_back( mesh );
44        return true;
45}
46
47data_channel_set* nv::nmd_loader::release_mesh_data( size_t index, data_node_info& info )
48{
49        data_channel_set* result = m_meshes[ index ];
50        info = m_infos[ index ];
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;
58        if ( m_bone_data ) delete m_bone_data;
59        if ( m_pose_data_set ) delete m_pose_data_set;
60        m_meshes.clear();
61
62        m_bone_data = nullptr;
63}
64
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
71nv::nmd_loader::~nmd_loader()
72{
73        reset();
74}
75
76bool nv::nmd_loader::load_strings( stream& source )
77{
78        if ( !m_strings ) return true;
79        // TODO: load strings optionally
80        string_table* strings = new string_table( source );
81        m_strings->insert( strings );
82        delete strings;
83        return true;
84}
85
86bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
87{
88        NV_ASSERT( m_bone_data == nullptr, "MULTIPLE NODE ENTRIES!" );
89        nmd_animation_header animation_header;
90        source.read( &animation_header, sizeof( animation_header ), 1 );
91        m_bone_data = new data_node_list( e.name );
92        for ( uint32 i = 0; i < e.children; ++i )
93        {
94                nmd_element_header element_header;
95                source.read( &element_header, sizeof( element_header ), 1 );
96                skip_attributes( source, element_header.attributes );
97                NV_ASSERT( element_header.type == nmd_type::NODE, "NODE expected!" );
98                data_channel_set* set = data_channel_set_creator::create_set( element_header.children );
99                data_node_info info;
100                load_channel_set( source, set, info, element_header );
101                m_bone_data->append( info );
102                delete set;
103        }
104        return true;
105}
106
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
117bool nv::nmd_loader::load_channel_set( stream& source, data_channel_set* channel_set, data_node_info& info, const nmd_element_header& e )
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        }
124        info.name = e.name;
125        info.parent_id = e.parent_id;
126        info.transform = e.transform;
127        return true;
128}
129
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
160mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data( size_t )
161{
162        return nullptr;
163}
164
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{
174        return m_pose_data_set != nullptr;
175}
176
177// ----------------------------------------------------------------
178// nmd format dump
179// HACK : TEMPORARY - will go to it's own file, probably nmd_io
180
181void nv::nmd_dump_header( stream& stream_out, uint32 elements, uint64 name )
182{
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
192void nv::nmd_dump_element( stream& stream_out, const data_channel_set& data, const data_node_info& info, nmd_type type )
193{
194        uint32 size = 0;
195        for ( auto& chan : data )
196        {
197                size += sizeof( nmd_channel_header );
198                size += chan.raw_size();
199        }
200
201        nmd_element_header eheader;
202        eheader.type       = type;
203        eheader.children   = static_cast<uint16>( data.size() );
204        eheader.size       = size;
205        eheader.name       = info.name;
206        eheader.transform  = info.transform;
207        eheader.parent_id  = info.parent_id;
208        eheader.attributes = 0;
209        stream_out.write( &eheader, sizeof( eheader ), 1 );
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        }
218}
219
220void nv::nmd_dump_bones( stream& stream_out, const data_node_list& nodes )
221{
222        uint32 total = sizeof( nmd_animation_header );
223        for ( auto node : nodes )
224        {
225                total += sizeof( nmd_element_header );
226        }
227
228        nmd_element_header header;
229        header.type = nmd_type::BONES;
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        }
257}
258
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;
263        pheader.children   = uint16( poses.size() );
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
293void nv::nmd_dump_strings( stream& stream_out, const string_table& strings )
294{
295        nmd_element_header sheader;
296        sheader.type       = nv::nmd_type::STRINGS;
297        sheader.children   = 0;
298        sheader.size       = strings.dump_size();
299        sheader.name       = shash64();
300        sheader.parent_id  = -1;
301    sheader.attributes = 0;
302        stream_out.write( &sheader, sizeof( sheader ), 1 );
303        strings.dump( stream_out );
304}
305
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.