Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 283)
+++ trunk/src/formats/nmd_loader.cc	(revision 283)
@@ -0,0 +1,303 @@
+// Copyright (C) 2014 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/formats/nmd_loader.hh"
+#include "nv/io/std_stream.hh"
+
+using namespace nv;
+
+bool nv::nmd_loader::load( stream& source )
+{
+	// TODO: proper error handling
+	reset();
+	nmd_header root_header;
+	source.read( &root_header, sizeof( root_header ), 1 );
+	for ( uint32 i = 0; i < root_header.elements; ++i )
+	{
+		nmd_element_header element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		switch ( element_header.type )
+		{
+		case nmd_type::MESH           : load_mesh( source, element_header.children ); break;
+		case nmd_type::ANIMATION      : load_animation( source, element_header.children ); break;
+		case nmd_type::BONE_ARRAY     : load_bones( source, element_header.children ); break;
+		case nmd_type::STRING_TABLE   : load_strings( source ); break;
+		default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
+		}
+	}
+	return true;
+}
+
+bool nv::nmd_loader::load_mesh( stream& source, uint32 children )
+{
+	mesh_data* mesh = new mesh_data();
+	for ( uint32 s = 0; s < children; ++s )
+	{
+		nmd_element_header element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		NV_ASSERT( element_header.type == nmd_type::STREAM, "STREAM expected!" );
+
+		nmd_stream_header stream_header;
+		source.read( &stream_header, sizeof( stream_header ), 1 );
+		mesh_raw_channel* channel = mesh_raw_channel::create( stream_header.format, stream_header.count );
+		source.read( channel->data, stream_header.format.size, stream_header.count );
+		mesh->add_channel( channel );
+	}
+	m_meshes.push_back( mesh );
+	return true;
+}
+
+mesh_data* nv::nmd_loader::release_mesh_data( size_t index )
+{
+	mesh_data* result = m_meshes[ index ];
+	m_meshes[ index ] = nullptr;
+	return result;
+}
+
+void nv::nmd_loader::reset()
+{
+	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
+	if ( m_animation ) delete m_animation;
+	if ( m_strings )   delete m_strings;
+	if ( m_bone_data ) delete m_bone_data;
+	m_meshes.clear();
+	m_animation = nullptr;
+	m_bone_data = nullptr;
+	m_strings   = nullptr;
+}
+
+nv::nmd_loader::~nmd_loader()
+{
+	reset();
+}
+
+bool nv::nmd_loader::load_strings( stream& source )
+{
+	NV_ASSERT( m_strings == nullptr, "MULTIPLE STRING ENTRIES!" );
+	m_strings = new string_table( &source );
+	return true;
+}
+
+bool nv::nmd_loader::load_bones( stream& source, uint32 children )
+{
+	NV_ASSERT( m_bone_data == nullptr, "MULTIPLE BONE ENTRIES!" );
+	m_bone_data = new nmd_bone_data;
+	m_bone_data->bones = new nmd_bone[ children ];
+	m_bone_data->count = (uint16)children;
+	source.read( m_bone_data->bones, sizeof( nmd_bone ), children );
+	return true;
+}
+
+nmd_animation* nv::nmd_loader::release_animation()
+{
+	nmd_animation* result = m_animation;
+	m_animation = nullptr;
+	return result;
+}
+
+nmd_bone_data* nv::nmd_loader::release_bone_data()
+{
+	nmd_bone_data* result = m_bone_data;
+	m_bone_data = nullptr;
+	return result;
+}
+
+string_table* nv::nmd_loader::release_string_table()
+{
+	string_table* result = m_strings;
+	m_strings = nullptr;
+	return result;
+}
+
+
+bool nv::nmd_loader::load_animation( stream& source, uint32 children )
+{
+	NV_ASSERT( m_animation == nullptr, "MULTIPLE ANIMATION ENTRIES!" );
+	nmd_animation_header header;
+	source.read( &header, sizeof( header ), 1 );
+	m_animation = new nmd_animation;
+	m_animation->fps        = header.fps;
+	m_animation->duration   = header.duration;
+	m_animation->flat       = header.flat;
+	m_animation->node_count = (uint16)children;
+	m_animation->nodes      = new nmd_node[ children ];
+	for ( uint32 i = 0; i < children; ++i )
+	{
+		nmd_element_header element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		NV_ASSERT( element_header.type == nmd_type::ANIMATION_NODE, "ANIMATION_NODE expected!" );
+
+		uint16 ch_count = element_header.children;
+
+		nmd_animation_node_header node_header;
+		source.read( &node_header, sizeof( node_header ), 1 );
+		m_animation->nodes[i].name          = node_header.name;
+		m_animation->nodes[i].parent_id     = node_header.parent_id;
+		m_animation->nodes[i].transform     = node_header.transform;
+		m_animation->nodes[i].channel_count = ch_count;
+		m_animation->nodes[i].channels      = nullptr;
+		if ( ch_count > 0 )
+		{
+			m_animation->nodes[i].channels      = new key_raw_channel* [ch_count];
+			for ( uint32 c = 0; c < ch_count; ++c )
+			{
+				source.read( &element_header, sizeof( element_header ), 1 );
+				NV_ASSERT( element_header.type == nmd_type::ANIMATION_CHANNEL, "ANIMATION_CHANNEL expected!" );
+				nv::nmd_animation_channel_header cheader;
+				source.read( &cheader, sizeof( cheader ), 1 );
+				key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count );
+				source.read( channel->data, channel->desc.size, channel->count );
+				m_animation->nodes[i].channels[c] = channel;
+			}
+		}
+	}
+	return true;
+}
+
+
+// TEMPORARY
+nv::nmd_temp_animation::nmd_temp_animation( nmd_loader* loader )
+{
+	m_animation = loader->release_animation();
+	m_strings   = loader->release_string_table();
+
+	for ( uint32 n = 0; n < m_animation->node_count; ++n )
+	{
+		nmd_node& node = m_animation->nodes[n];
+		key_animation_data* keys = nullptr;
+		if ( node.channel_count > 1 )
+		{
+			keys = new nv::key_vectors_prs( node.channels[0], node.channels[1], node.channels[2] );
+		}
+		else if ( node.channel_count == 1 )
+		{
+			keys      =  new nv::transform_vector( node.channels[0] );
+			node.channels[0] = nullptr;
+			node.channel_count = 0;
+		}
+		m_data.push_back( keys );
+	}
+
+	if ( !m_animation->flat )
+	{
+		m_children.resize( m_animation->node_count );
+		for ( nv::uint32 n = 0; n < m_animation->node_count; ++n )
+		{
+			const nmd_node& node = m_animation->nodes[n];
+			if ( node.parent_id != -1 )
+			{
+				m_children[ node.parent_id ].push_back( n );
+			}
+		}
+	}
+
+	m_bone_ids.resize( m_animation->node_count );
+}
+
+nv::nmd_temp_animation::~nmd_temp_animation()
+{
+	for ( auto node : m_data ) delete node;
+	delete m_animation;
+	delete m_strings;
+}
+
+void nv::nmd_temp_animation::prepare( const nmd_temp_model* model )
+{
+	m_offsets = model->m_bone_offsets.data();
+	for ( uint32 n = 0; n < m_animation->node_count; ++n )
+	{
+		const nmd_node& node = m_animation->nodes[n];
+		sint16 bone_id = -1;
+
+		auto bi = model->m_bone_names.find( m_strings->get( node.name ) );
+		if ( bi != model->m_bone_names.end() )
+		{
+			bone_id = bi->second;
+		}
+		m_bone_ids[n] = bone_id;
+	}
+}
+
+void nv::nmd_temp_animation::animate( mat4* data, uint32 time )
+{
+	float tick_time = ( time / 1000.0f ) * m_animation->fps;
+	float anim_time = fmodf( tick_time, m_animation->duration );
+
+	if ( !m_animation->flat )
+	{
+		animate_rec( data, anim_time, 0, mat4() );
+		return;
+	}
+
+	for ( uint32 n = 0; n < m_animation->node_count; ++n )
+		if ( m_bone_ids[n] >= 0 )
+		{
+			const nmd_node* node = &m_animation->nodes[ n ];
+			nv::mat4 node_mat( node->transform );
+
+			if ( m_data[n] && !m_data[n]->empty() )
+			{
+				node_mat = m_data[n]->get_matrix( anim_time );
+			}
+
+			sint16 bone_id = m_bone_ids[n];
+			data[ bone_id ] = node_mat * m_offsets[ bone_id ];
+		}
+
+}
+
+void nv::nmd_temp_animation::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat )
+{
+	// TODO: fix transforms, which are now embedded,
+	//       see note in assimp_loader.cc:load_node
+	const nmd_node* node = &m_animation->nodes[ node_id ];
+	mat4 node_mat( node->transform );
+
+	if ( m_data[ node_id ] && !m_data[ node_id ]->empty() )
+	{
+		node_mat = m_data[ node_id ]->get_matrix( time );
+	}
+
+	mat4 global_mat = parent_mat * node_mat;
+
+	sint16 bone_id = m_bone_ids[ node_id ];
+	if ( bone_id >= 0 )
+	{
+		data[ bone_id ] = global_mat * m_offsets[ bone_id ];
+	}
+
+	for ( auto child : m_children[ node_id ] )
+	{
+		animate_rec( data, time, child, global_mat );
+	}
+}
+
+nv::nmd_temp_model::nmd_temp_model( nmd_loader* loader )
+{
+	for ( unsigned m = 0; m < loader->get_mesh_count(); ++m )
+	{
+		m_mesh_data.push_back(loader->release_mesh_data(m));
+	}
+	nmd_bone_data* bone_data = loader->release_bone_data();
+	string_table*  strings   = loader->release_string_table();
+
+	for ( nv::uint16 bi = 0; bi < bone_data->count; ++bi )
+	{
+		m_bone_names[ strings->get( bone_data->bones[bi].name ) ] = bi;
+		m_bone_offsets.push_back( bone_data->bones[bi].offset );
+	}
+
+	delete bone_data;
+	delete strings;
+}
+
+nv::nmd_temp_model::~nmd_temp_model()
+{
+	for ( unsigned m = 0; m < m_mesh_data.size(); ++m )
+	{
+		delete m_mesh_data[m];
+	}
+}
