1 | // Copyright (C) 2014 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/nmd_loader.hh"
|
---|
8 | #include "nv/io/std_stream.hh"
|
---|
9 |
|
---|
10 | using namespace nv;
|
---|
11 |
|
---|
12 | bool nv::nmd_loader::load( stream& source )
|
---|
13 | {
|
---|
14 | // TODO: proper error handling
|
---|
15 | reset();
|
---|
16 | nmd_header root_header;
|
---|
17 | source.read( &root_header, sizeof( root_header ), 1 );
|
---|
18 | for ( uint32 i = 0; i < root_header.elements; ++i )
|
---|
19 | {
|
---|
20 | nmd_element_header element_header;
|
---|
21 | source.read( &element_header, sizeof( element_header ), 1 );
|
---|
22 | switch ( element_header.type )
|
---|
23 | {
|
---|
24 | case nmd_type::MESH : load_mesh( source, element_header ); break;
|
---|
25 | case nmd_type::ANIMATION : load_animation( source, element_header ); break;
|
---|
26 | case nmd_type::BONE_ARRAY : load_bones( source, element_header ); break;
|
---|
27 | case nmd_type::STRING_TABLE : load_strings( source ); break;
|
---|
28 | default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
|
---|
29 | }
|
---|
30 | }
|
---|
31 | return true;
|
---|
32 | }
|
---|
33 |
|
---|
34 | bool nv::nmd_loader::load_mesh( stream& source, const nmd_element_header& e )
|
---|
35 | {
|
---|
36 | mesh_data* mesh = new mesh_data();
|
---|
37 | for ( uint32 s = 0; s < e.children; ++s )
|
---|
38 | {
|
---|
39 | nmd_element_header element_header;
|
---|
40 | source.read( &element_header, sizeof( element_header ), 1 );
|
---|
41 | NV_ASSERT( element_header.type == nmd_type::STREAM, "STREAM expected!" );
|
---|
42 |
|
---|
43 | nmd_stream_header stream_header;
|
---|
44 | source.read( &stream_header, sizeof( stream_header ), 1 );
|
---|
45 | mesh_raw_channel* channel = mesh_raw_channel::create( stream_header.format, stream_header.count );
|
---|
46 | source.read( channel->data, stream_header.format.size, stream_header.count );
|
---|
47 | mesh->add_channel( channel );
|
---|
48 | }
|
---|
49 | m_meshes.push_back( mesh );
|
---|
50 | return true;
|
---|
51 | }
|
---|
52 |
|
---|
53 | mesh_data* nv::nmd_loader::release_mesh_data( size_t index )
|
---|
54 | {
|
---|
55 | mesh_data* result = m_meshes[ index ];
|
---|
56 | m_meshes[ index ] = nullptr;
|
---|
57 | return result;
|
---|
58 | }
|
---|
59 |
|
---|
60 | void nv::nmd_loader::reset()
|
---|
61 | {
|
---|
62 | for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
|
---|
63 | if ( m_animation ) delete m_animation;
|
---|
64 | if ( m_strings ) delete m_strings;
|
---|
65 | if ( m_bone_data ) delete m_bone_data;
|
---|
66 | m_meshes.clear();
|
---|
67 | m_animation = nullptr;
|
---|
68 | m_bone_data = nullptr;
|
---|
69 | m_strings = nullptr;
|
---|
70 | }
|
---|
71 |
|
---|
72 | nv::nmd_loader::~nmd_loader()
|
---|
73 | {
|
---|
74 | reset();
|
---|
75 | }
|
---|
76 |
|
---|
77 | bool nv::nmd_loader::load_strings( stream& source )
|
---|
78 | {
|
---|
79 | NV_ASSERT( m_strings == nullptr, "MULTIPLE STRING ENTRIES!" );
|
---|
80 | m_strings = new string_table( &source );
|
---|
81 | return true;
|
---|
82 | }
|
---|
83 |
|
---|
84 | bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
|
---|
85 | {
|
---|
86 | NV_ASSERT( m_bone_data == nullptr, "MULTIPLE BONE ENTRIES!" );
|
---|
87 | m_bone_data = new nmd_bone_data;
|
---|
88 | m_bone_data->bones = new nmd_bone[ e.children ];
|
---|
89 | m_bone_data->count = (uint16)e.children;
|
---|
90 | source.read( m_bone_data->bones, sizeof( nmd_bone ), e.children );
|
---|
91 | return true;
|
---|
92 | }
|
---|
93 |
|
---|
94 | nmd_animation* nv::nmd_loader::release_animation()
|
---|
95 | {
|
---|
96 | nmd_animation* result = m_animation;
|
---|
97 | m_animation = nullptr;
|
---|
98 | return result;
|
---|
99 | }
|
---|
100 |
|
---|
101 | nmd_bone_data* nv::nmd_loader::release_bone_data()
|
---|
102 | {
|
---|
103 | nmd_bone_data* result = m_bone_data;
|
---|
104 | m_bone_data = nullptr;
|
---|
105 | return result;
|
---|
106 | }
|
---|
107 |
|
---|
108 | string_table* nv::nmd_loader::release_string_table()
|
---|
109 | {
|
---|
110 | string_table* result = m_strings;
|
---|
111 | m_strings = nullptr;
|
---|
112 | return result;
|
---|
113 | }
|
---|
114 |
|
---|
115 |
|
---|
116 | bool nv::nmd_loader::load_animation( stream& source, const nmd_element_header& e )
|
---|
117 | {
|
---|
118 | NV_ASSERT( m_animation == nullptr, "MULTIPLE ANIMATION ENTRIES!" );
|
---|
119 | nmd_animation_header header;
|
---|
120 | source.read( &header, sizeof( header ), 1 );
|
---|
121 | m_animation = new nmd_animation;
|
---|
122 | m_animation->fps = header.fps;
|
---|
123 | m_animation->duration = header.duration;
|
---|
124 | m_animation->flat = header.flat;
|
---|
125 | m_animation->node_count = (uint16)e.children;
|
---|
126 | m_animation->nodes = new nmd_node[ e.children ];
|
---|
127 | for ( uint32 i = 0; i < e.children; ++i )
|
---|
128 | {
|
---|
129 | nmd_element_header element_header;
|
---|
130 | source.read( &element_header, sizeof( element_header ), 1 );
|
---|
131 | NV_ASSERT( element_header.type == nmd_type::ANIMATION_NODE, "ANIMATION_NODE expected!" );
|
---|
132 | m_animation->nodes[i].name = element_header.name;
|
---|
133 |
|
---|
134 | uint16 ch_count = element_header.children;
|
---|
135 |
|
---|
136 | nmd_animation_node_header node_header;
|
---|
137 | source.read( &node_header, sizeof( node_header ), 1 );
|
---|
138 | m_animation->nodes[i].parent_id = node_header.parent_id;
|
---|
139 | m_animation->nodes[i].transform = node_header.transform;
|
---|
140 | m_animation->nodes[i].data = nullptr;
|
---|
141 | if ( ch_count > 0 )
|
---|
142 | {
|
---|
143 | key_data* kdata = new key_data;
|
---|
144 | m_animation->nodes[i].data = kdata;
|
---|
145 | for ( uint32 c = 0; c < ch_count; ++c )
|
---|
146 | {
|
---|
147 | source.read( &element_header, sizeof( element_header ), 1 );
|
---|
148 | NV_ASSERT( element_header.type == nmd_type::ANIMATION_CHANNEL, "ANIMATION_CHANNEL expected!" );
|
---|
149 | nv::nmd_animation_channel_header cheader;
|
---|
150 | source.read( &cheader, sizeof( cheader ), 1 );
|
---|
151 | key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count );
|
---|
152 | source.read( channel->data, channel->desc.size, channel->count );
|
---|
153 | kdata->add_channel( channel );
|
---|
154 | }
|
---|
155 | }
|
---|
156 | }
|
---|
157 | return true;
|
---|
158 | }
|
---|
159 |
|
---|
160 |
|
---|
161 | // TEMPORARY
|
---|
162 | nv::nmd_temp_animation::nmd_temp_animation( nmd_loader* loader )
|
---|
163 | {
|
---|
164 | m_animation = loader->release_animation();
|
---|
165 | m_strings = loader->release_string_table();
|
---|
166 |
|
---|
167 | for ( uint32 n = 0; n < m_animation->node_count; ++n )
|
---|
168 | {
|
---|
169 | nmd_node& node = m_animation->nodes[n];
|
---|
170 | m_data.push_back( node.data );
|
---|
171 | node.data = nullptr;
|
---|
172 | }
|
---|
173 |
|
---|
174 | if ( !m_animation->flat )
|
---|
175 | {
|
---|
176 | m_children.resize( m_animation->node_count );
|
---|
177 | for ( nv::uint32 n = 0; n < m_animation->node_count; ++n )
|
---|
178 | {
|
---|
179 | const nmd_node& node = m_animation->nodes[n];
|
---|
180 | if ( node.parent_id != -1 )
|
---|
181 | {
|
---|
182 | m_children[ node.parent_id ].push_back( n );
|
---|
183 | }
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | m_bone_ids.resize( m_animation->node_count );
|
---|
188 | }
|
---|
189 |
|
---|
190 | nv::nmd_temp_animation::~nmd_temp_animation()
|
---|
191 | {
|
---|
192 | for ( auto node : m_data ) delete node;
|
---|
193 | delete m_animation;
|
---|
194 | delete m_strings;
|
---|
195 | }
|
---|
196 |
|
---|
197 | void nv::nmd_temp_animation::prepare( const nmd_temp_model* model )
|
---|
198 | {
|
---|
199 | m_offsets = model->m_bone_offsets.data();
|
---|
200 | for ( uint32 n = 0; n < m_animation->node_count; ++n )
|
---|
201 | {
|
---|
202 | const nmd_node& node = m_animation->nodes[n];
|
---|
203 | sint16 bone_id = -1;
|
---|
204 |
|
---|
205 | auto bi = model->m_bone_names.find( m_strings->get( node.name ) );
|
---|
206 | if ( bi != model->m_bone_names.end() )
|
---|
207 | {
|
---|
208 | bone_id = bi->second;
|
---|
209 | }
|
---|
210 | m_bone_ids[n] = bone_id;
|
---|
211 | }
|
---|
212 | }
|
---|
213 |
|
---|
214 | void nv::nmd_temp_animation::animate( mat4* data, uint32 time )
|
---|
215 | {
|
---|
216 | float tick_time = ( time / 1000.0f ) * m_animation->fps;
|
---|
217 | float anim_time = fmodf( tick_time, m_animation->duration );
|
---|
218 |
|
---|
219 | if ( !m_animation->flat )
|
---|
220 | {
|
---|
221 | animate_rec( data, anim_time, 0, mat4() );
|
---|
222 | return;
|
---|
223 | }
|
---|
224 |
|
---|
225 | for ( uint32 n = 0; n < m_animation->node_count; ++n )
|
---|
226 | if ( m_bone_ids[n] >= 0 )
|
---|
227 | {
|
---|
228 | const nmd_node* node = &m_animation->nodes[ n ];
|
---|
229 | nv::mat4 node_mat( node->transform );
|
---|
230 |
|
---|
231 | if ( m_data[n] )
|
---|
232 | {
|
---|
233 | node_mat = m_data[n]->get_matrix( anim_time );
|
---|
234 | }
|
---|
235 |
|
---|
236 | sint16 bone_id = m_bone_ids[n];
|
---|
237 | data[ bone_id ] = node_mat * m_offsets[ bone_id ];
|
---|
238 | }
|
---|
239 |
|
---|
240 | }
|
---|
241 |
|
---|
242 | void nv::nmd_temp_animation::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat )
|
---|
243 | {
|
---|
244 | // TODO: fix transforms, which are now embedded,
|
---|
245 | // see note in assimp_loader.cc:load_node
|
---|
246 | const nmd_node* node = &m_animation->nodes[ node_id ];
|
---|
247 | mat4 node_mat( node->transform );
|
---|
248 |
|
---|
249 | if ( m_data[ node_id ] )
|
---|
250 | {
|
---|
251 | node_mat = m_data[ node_id ]->get_matrix( time );
|
---|
252 | }
|
---|
253 |
|
---|
254 | mat4 global_mat = parent_mat * node_mat;
|
---|
255 |
|
---|
256 | sint16 bone_id = m_bone_ids[ node_id ];
|
---|
257 | if ( bone_id >= 0 )
|
---|
258 | {
|
---|
259 | data[ bone_id ] = global_mat * m_offsets[ bone_id ];
|
---|
260 | }
|
---|
261 |
|
---|
262 | for ( auto child : m_children[ node_id ] )
|
---|
263 | {
|
---|
264 | animate_rec( data, time, child, global_mat );
|
---|
265 | }
|
---|
266 | }
|
---|
267 |
|
---|
268 | nv::nmd_temp_model::nmd_temp_model( nmd_loader* loader )
|
---|
269 | {
|
---|
270 | for ( unsigned m = 0; m < loader->get_mesh_count(); ++m )
|
---|
271 | {
|
---|
272 | m_mesh_data.push_back(loader->release_mesh_data(m));
|
---|
273 | }
|
---|
274 | nmd_bone_data* bone_data = loader->release_bone_data();
|
---|
275 | string_table* strings = loader->release_string_table();
|
---|
276 |
|
---|
277 | for ( nv::uint16 bi = 0; bi < bone_data->count; ++bi )
|
---|
278 | {
|
---|
279 | m_bone_names[ strings->get( bone_data->bones[bi].name ) ] = bi;
|
---|
280 | m_bone_offsets.push_back( bone_data->bones[bi].offset );
|
---|
281 | }
|
---|
282 |
|
---|
283 | delete bone_data;
|
---|
284 | delete strings;
|
---|
285 | }
|
---|
286 |
|
---|
287 | nv::nmd_temp_model::~nmd_temp_model()
|
---|
288 | {
|
---|
289 | for ( unsigned m = 0; m < m_mesh_data.size(); ++m )
|
---|
290 | {
|
---|
291 | delete m_mesh_data[m];
|
---|
292 | }
|
---|
293 | }
|
---|