Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 285)
+++ trunk/src/formats/assimp_loader.cc	(revision 287)
@@ -79,7 +79,13 @@
 {
 	if ( index >= m_mesh_count ) return nullptr;
-	mesh_data* result = new mesh_data();
+	mesh_data* result = new mesh_data;
+	load_mesh_data( result, index );
+	return result;
+}
+void nv::assimp_loader::load_mesh_data( mesh_data* data, size_t index )
+{
 	const aiScene* scene = (const aiScene*)m_scene;
 	const aiMesh*  mesh  = scene->mMeshes[ index ];
+	data->set_name( mesh->mName.data );
 
 	vec3 vertex_offset     = glm::vec3(); 
@@ -94,5 +100,5 @@
 		channel = mesh_raw_channel::create< assimp_plain_vtx >( mesh->mNumVertices );
 
-	result->add_channel( channel );
+	data->add_channel( channel );
 	for (unsigned int i=0; i<mesh->mNumVertices; i++)
 	{
@@ -139,5 +145,5 @@
 
 	mesh_raw_channel* ichannel = mesh_raw_channel::create_index( USHORT, mesh->mNumFaces * 3 );
-	result->add_channel( ichannel );
+	data->add_channel( ichannel );
 	uint16* indices = (uint16*)ichannel->data;
 	for (unsigned int i=0; i<mesh->mNumFaces; i++)
@@ -149,6 +155,4 @@
 		}
 	}
-
-	return result;
 }
 
@@ -322,9 +326,18 @@
 	const aiAnimation* anim = scene->mAnimations[0]; // if implemented, change in load_node also
 
-	result->fps            = (float)anim->mTicksPerSecond;
+	result->frame_rate     = (uint16)anim->mTicksPerSecond;
 	result->duration       = (float)anim->mDuration;
-	result->pretransformed = pre_transform;
+	result->flat           = pre_transform;
+	result->max_frames     = 0;
 
 	load_node( result, root, 0, -1 );
+
+	// DAE pre_transform hack
+	if ( result->flat && result->frame_rate == 1 )
+	{
+		result->frame_rate = 32;
+		result->duration   = (float)result->max_frames;
+	}
+
 	return result;
 }
@@ -371,5 +384,5 @@
 	if (anode) 
 	{
-		if ( data->pretransformed )
+		if ( data->flat )
 		{
 			create_transformed_keys( &a_data, anode, parent_id >= 0 ? &(data->nodes[ parent_id ]) : nullptr );
@@ -379,4 +392,5 @@
 			create_direct_keys( &a_data, anode );
 		}
+		data->max_frames = glm::max<uint32>( a_data.data->get_channel(0)->count, data->max_frames );
 	}
 
@@ -386,4 +400,5 @@
 		next = load_node( data, node->mChildren[i], next, this_id );
 	}
+
 	return next;
 }
@@ -432,11 +447,11 @@
 	key_raw_channel* raw_pchannel = key_raw_channel::create<assimp_key_p>( node->mNumPositionKeys );
 	key_raw_channel* raw_rchannel = key_raw_channel::create<assimp_key_r>( node->mNumRotationKeys );
-	key_raw_channel* raw_schannel = key_raw_channel::create<assimp_key_s>( node->mNumScalingKeys );
+	//key_raw_channel* raw_schannel = key_raw_channel::create<assimp_key_s>( node->mNumScalingKeys );
 	data->data->add_channel( raw_pchannel );
 	data->data->add_channel( raw_rchannel );
-	data->data->add_channel( raw_schannel );
+	//data->data->add_channel( raw_schannel );
 	assimp_key_p* pchannel = ((assimp_key_p*)(raw_pchannel->data));
 	assimp_key_r* rchannel = ((assimp_key_r*)(raw_rchannel->data));
-	assimp_key_s* schannel = ((assimp_key_s*)(raw_schannel->data));
+	//assimp_key_s* schannel = ((assimp_key_s*)(raw_schannel->data));
 
 	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
@@ -450,24 +465,40 @@
 		rchannel[np].rotation = glm::quat_cast( m_r33 * glm::mat3_cast( assimp_quat_cast(node->mRotationKeys[np].mValue ) ) * m_ri33 );
 	}
-	if ( node->mNumScalingKeys > 0 )
-	{
-		nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
-		float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
-		if ( node->mNumScalingKeys > 1 || scale_value > 0.001 ) 
-		{
-			NV_LOG( nv::LOG_WARNING, "scale key significant!" );
-			for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
-			{
-				schannel[np].time  = (float)node->mScalingKeys[np].mTime;
-				schannel[np].scale = assimp_vec3_cast(node->mScalingKeys[np].mValue);
-			}
-		}
-		else 
-		{
-			schannel[0].time  = (float)node->mScalingKeys[0].mTime;
-			schannel[0].scale = assimp_vec3_cast(node->mScalingKeys[0].mValue);
-		}
-	}
-
-}
-
+// 	if ( node->mNumScalingKeys > 0 )
+// 	{
+// 		nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
+// 		float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
+// 		if ( node->mNumScalingKeys > 1 || scale_value > 0.001 ) 
+// 		{
+// 			NV_LOG( nv::LOG_WARNING, "scale key significant!" );
+// 			for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
+// 			{
+// 				schannel[np].time  = (float)node->mScalingKeys[np].mTime;
+// 				schannel[np].scale = assimp_vec3_cast(node->mScalingKeys[np].mValue);
+// 			}
+// 		}
+// 		else 
+// 		{
+// 			schannel[0].time  = (float)node->mScalingKeys[0].mTime;
+// 			schannel[0].scale = assimp_vec3_cast(node->mScalingKeys[0].mValue);
+// 		}
+// 	}
+
+}
+
+mesh_data_pack* nv::assimp_loader::release_mesh_data_pack()
+{
+	mesh_data* meshes = new mesh_data[ m_mesh_count ];
+	for ( uint32 i = 0; i < m_mesh_count; ++i )
+	{
+		load_mesh_data(&meshes[i],i);
+	}
+	return new mesh_data_pack( m_mesh_count, meshes, release_mesh_nodes_data() );
+}
+
+mesh_nodes_data* nv::assimp_loader::release_mesh_nodes_data()
+{
+	// TODO: implement
+	return nullptr;
+}
+
Index: trunk/src/formats/md2_loader.cc
===================================================================
--- trunk/src/formats/md2_loader.cc	(revision 285)
+++ trunk/src/formats/md2_loader.cc	(revision 287)
@@ -315,8 +315,10 @@
 mesh_data* nv::md2_loader::release_mesh_data( size_t )
 {
-	return release_mesh_frame( -1 );
-}
-
-mesh_data* nv::md2_loader::release_mesh_frame( sint32 frame )
+	mesh_data* data = new mesh_data( "md2_mesh" );
+	release_mesh_frame( data, -1 );
+	return data;
+}
+
+void nv::md2_loader::release_mesh_frame( mesh_data* data, sint32 frame )
 {
 	md2_t* md2 = (md2_t*)m_md2;
@@ -366,8 +368,13 @@
 	}
 
-	mesh_data* result = new mesh_data();
-	result->add_channel( mc_pn );
-	result->add_channel( mc_t );
-	result->add_channel( ic );
-	return result;
-}
+	data->add_channel( mc_pn );
+	data->add_channel( mc_t );
+	data->add_channel( ic );
+}
+
+mesh_data_pack* nv::md2_loader::release_mesh_data_pack()
+{
+	mesh_data* data = new mesh_data[1];
+	release_mesh_frame( &data[0], -1 );
+	return new mesh_data_pack( 1, data );
+}
Index: trunk/src/formats/md3_loader.cc
===================================================================
--- trunk/src/formats/md3_loader.cc	(revision 285)
+++ trunk/src/formats/md3_loader.cc	(revision 287)
@@ -321,8 +321,10 @@
 mesh_data* nv::md3_loader::release_mesh_data( size_t )
 {
-	return release_mesh_frame( -1 );
-}
-
-mesh_data* nv::md3_loader::release_mesh_frame( sint32 frame )
+	mesh_data* data = new mesh_data;
+	release_mesh_frame( data, -1 );
+	return data;
+}
+
+void nv::md3_loader::release_mesh_frame( mesh_data* data, sint32 frame )
 {
 	md3_t* md3 = (md3_t*)m_md3;
@@ -394,48 +396,38 @@
 	}
 
-	mesh_data* result = new mesh_data();
-	result->add_channel( mc_pn );
-	result->add_channel( mc_t );
-	result->add_channel( ic );
-	return result;
-}
-
-tag_map* nv::md3_loader::create_tag_map()
+	data->set_name( (char*)md3->header.name );
+	data->add_channel( mc_pn );
+	data->add_channel( mc_t );
+	data->add_channel( ic );
+}
+
+mesh_nodes_data* nv::md3_loader::release_mesh_nodes_data()
 {
 	md3_t* md3 = (md3_t*)m_md3;
-	tag_map* result = new tag_map();
-	for ( sint32 i = 0; i < md3->header.num_tags; ++i )
+	uint32 node_count = md3->header.num_tags;
+	if ( node_count == 0 ) return nullptr;;
+	mesh_node_data* nodes = new mesh_node_data[ node_count ];
+	for ( uint32 i = 0; i < node_count; ++i )
 	{
 		const md3_tag_t& rtag = md3->tags[i];
 		std::string name( (char*)(rtag.name) );
 
-		key_data* data = new key_data;
-		data->add_channel( load_tags( name ) );
-		result->insert( name, data );
-	}
-	return result;
-}
-
-mesh_node_data* nv::md3_loader::release_mesh_node_data( size_t index )
-{
-	md3_t* md3 = (md3_t*)m_md3;
-	const md3_tag_t& rtag = md3->tags[index];
-	std::string name( (char*)(rtag.name) );
-
-	mesh_node_data* result = new mesh_node_data;
-	result->transform = mat4();
-	result->name      = name;
-	result->parent_id = -1;
-	result->target_id = -1;
-	result->data = new key_data;
+		nodes[i].transform = mat4();
+		nodes[i].name      = name;
+		nodes[i].parent_id = -1;
+		nodes[i].target_id = -1;
+		nodes[i].data = new key_data;
 	
-	key_raw_channel* keys = load_tags( name );
-	result->data->add_channel( keys );
-	return result;
-}
-
-size_t nv::md3_loader::get_node_count() const
-{
-	return ((md3_t*)m_md3)->header.num_tags;
+		key_raw_channel* keys = load_tags( name );
+		nodes[i].data->add_channel( keys );
+	}
+	return new mesh_nodes_data( "tags", node_count, nodes );
+}
+
+mesh_data_pack* nv::md3_loader::release_mesh_data_pack()
+{
+	mesh_data* data = new mesh_data[1];
+	release_mesh_frame( &data[0], -1 );
+	return new mesh_data_pack( 1, data, release_mesh_nodes_data() );
 }
 
Index: trunk/src/formats/md5_loader.cc
===================================================================
--- trunk/src/formats/md5_loader.cc	(revision 285)
+++ trunk/src/formats/md5_loader.cc	(revision 287)
@@ -10,5 +10,4 @@
 #include "nv/logging.hh"
 #include "nv/io/std_stream.hh"
-#include "nv/profiler.hh"
 #include <cstring>
 
@@ -45,5 +44,4 @@
 bool md5_loader::load( stream& source )
 {
-	NV_PROFILE( "Load MD5" ); // 16XXms original
 	std_stream sstream( &source );
 	std::string command;
@@ -96,7 +94,9 @@
 		else if ( command == "mesh" )
 		{
-			md5_mesh_data* mesh = new md5_mesh_data();
-
-			int num_verts, num_tris, num_weights;
+			mesh_data* mesh = new mesh_data("md5_mesh");
+
+			int num_verts   = 0;
+			int num_tris    = 0;
+			int num_weights = 0;
 
 			discard( sstream, "{" );
@@ -106,7 +106,7 @@
 				if ( command == "shader" )
 				{
-					sstream >> mesh->m_shader;
-					remove_quotes( mesh->m_shader );
-					// texturePath.replace_extension( ".tga" );
+					std::string shader;
+					sstream >> shader;
+					remove_quotes( shader );
 					next_line( sstream );
 				}
@@ -115,13 +115,16 @@
 					sstream >> num_verts; 
 
+					md5_vtx_t* tdata = nullptr;
 					{
 						mesh_raw_channel* ch_pnt = mesh_raw_channel::create<md5_vtx_pnt>( num_verts );
 						mesh_raw_channel* ch_t   = mesh_raw_channel::create<md5_vtx_t>( num_verts );
-						mesh->m_pntdata          = (md5_vtx_pnt*)ch_pnt->data;
-						mesh->m_tdata            = (md5_vtx_t*)ch_t->data;
+						mesh_raw_channel* ch_pntiw = mesh_raw_channel::create<md5_vtx_pntiw>( num_verts );
+						tdata = (md5_vtx_t*)ch_t->data;
 						mesh->add_channel( ch_pnt );
 						mesh->add_channel( ch_t );
+						// TODO: hack to prevent rendering
+						ch_pntiw->count = 0;
+						mesh->add_channel( ch_pntiw );
 					}
-					mesh->m_vtx_data.resize( num_verts );
 					weight_info.resize( num_verts );
 
@@ -138,5 +141,5 @@
 						weight_info[i].start_weight = start_weight;
 						weight_info[i].weight_count = weight_count;
-						mesh->m_tdata[i].texcoord = texcoord;
+						tdata[i].texcoord = texcoord;
 					}  
 				}
@@ -147,5 +150,4 @@
 					mesh_raw_channel* ch_i = mesh_raw_channel::create_index<uint32>( num_tris * 3 );
 					uint32* vtx_i                = (uint32*)ch_i->data;
-					mesh->m_idata                = vtx_i;
 					uint32 idx = 0;
 					mesh->add_channel( ch_i );
@@ -190,5 +192,5 @@
 			}
 
-			prepare_mesh( mesh, weights.data(), weight_info.data() );
+			prepare_mesh( weight_info.size(), mesh, weights.data(), weight_info.data() );
 
 			m_meshes[ m_num_meshes ] = mesh;
@@ -201,15 +203,8 @@
 }
 
-bool md5_loader::prepare_mesh( md5_mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info )
-{
-	uint32 vtx_count = mdata->m_vtx_data.size();
-	md5_vtx_pnt* vtcs = mdata->m_pntdata;
-
-	mdata->m_bone_offset.resize( m_joints.size() );
-	for ( uint32 i = 0; i < m_joints.size(); ++i )
-	{
-		transform j( m_joints[i].pos, m_joints[i].orient );
-		mdata->m_bone_offset[i] = j.inverse();
-	}
+bool md5_loader::prepare_mesh( uint32 vtx_count, mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info )
+{
+	md5_vtx_pnt* vtcs = (md5_vtx_pnt*)mdata->get_channel< md5_vtx_pnt >()->data; 
+	md5_vtx_pntiw* vtx_data = (md5_vtx_pntiw*)mdata->get_channel< md5_vtx_pntiw >()->data; 
 
 	for ( uint32 i = 0; i < vtx_count; ++i )
@@ -217,5 +212,5 @@
 		size_t start_weight = weight_info[i].start_weight;
 		size_t weight_count = weight_info[i].weight_count;
-		md5_vtx_data& vdata = mdata->m_vtx_data[i];
+		md5_vtx_pntiw& vdata = vtx_data[i];
 		md5_vtx_pnt& vtc = vtcs[i];
 
@@ -268,11 +263,14 @@
 	}
 
+	const uint32*    idata = (uint32*)mdata->get_index_channel()->data;
+	const md5_vtx_t* tdata = mdata->get_channel_data<md5_vtx_t>();
+
 	// Prepare normals
 	uint32 tri_count = mdata->get_count() / 3;
 	for ( unsigned int i = 0; i < tri_count; ++i )
 	{
-		uint32 ti0 = mdata->m_idata[ i * 3 ];
-		uint32 ti1 = mdata->m_idata[ i * 3 + 1 ];
-		uint32 ti2 = mdata->m_idata[ i * 3 + 2 ];
+		uint32 ti0 = idata[ i * 3 ];
+		uint32 ti1 = idata[ i * 3 + 1 ];
+		uint32 ti2 = idata[ i * 3 + 2 ];
  
 		glm::vec3 v1 = vtcs[ ti0 ].position;
@@ -288,7 +286,7 @@
 		vtcs[ ti2 ].normal += normal;
 
-		const vec2& w1 = mdata->m_tdata[ ti0 ].texcoord;
-		const vec2& w2 = mdata->m_tdata[ ti1 ].texcoord;
-		const vec2& w3 = mdata->m_tdata[ ti2 ].texcoord;
+		const vec2& w1 = tdata[ ti0 ].texcoord;
+		const vec2& w2 = tdata[ ti1 ].texcoord;
+		const vec2& w3 = tdata[ ti2 ].texcoord;
 
 		vec2 st1 = w3 - w1;
@@ -306,5 +304,5 @@
 	for ( size_t i = 0; i < vtx_count; ++i )
 	{
-		md5_vtx_data& vdata = mdata->m_vtx_data[i];
+		md5_vtx_pntiw& vdata = vtx_data[i];
 
 		glm::vec3 normal  = glm::normalize( vtcs[i].normal );
@@ -541,8 +539,32 @@
 }
 
-md5_mesh_instance* nv::md5_mesh_data::spawn() const
-{
-	return new md5_mesh_instance( this );
-}
+mesh_nodes_data* nv::md5_loader::release_mesh_nodes_data()
+{
+	mesh_node_data* nodes = new mesh_node_data[ m_num_joints ];
+	for ( uint32 i = 0; i < m_num_joints; ++i )
+	{
+		mesh_node_data& node = nodes[i];
+		node.name      = m_joints[i].name;
+		node.target_id = -1;
+		node.parent_id = -1;
+		node.transform = transform( m_joints[i].pos, m_joints[i].orient ).inverse().extract();
+		node.data      = nullptr;
+	}
+	return new mesh_nodes_data( "nodes", m_num_joints, nodes );
+}
+
+mesh_data_pack* nv::md5_loader::release_mesh_data_pack()
+{
+	uint32 size = m_meshes.size();
+	mesh_data* meshes = new mesh_data[ size ];
+	for ( uint32 i = 0; i < size; ++i )
+	{
+		m_meshes[i]->move_to( meshes[i] );
+		delete m_meshes[i];
+		m_meshes[i] = nullptr;
+	}
+	return new mesh_data_pack( size, meshes, release_mesh_nodes_data() );
+}
+
 
 nv::md5_loader::~md5_loader()
@@ -551,38 +573,2 @@
 }
 
-nv::md5_mesh_instance::md5_mesh_instance( const md5_mesh_data* a_data ) 
-	: m_data( a_data ), m_indices( 0 )
-{
-	m_indices = m_data->get_count();
-	m_pntdata.assign( m_data->m_pntdata, m_data->m_vtx_data.size() );
-}
-
-void nv::md5_mesh_instance::apply( const transform* skeleton )
-{
-	NV_PROFILE("md5::apply");
-	size_t skeleton_size = m_data->m_bone_offset.size();
-	size_t vertex_count  = m_pntdata.size();
-	m_pos_offset.resize( skeleton_size );
-	for ( unsigned int i = 0; i < skeleton_size; ++i )
-	{
-		m_pos_offset[i] = skeleton[i] * m_data->m_bone_offset[i];
-	}
-
-	std::fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
-	for ( unsigned int i = 0; i < vertex_count; ++i )
-	{
-		const md5_vtx_data& vert = m_data->m_vtx_data[i];
-		md5_vtx_pnt& result = m_pntdata[i];
-
-		for ( size_t j = 0; j < 4; ++j )
-		{
-			int   index  = vert.boneindex[j];
-			float weight = vert.boneweight[j];
-			const quat& orient      = skeleton[index].get_orientation();
-			const transform& offset = m_pos_offset[index];
-			result.position += offset.transformed( vert.position )        * weight;
-			result.normal   += ( orient * vert.normal  ) * weight;
-			result.tangent  += ( orient * vert.tangent ) * weight;
-		}
-	}
-}
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 285)
+++ trunk/src/formats/nmd_loader.cc	(revision 287)
@@ -7,4 +7,5 @@
 #include "nv/formats/nmd_loader.hh"
 #include "nv/io/std_stream.hh"
+#include "nv/string.hh"
 
 using namespace nv;
@@ -24,5 +25,4 @@
 		case nmd_type::MESH           : load_mesh( source, element_header ); break;
 		case nmd_type::ANIMATION      : load_animation( source, element_header ); break;
-		case nmd_type::BONE_ARRAY     : load_bones( source, element_header ); break;
 		case nmd_type::STRING_TABLE   : load_strings( source ); break;
 		default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
@@ -47,4 +47,5 @@
 		mesh->add_channel( channel );
 	}
+	m_mesh_names.push_back( e.name );
 	m_meshes.push_back( mesh );
 	return true;
@@ -54,6 +55,20 @@
 {
 	mesh_data* result = m_meshes[ index ];
+	if ( m_strings ) result->set_name( m_strings->get( m_mesh_names[ index ] ) );
 	m_meshes[ index ] = nullptr;
 	return result;
+}
+
+mesh_data_pack* nv::nmd_loader::release_mesh_data_pack()
+{
+	uint32 size = m_meshes.size();
+	mesh_data* meshes = new mesh_data[ size ];
+	for ( uint32 i = 0; i < size; ++i )
+	{
+		m_meshes[i]->move_to( meshes[i] );
+		delete m_meshes[i];
+	}
+	m_meshes.clear();
+	return new mesh_data_pack( size, meshes, release_mesh_nodes_data() );
 }
 
@@ -61,11 +76,13 @@
 {
 	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;
+	if ( m_node_data ) delete m_node_data;
 	m_meshes.clear();
-	m_animation = nullptr;
-	m_bone_data = nullptr;
-	m_strings   = nullptr;
+	m_mesh_names.clear();
+	m_node_names.clear();
+
+	m_node_data  = nullptr;
+	m_node_array = nullptr;
+	m_strings    = nullptr;
 }
 
@@ -82,70 +99,32 @@
 }
 
-bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
-{
-	NV_ASSERT( m_bone_data == nullptr, "MULTIPLE BONE ENTRIES!" );
-	m_bone_data = new nmd_bone_data;
-	m_bone_data->bones = new nmd_bone[ e.children ];
-	m_bone_data->count = (uint16)e.children;
-	source.read( m_bone_data->bones, sizeof( nmd_bone ), e.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, const nmd_element_header& e )
 {
-	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)e.children;
-	m_animation->nodes      = new nmd_node[ e.children ];
+	NV_ASSERT( m_node_data == nullptr, "MULTIPLE NODE ENTRIES!" );
+	nmd_animation_header animation_header;
+	source.read( &animation_header, sizeof( animation_header ), 1 );
+	m_node_array = new mesh_node_data[ e.children ];
 	for ( uint32 i = 0; i < e.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!" );
-		m_animation->nodes[i].name          = element_header.name;
-
+		NV_ASSERT( element_header.type == nmd_type::NODE, "NODE expected!" );
+		m_node_names.push_back( element_header.name );
 		uint16 ch_count = element_header.children;
 
-		nmd_animation_node_header node_header;
+		nmd_node_header node_header;
 		source.read( &node_header, sizeof( node_header ), 1 );
-		m_animation->nodes[i].parent_id     = node_header.parent_id;
-		m_animation->nodes[i].transform     = node_header.transform;
-		m_animation->nodes[i].data          = nullptr;
+		m_node_array[i].parent_id     = node_header.parent_id;
+		m_node_array[i].transform     = node_header.transform;
+		m_node_array[i].data          = nullptr;
 		if ( ch_count > 0 )
 		{
 			key_data* kdata = new key_data;
-			m_animation->nodes[i].data = kdata;
+			m_node_array[i].data = kdata;
 			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;
+				NV_ASSERT( element_header.type == nmd_type::KEY_CHANNEL, "CHANNEL expected!" );
+				nv::nmd_key_channel_header cheader;
 				source.read( &cheader, sizeof( cheader ), 1 );
 				key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count );
@@ -155,116 +134,30 @@
 		}
 	}
+	m_node_data = new mesh_nodes_data( "animation", e.children, m_node_array, animation_header.frame_rate, animation_header.duration, animation_header.flat );
 	return true;
 }
 
+mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data()
+{
+	if ( m_node_data )
+	{
+		if ( m_strings )
+		{
+			for ( uint32 i = 0; i < m_node_data->get_count(); ++i )
+			{
+				m_node_array[i].name = m_strings->get( m_node_names[i] );
+			}
+		}
+		mesh_nodes_data* result = m_node_data;
+		m_node_data = nullptr;
+		m_node_array = nullptr;
+		return result;
+	}
+	return nullptr;
+}
 
 // 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];
-		m_data.push_back( node.data );
-		node.data = nullptr;
-	}
-
-	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] )
-			{
-				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 ] )
-	{
-		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 )
+nv::nmd_temp_model_data::nmd_temp_model_data( nmd_loader* loader )
 {
 	for ( unsigned m = 0; m < loader->get_mesh_count(); ++m )
@@ -272,18 +165,8 @@
 		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;
+	m_node_data = loader->release_mesh_nodes_data();
 }
 
-nv::nmd_temp_model::~nmd_temp_model()
+nv::nmd_temp_model_data::~nmd_temp_model_data()
 {
 	for ( unsigned m = 0; m < m_mesh_data.size(); ++m )
@@ -291,3 +174,4 @@
 		delete m_mesh_data[m];
 	}
+	delete m_node_data;
 }
Index: trunk/src/formats/obj_loader.cc
===================================================================
--- trunk/src/formats/obj_loader.cc	(revision 285)
+++ trunk/src/formats/obj_loader.cc	(revision 287)
@@ -50,4 +50,6 @@
 	std::string line;
 	std::string cmd;
+	std::string name;
+	std::string next_name;
 
 	std::size_t size;
@@ -77,4 +79,5 @@
 bool obj_reader::read_stream( std::istream& stream )
 {
+	name = next_name;
 	bool added_faces = false;
 	f32 x, y, z;
@@ -146,5 +149,8 @@
 		if ( cmd == "g" )
 		{
-			if (added_faces) return true;
+			ss >> next_name;
+			if (added_faces) 
+				return true;
+			name = next_name;
 			continue;
 		}
@@ -335,5 +341,5 @@
 		channel->count = reader->size * 3;
 
-		mesh_data* mesh = new mesh_data();
+		mesh_data* mesh = new mesh_data(reader->name);
 		mesh->add_channel( channel );
 		m_meshes.push_back( mesh );
@@ -357,2 +363,15 @@
 	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
 }
+
+mesh_data_pack* nv::obj_loader::release_mesh_data_pack()
+{
+	uint32 size = m_meshes.size();
+	mesh_data* meshes = new mesh_data[ size ];
+	for ( uint32 i = 0; i < size; ++i )
+	{
+		m_meshes[i]->move_to( meshes[i] );
+		delete m_meshes[i];
+	}
+	m_meshes.clear();
+	return new mesh_data_pack( size, meshes );
+}
