Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 482)
+++ trunk/src/formats/assimp_loader.cc	(revision 485)
@@ -12,4 +12,20 @@
 
 using namespace nv;
+
+namespace nv {
+
+	struct assimp_data
+	{
+		const aiScene* scene;
+		vector< const aiBone* > bones;
+		vector< const aiNode* > nodes;
+		vector< const aiMesh* > meshes;
+		vector< const aiNode* > skeletons;
+
+		hash_store< shash64, const aiBone* > bone_by_name;
+		hash_store< shash64, const aiNode* > node_by_name;
+	};
+
+}
 
 const unsigned MAX_BONES = 64;
@@ -58,5 +74,5 @@
 
 nv::assimp_loader::assimp_loader( string_table* strings, const string_view& a_ext, uint32 a_assimp_flags /*= 0 */ )
-	: mesh_loader( strings ), m_scene( nullptr ), m_mesh_count(0)
+	: mesh_loader( strings ), m_mesh_count(0)
 {
 	m_ext   = a_ext;
@@ -76,7 +92,9 @@
 			aiProcess_SortByPType                   |  
 			aiProcess_FindDegenerates               |  
-			aiProcess_FindInvalidData               |  
+			aiProcess_FindDegenerates |
 			0 );
 	}
+	m_data = new assimp_data;
+	m_data->scene = nullptr;
 }
 
@@ -85,6 +103,6 @@
 {
 	load_assimp_library();
-	if ( m_scene != nullptr ) aiReleaseImport( reinterpret_cast<const aiScene*>( m_scene ) );
-	m_scene = nullptr;
+	if ( m_data->scene != nullptr ) aiReleaseImport( m_data->scene );
+	m_data->scene = nullptr;
 	m_mesh_count = 0;
 	NV_LOG_NOTICE( "AssImp loading file..." );
@@ -99,7 +117,27 @@
 		return false;
 	}
-	m_scene      = scene;
-	m_mesh_count = scene->mNumMeshes;
+	m_data->scene = scene;
+	m_mesh_count  = scene->mNumMeshes;
 	NV_LOG_NOTICE( "Loading successfull" );
+
+	scan_nodes( scene->mRootNode );
+
+	for ( unsigned i = 0; i < scene->mRootNode->mNumChildren; ++i )
+	{
+		const aiNode* node = scene->mRootNode->mChildren[i];
+		if ( node->mNumMeshes == 0 )
+			m_data->skeletons.push_back( node );
+	}
+
+	for ( nv::uint32 i = 0; i < m_mesh_count; i++ )
+	{
+ 		data_node_info info;
+		data_channel_set* data = data_channel_set_creator::create_set( 2 );
+		load_mesh_data( data, i, info );
+		m_meshes.push_back( data );
+		m_mesh_info.push_back( info );
+	}
+
+	scene_report();
 	return true;
 }
@@ -108,12 +146,10 @@
 {
 	if ( index >= m_mesh_count ) return nullptr;
-	data_channel_set* result = data_channel_set_creator::create_set( 2 );
-	load_mesh_data( result, index, info );
-	return result;
+	info = m_mesh_info[index];
+	return m_meshes[index];
 }
 void nv::assimp_loader::load_mesh_data( data_channel_set* data, size_t index, data_node_info& info )
 {
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
-	const aiMesh*  mesh  = scene->mMeshes[ index ];
+	const aiMesh*  mesh  = m_data->scene->mMeshes[ index ];
 
 	bool skinned = mesh->mNumBones > 0;
@@ -125,7 +161,30 @@
 		desc.initialize< assimp_plain_vtx >();
 	data_channel_set_creator maccess( data );
-	const char* name = mesh->mName.data;
+	string64 name( mesh->mName.data, mesh->mName.length );
+	if ( mesh->mName.length == 0 )
+	{
+		for ( auto node : m_data->nodes )
+		{
+			if ( node->mMeshes )
+				for ( uint32 i = 0; i < node->mNumMeshes; ++i )
+					if ( node->mMeshes[i] == index )
+					{
+						name.assign( node->mName.data, node->mName.length );
+						if ( i != 0 )
+						{
+							name.append( "#0" );
+							name.append( i );
+						}
+					}
+
+		}
+	}
 	info.name = make_name( name );
 	info.parent_id = -1;
+	int hack_for_node_anim;
+	if ( is_node_animated() )
+		info.parent_id = index;
+
+
 	uint8*  cdata   = maccess.add_channel( desc, mesh->mNumVertices ).raw_data();
 	uint16* indices = reinterpret_cast<uint16*>( maccess.add_channel< index_u16 >( mesh->mNumFaces * 3 ).raw_data() );
@@ -188,37 +247,20 @@
 nv::assimp_loader::~assimp_loader()
 {
-	if ( m_scene != nullptr ) aiReleaseImport( reinterpret_cast<const aiScene*>( m_scene ) );
-}
-
-bool nv::assimp_loader::load_bones( size_t index, array_ref< data_node_info > bones )
-{
-	if ( m_scene == nullptr ) return false;
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
-	const aiMesh*  mesh  = scene->mMeshes[ index ];
-
-	for (unsigned int m=0; m<mesh->mNumBones; m++)
-	{
-		aiBone* bone   = mesh->mBones[m];
-		mat4    offset = assimp_mat4_cast( bone->mOffsetMatrix );
-		const char* name = bone->mName.data;
-		bones[m].name = make_name( name );
-		bones[m].transform = offset;
-	}
-	return true;
+	if ( m_data->scene != nullptr ) aiReleaseImport( m_data->scene );
+	delete m_data;
 }
 
 void nv::assimp_loader::scene_report() const
 {
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
-	if ( scene == nullptr ) return;
+	if ( m_data->scene == nullptr ) return;
 
 	NV_LOG_NOTICE( "------------------------" );
-	NV_LOG_NOTICE( "Texture   count - ", scene->mNumTextures );
-	NV_LOG_NOTICE( "Animation count - ", scene->mNumAnimations );
-	NV_LOG_NOTICE( "Material  count - ", scene->mNumMaterials );
-	NV_LOG_NOTICE( "Meshes    count - ", scene->mNumMeshes );
+	NV_LOG_NOTICE( "Texture   count - ", m_data->scene->mNumTextures );
+	NV_LOG_NOTICE( "Animation count - ", m_data->scene->mNumAnimations );
+	NV_LOG_NOTICE( "Material  count - ", m_data->scene->mNumMaterials );
+	NV_LOG_NOTICE( "Meshes    count - ", m_data->scene->mNumMeshes );
 	NV_LOG_NOTICE( "------------------------" );
 
-	aiNode* root = scene->mRootNode;
+	aiNode* root = m_data->scene->mRootNode;
 	if (root)
 	{
@@ -233,11 +275,11 @@
 	NV_LOG_NOTICE( "------------------------" );
 
-	if ( scene->mNumMeshes > 0 )
-	{
-		for ( nv::uint32 mc = 0; mc < scene->mNumMeshes; mc++ )
-		{
-			aiMesh* mesh = scene->mMeshes[mc];
-
-			NV_LOG_NOTICE( "Mesh #", mc, "   - ", string_view( static_cast<char*>( mesh->mName.data ) ) );
+	if ( m_data->scene->mNumMeshes > 0 )
+	{
+		for ( nv::uint32 mc = 0; mc < m_data->scene->mNumMeshes; mc++ )
+		{
+			aiMesh* mesh = m_data->scene->mMeshes[mc];
+
+			NV_LOG_NOTICE( "Mesh #", mc, "   - ", string_view( static_cast<char*>( mesh->mName.data ), mesh->mName.length ) );
 			NV_LOG_NOTICE( "  bones   - ", mesh->mNumBones );
 			NV_LOG_NOTICE( "  uvs     - ", mesh->mNumUVComponents[0] );
@@ -259,4 +301,13 @@
 	NV_LOG_NOTICE( "------------------------" );
 
+	for ( auto node : m_data->nodes )
+	{
+		NV_LOG_NOTICE( "Node : ", string_view( node->mName.data, node->mName.length ) );
+	}
+
+	for ( auto skeleton : m_data->skeletons )
+	{
+		NV_LOG_NOTICE( "Skeleton : ", string_view( skeleton->mName.data, skeleton->mName.length ) );
+	}
 
 	// 	if ( scene->mNumMaterials > 0 )
@@ -283,64 +334,117 @@
 }
 
-data_node_list* nv::assimp_loader::release_merged_bones( data_channel_set* meshes )
-{
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
-	data_node_list* result = new data_node_list( make_name( "bones" ) );
-	hash_store< shash64, uint16 > names;
+bool nv::assimp_loader::is_node_animated()
+{
+	return is_animated() && m_data->skeletons.empty();
+}
+
+void nv::assimp_loader::build_skeleton( vector< data_node_info >& skeleton, const void* node, int parent_id )
+{
+	const aiNode* ainode = reinterpret_cast<const aiNode*>( node );
+
+	if ( ainode->mNumMeshes > 0 )
+	{
+		int error;
+		int bug_this_works_only_if_before_releasing_meshes;
+
+		nv::uint32 mid = ainode->mMeshes[0];
+		m_mesh_info[mid].parent_id = parent_id;
+		return;
+	}
+
+	string_view name( ainode->mName.data, ainode->mName.length );
+	if ( name.starts_with( '_' ) ) return;
+
+	data_node_info info;
+	info.name      = make_name( name );
+	info.parent_id = parent_id;
+
+	int this_id = skeleton.size();
+	skeleton.push_back( info );
+	for ( unsigned i = 0; i < ainode->mNumChildren; ++i )
+	{
+		build_skeleton( skeleton, ainode->mChildren[i], this_id );
+	}
+}
+
+data_node_list* nv::assimp_loader::release_merged_bones()
+{
+	if ( m_data->skeletons.empty() ) return nullptr;
+	vector< data_node_info > bone_data;
+	hash_store< shash64, uint16 > bone_map;
+
+	{
+		const aiNode* skeleton = m_data->skeletons[0];
+		build_skeleton( bone_data, skeleton, -1 );
+
+		for ( uint32 i = 0; i < bone_data.size(); ++i )
+		{
+			bone_map[bone_data[i].name] = uint16(i);
+		}
+	}
+
 	for ( unsigned int m = 0; m < m_mesh_count; ++m )
 	{
 		uint16 translate[MAX_BONES];
 		vector< data_node_info > bones;
-		const aiMesh*  mesh  = scene->mMeshes[ m ];
+		const aiMesh*  mesh = m_data->scene->mMeshes[m];
 		if ( mesh->mNumBones != 0 )
 		{
-			bones.resize( mesh->mNumBones );
-			NV_ASSERT( false, "parent ids for bones are not loaded!" );
-			load_bones( m, bones );
-			for ( unsigned int b = 0; b < mesh->mNumBones; ++b )
+			for ( unsigned int b = 0; b < mesh->mNumBones; b++ )
 			{
-
-				data_node_info bone = bones[b];
-				auto iname = names.find( bone.name );
-				if ( iname == names.end() )
+				aiBone* bone = mesh->mBones[b];
+				mat4    offset = assimp_mat4_cast( bone->mOffsetMatrix );
+				shash64 bone_name( bone->mName.data );
+
+				int remove_this;
+
+				NV_ASSERT( bone_map.find( shash64( bone->mName.data ) ) != bone_map.end(), "BONE NOT FOUND!" );
+				uint16 index = bone_map[bone_name];
+				bone_data[index].transform = offset;
+				translate[b] = index;
+			}
+
+			data_channel_access< assimp_skinned_vtx > channel( const_cast<raw_data_channel*>( m_meshes[m]->get_channel( 0 ) ) );
+			for ( unsigned v = 0; v < channel.size(); ++v )
+			{
+				assimp_skinned_vtx& vertex = channel.data()[v];
+				for ( int i = 0; i < 4; ++i )
 				{
-					NV_ASSERT( result->size() < MAX_BONES, "Too many bones to merge!" );
-					uint16 index = uint16( result->size() );
-					result->append( bone );
-					names[ bone.name ] = index;
-					translate[b] = index;
-				}
-				else
-				{
-					translate[b] = iname->second;
-				}
-			}
-			if ( m > 0 && bones.size() > 0 )
-			{
-				data_channel_access< assimp_skinned_vtx > channel( const_cast< raw_data_channel* >( meshes[m].get_channel(0) ) );
-				for ( unsigned v = 0; v < channel.size(); ++v )
-				{
-					assimp_skinned_vtx& vertex = channel.data()[v];
-
-					for ( int i = 0 ; i < 4; ++i)
+					if ( vertex.boneweight[i] > 0.0f )
 					{
-						if ( vertex.boneweight[i] > 0.0f ) 
-						{
-							vertex.boneindex[i] = int( translate[vertex.boneindex[i]] );
-						}
+						vertex.boneindex[i] = int( translate[vertex.boneindex[i]] );
 					}
 				}
 			}
-		}	
-	}
-	//result->initialize();
-
-	return result;
+
+		}
+	}
+
+	for ( uint32 i = 0; i < bone_data.size(); ++i )
+	{
+		int error; // not really, just
+		int check_this_shit;
+		if ( bone_data[i].transform == mat4() )
+		{
+			mat4 tr = nv::math::inverse( assimp_mat4_cast( m_data->node_by_name[bone_data[i].name]->mTransformation ) );
+			bone_data[i].transform = tr * bone_data[bone_data[i].parent_id].transform;
+		}
+//		list->append( bone_data[i] );
+	}
+
+
+	data_node_list* list = new data_node_list( make_name( "bones" ) );
+	for ( uint32 i = 0; i < bone_data.size(); ++i )
+	{
+		list->append( bone_data[i] );
+	}
+
+	return list;
 }
 
 mesh_nodes_data* nv::assimp_loader::release_mesh_nodes_data( size_t index /*= 0*/ )
 {
-	if ( m_scene == nullptr ) return nullptr;
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
+	if ( m_data->scene == nullptr ) return nullptr;
+	const aiScene* scene = reinterpret_cast<const aiScene*>( m_data->scene );
 	if ( scene->mRootNode == nullptr || scene->mAnimations == nullptr || scene->mAnimations[index] == nullptr) return nullptr;
 
@@ -349,13 +453,20 @@
 
 	uint32 count = count_nodes( scene->mRootNode );
+	count = count - 1;
 
 	uint16 frame_rate     = static_cast<uint16>( anim->mTicksPerSecond );
-	uint16 duration       = static_cast<uint16>( anim->mDuration );
+	uint16 duration       = static_cast<uint16>( anim->mDuration )+1;
 
 	data_channel_set** temp  = new data_channel_set*[ count ];
-	data_node_info*    temp2 = new data_node_info[count];
+	data_node_info*    temp2 = new data_node_info[count ];
 	array_ref< data_channel_set* > temp_ref( temp, count );
 	array_ref< data_node_info >    temp2_ref( temp2, count );
-	load_node( index, temp_ref, temp2_ref, root, 0, -1 );
+
+	nv::sint16 next = 0;
+	for ( unsigned i = 0; i < scene->mRootNode->mNumChildren; ++i )
+	{
+		next = load_node( index, temp_ref, temp2_ref, scene->mRootNode->mChildren[i], next, -1 );
+	}
+//	load_node( index, temp_ref, temp2_ref, scene->mRootNode, 0, -1 );
 
 	mesh_nodes_data* result = new mesh_nodes_data( make_name( static_cast<const char*>( anim->mName.data ) ), frame_rate, duration );
@@ -372,19 +483,25 @@
 data_node_list* nv::assimp_loader::release_data_node_list( size_t index /*= 0 */ )
 {
+	return release_merged_bones();
+}
+
+bool nv::assimp_loader::is_animated( size_t /*= 0 */ )
+{
 	int this_is_incorrect;
-	NV_ASSERT( false, "unimplemented!" );
-// 	mesh_nodes_data* half_result = release_mesh_nodes_data( index );
-// 	data_node_list* result = new data_node_list( half_result->get_name() );
-// 	for ( auto node : *half_result )
-// 		result->append( node->get_info() );
-// 	delete half_result;
-//	return result;
-	return nullptr;
-}
-
-bool nv::assimp_loader::is_animated( size_t /*= 0 */ )
-{
-	int this_is_incorrect;
-	return false;
+	return m_mesh_count == 0 || m_data->scene->mNumAnimations > 0 && m_data->skeletons.size() == 0;
+}
+
+int indent = 0;
+
+void nv::assimp_loader::scan_nodes( const void* node ) const
+{
+	const aiNode* ainode = reinterpret_cast<const aiNode*>( node );
+	m_data->nodes.push_back( ainode );
+	m_data->node_by_name[shash64(ainode->mName.data)] = ainode;
+
+	for ( unsigned i = 0; i < ainode->mNumChildren; ++i )
+	{
+		scan_nodes( ainode->mChildren[i] );
+	}
 }
 
@@ -402,8 +519,7 @@
 nv::sint16 nv::assimp_loader::load_node( uint32 anim_id, array_ref< data_channel_set* > nodes, array_ref< data_node_info > infos, const void* vnode, sint16 this_id, sint16 parent_id )
 {
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
 	const aiNode*  node  = reinterpret_cast<const aiNode*>( vnode );
 	string_view name( static_cast< const char* >( node->mName.data ) );
-	const aiAnimation* anim  = scene->mAnimations[anim_id];
+	const aiAnimation* anim  = m_data->scene->mAnimations[anim_id];
 	const aiNodeAnim*  anode = nullptr;
 
@@ -415,5 +531,8 @@
 	}
 
-	nodes[ this_id ] = anode ? create_keys( anode ) : data_channel_set_creator::create_set( 0 );
+
+	transform t = nv::transform( nv::assimp_mat4_cast( node->mTransformation ) );
+
+	nodes[ this_id ] = anode ? create_keys( anode, t ) : data_channel_set_creator::create_set( 0 );
 
 	infos[this_id].name      = make_name( name );
@@ -424,6 +543,7 @@
 	//       node's without keys
 	// TODO: this can probably be deleted
-	infos[this_id].transform = nv::assimp_mat4_cast( node->mTransformation );
-	if ( this_id == 0 ) infos[this_id].transform = mat4();
+//   	infos[this_id].transform = nv::assimp_mat4_cast( node->mTransformation );
+//   	if ( this_id == 0 ) infos[this_id].transform = mat4();
+
 
 	nv::sint16 next = this_id + 1;
@@ -436,12 +556,13 @@
 }
 
-data_channel_set* nv::assimp_loader::create_keys( const void* vnode )
-{
-	const aiNodeAnim* node = reinterpret_cast< const aiNodeAnim* >( vnode );
+data_channel_set* nv::assimp_loader::create_keys( const void* vnode, const transform& tr )
+{
+	const aiNodeAnim* node  = reinterpret_cast< const aiNodeAnim* >( vnode );
 	if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 )
 	{
 		return data_channel_set_creator::create_set( 0 );
 	}
-	
+
+/* OLD
 	data_channel_set* set = data_channel_set_creator::create_set( 2 );
 	data_channel_set_creator key_set( set );
@@ -461,4 +582,17 @@
 		rchannel[np].rotation = assimp_quat_cast(node->mRotationKeys[np].mValue );
 	}
+	*/
+	data_channel_set* set = data_channel_set_creator::create_set( 1 );
+	data_channel_set_creator key_set( set );
+
+	assimp_key_tr* channel = key_set.add_channel< assimp_key_tr >( node->mNumPositionKeys ).data();
+	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
+	{
+		channel[np].tform.set_position( assimp_vec3_cast( node->mPositionKeys[np].mValue ) );
+		channel[np].tform.set_orientation( assimp_quat_cast( node->mRotationKeys[np].mValue ) );
+		if ( is_node_animated() )
+			channel[np].tform = tr.inverse() * channel[np].tform ;
+	}
+
 // 	if ( node->mNumScalingKeys > 0 )
 // 	{
@@ -503,8 +637,7 @@
 nv::size_t nv::assimp_loader::get_nodes_data_count() const
 {
-	if ( m_scene == nullptr ) return 0;
-	const aiScene* scene = reinterpret_cast<const aiScene*>( m_scene );
-	return scene->mNumAnimations;	
-}
-
-
+	if ( m_data->scene == nullptr ) return 0;
+	return m_data->scene->mNumAnimations;
+}
+
+
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 482)
+++ trunk/src/formats/nmd_loader.cc	(revision 485)
@@ -26,6 +26,7 @@
 		{
 		case nmd_type::MESH           : load_mesh( source, element_header ); break;
-		case nmd_type::ANIMATION      : load_animation( source, element_header ); break;
+		case nmd_type::BONES          : load_bones( source, element_header ); break;
 		case nmd_type::STRINGS        : load_strings( source ); break;
+		case nmd_type::POSES          : load_poses( source, element_header ); break;
 		default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
 		}
@@ -39,5 +40,4 @@
 	data_node_info info;
 	load_channel_set( source, mesh, info, e );
-//	m_mesh_names.push_back( e.name );
 	m_infos.push_back( info );
 	m_meshes.push_back( mesh );
@@ -56,9 +56,8 @@
 {
 	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
-	if ( m_node_data ) delete m_node_data;
 	if ( m_bone_data ) delete m_bone_data;
+	if ( m_pose_data_set ) delete m_pose_data_set;
 	m_meshes.clear();
 
-	m_node_data = nullptr;
 	m_bone_data = nullptr;
 }
@@ -85,10 +84,9 @@
 }
 
-bool nv::nmd_loader::load_animation( stream& source, const nmd_element_header& e )
-{
-	NV_ASSERT( m_node_data == nullptr, "MULTIPLE NODE ENTRIES!" );
+bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
+{
+	NV_ASSERT( m_bone_data == nullptr, "MULTIPLE NODE ENTRIES!" );
 	nmd_animation_header animation_header;
 	source.read( &animation_header, sizeof( animation_header ), 1 );
-	m_node_data = new mesh_nodes_data( e.name, animation_header.frame_rate, animation_header.frame_count );
 	m_bone_data = new data_node_list( e.name );
 	for ( uint32 i = 0; i < e.children; ++i )
@@ -102,7 +100,6 @@
 		load_channel_set( source, set, info, element_header );
 		m_bone_data->append( info );
-		m_node_data->append( set, info );
-	}
-	m_node_data->initialize();
+		delete set;
+	}
 	return true;
 }
@@ -131,9 +128,37 @@
 }
 
+bool nv::nmd_loader::load_poses( stream& source, const nmd_element_header& e )
+{
+	if ( m_pose_data_set == nullptr )
+	{
+		NV_ASSERT_ALWAYS( m_bone_data, "POSES WITHOUT BONES!" );
+		m_pose_data_set = new pose_data_set;
+		m_pose_data_set->initialize( *m_bone_data );
+	}
+
+	uint32  count = e.children;
+	shash64 name  = e.name;
+
+	auto& set = m_pose_data_set->m_sets[ name ];
+	NV_ASSERT_ALWAYS( !set.name, "SET REWRITE!" );
+	set.name = name;
+	set.count = count;
+	set.start = m_pose_data_set->size();
+
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		uint32 length = 0; 
+		source.read( &length, sizeof( length ), 1 );
+		m_pose_data_set->m_data.push_back( skeleton_transforms() );
+		auto& data = m_pose_data_set->m_data.back().m_transforms;
+		data.resize( length );
+		source.read( &data[0], length * sizeof( transform ), 1 );
+	}
+	return true;
+}
+
 mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data( size_t )
 {
-	mesh_nodes_data* result = m_node_data;
-	m_node_data = nullptr;
-	return result;
+	return nullptr;
 }
 
@@ -147,6 +172,5 @@
 bool nv::nmd_loader::is_animated( size_t /*= 0 */ )
 {
-	if ( !m_node_data ) return false;
-	return m_node_data->is_animated();
+	return m_pose_data_set != nullptr;
 }
 
@@ -194,5 +218,5 @@
 }
 
-void nv::nmd_dump_nodes( stream& stream_out, const mesh_nodes_data& nodes )
+void nv::nmd_dump_bones( stream& stream_out, const data_node_list& nodes )
 {
 	uint32 total = sizeof( nmd_animation_header );
@@ -200,44 +224,8 @@
 	{
 		total += sizeof( nmd_element_header );
-		for ( uint32 c = 0; c < node->size(); ++c )
-		{
-			total += sizeof( nmd_channel_header );
-			total += node->get_channel( c )->raw_size();
-		}
 	}
 
 	nmd_element_header header;
-	header.type = nmd_type::ANIMATION;
-	header.children = static_cast<uint16>( nodes.size() );
-	header.size = total;
-	header.name = nodes.get_name();
-	header.transform = mat4();
-	header.parent_id = -1;
-	header.attributes = 0;
-
-	stream_out.write( &header, sizeof( header ), 1 );
-
-	nmd_animation_header aheader;
-	aheader.frame_rate  = nodes.get_fps();
-	aheader.frame_count = nodes.get_frame_count();
-	aheader.unused      = false;
-	stream_out.write( &aheader, sizeof( aheader ), 1 );
-
-	for ( uint32 i = 0; i < nodes.size(); ++i )
-	{
-		nmd_dump_element( stream_out, *nodes[i], nodes.get_info(i), nv::nmd_type::NODE );
-	}
-}
-
-void nv::nmd_dump_bones( stream& stream_out, const data_node_list& nodes )
-{
-	uint32 total = sizeof( nmd_animation_header );
-	for ( auto node : nodes )
-	{
-		total += sizeof( nmd_element_header );
-	}
-
-	nmd_element_header header;
-	header.type = nmd_type::ANIMATION;
+	header.type = nmd_type::BONES;
 	header.children = static_cast<uint16>( nodes.size() );
 	header.size = total;
@@ -269,4 +257,38 @@
 }
 
+void nv::nmd_dump_poses( stream& stream_out, const array_view< skeleton_transforms* > poses, shash64 name )
+{
+	nmd_element_header pheader;
+	pheader.type       = nv::nmd_type::POSES;
+	pheader.children   = poses.size();
+	pheader.size       = sizeof( transform ) * poses.size() * ( poses.size() > 0 ? poses[0]->size() : 0 )
+		               + sizeof( uint32 ) * poses.size();
+	pheader.name       = name;
+	pheader.parent_id  = -1;
+	pheader.attributes = 0;
+	stream_out.write( &pheader, sizeof( pheader ), 1 );
+	for ( auto pose : poses )
+	{
+		uint32 count = pose->size();
+		stream_out.write( &count, sizeof( count ), 1 );
+		stream_out.write( pose->xforms(), sizeof( transform ) * count, 1 );
+	}
+}
+
+void nv::nmd_dump_pose( stream& stream_out, const skeleton_transforms& pose, shash64 name )
+{
+	nmd_element_header pheader;
+	pheader.type       = nv::nmd_type::POSES;
+	pheader.children   = 1;
+	pheader.size       = sizeof( transform ) * pose.size();
+	pheader.name       = name;
+	pheader.parent_id  = -1;
+	pheader.attributes = 0;
+	stream_out.write( &pheader, sizeof( pheader ), 1 );
+	uint32 count = pose.size();
+	stream_out.write( &count, sizeof( count ), 1 );
+	stream_out.write( pose.xforms(), sizeof( transform ) * count, 1 );
+}
+
 void nv::nmd_dump_strings( stream& stream_out, const string_table& strings )
 {
@@ -282,5 +304,5 @@
 }
 
-void nv::nmd_dump( stream& stream_out, array_view< data_channel_set* > meshes, array_view< data_node_info > infos, const mesh_nodes_data* nodes, const string_table* strings /*= nullptr*/, uint64 name /*= 0 */ )
+void 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 */ )
 {
 	uint32 elements = ( strings ? 1 : 0 ) // +1 string array
@@ -297,5 +319,5 @@
 	if ( nodes && nodes->size() > 0 )
 	{
-		nmd_dump_nodes( stream_out, *nodes );
+		nmd_dump_bones( stream_out, *nodes );
 	}
 
@@ -305,43 +327,2 @@
 	}
 }
-
-void 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 */ )
-{
-	uint32 elements = ( strings ? 1 : 0 ) // +1 string array
-		+ meshes.size() // meshes
-		+ ( nodes && nodes->size() > 0 ? 1 : 0 ); // nodes
-	nmd_dump_header( stream_out, elements, name );
-
-	for ( uint32 i = 0; i < meshes.size(); ++i )
-	{
-		NV_ASSERT( meshes[i], "mesh is null!" );
-		nmd_dump_element( stream_out, *meshes[i], infos[i], nv::nmd_type::MESH );
-	}
-
-	if ( nodes && nodes->size() > 0 )
-	{
-		nmd_dump_bones( stream_out, *nodes );
-	}
-
-	if ( strings )
-	{
-		nmd_dump_strings( stream_out, *strings );
-	}
-}
-
-void nv::nmd_dump( stream& stream_out, const mesh_nodes_data& animation, const string_table* strings, uint64 name )
-{
-	uint32 elements = ( strings ? 1 : 0 ) // +1 string array
-		+ ( animation.size() > 0 ? 1 : 0 ); // nodes
-	nmd_dump_header( stream_out, elements, name );
-
-	if ( animation.size() > 0 )
-	{
-		nmd_dump_nodes( stream_out, animation );
-	}
-
-	if ( strings )
-	{
-		nmd_dump_strings( stream_out, *strings );
-	}
-}
