Index: trunk/nv/formats/assimp_loader.hh
===================================================================
--- trunk/nv/formats/assimp_loader.hh	(revision 284)
+++ trunk/nv/formats/assimp_loader.hh	(revision 285)
@@ -42,18 +42,13 @@
 		sint32                parent_id;
 		mat4                  transform;
-		uint16                channel_count;
-		key_raw_channel*      channels[4];
+		key_data*             data;
 
 		assimp_animated_node_data() 
-			: name(), parent_id( -1 )
+			: name(), parent_id( -1 ), data( nullptr )
 		{
-			channels[0] = nullptr; channels[1] = nullptr; channels[2] = nullptr; channels[3] = nullptr;
 		}
 		~assimp_animated_node_data() 
 		{
-			if ( channels[0] ) delete channels[0];
-			if ( channels[1] ) delete channels[1];
-			if ( channels[2] ) delete channels[2];
-			if ( channels[3] ) delete channels[3];
+			if ( data ) delete data;
 		}
 	};
Index: trunk/nv/formats/md2_loader.hh
===================================================================
--- trunk/nv/formats/md2_loader.hh	(revision 284)
+++ trunk/nv/formats/md2_loader.hh	(revision 285)
@@ -29,8 +29,8 @@
 		virtual bool load( stream& source );
 		virtual mesh_data* release_mesh_data( size_t index = 0 );
+		virtual size_t get_mesh_count() const { return 1; }
+	private:
 		virtual mesh_data* release_mesh_frame( sint32 frame );
-		virtual size_t get_mesh_count() const { return 1; }
 		size_t get_max_frames() const;
-	private:
 		void reindex();
 	private:
Index: trunk/nv/formats/md3_loader.hh
===================================================================
--- trunk/nv/formats/md3_loader.hh	(revision 284)
+++ trunk/nv/formats/md3_loader.hh	(revision 285)
@@ -40,4 +40,6 @@
 		size_t get_max_frames() const;
 		virtual tag_map* create_tag_map();
+		virtual mesh_node_data* release_mesh_node_data( size_t index );
+		virtual size_t get_node_count() const;
 	private:
 		key_raw_channel* load_tags( const std::string& tag );
Index: trunk/nv/formats/nmd_loader.hh
===================================================================
--- trunk/nv/formats/nmd_loader.hh	(revision 284)
+++ trunk/nv/formats/nmd_loader.hh	(revision 285)
@@ -78,6 +78,5 @@
 		sint16 parent_id;
 		mat4   transform;
-		uint16 channel_count;
-		key_raw_channel** channels;
+		key_data* data;
 	};
 
@@ -91,19 +90,10 @@
 
 		nmd_animation() : node_count(0), nodes( nullptr ) {}
-		void clear_node( nmd_node* node )
-		{
-			if ( node->channel_count > 0 )
-			{
-				for ( uint16 c = 0; c < node->channel_count; ++c )
-					delete node->channels[c];
-				delete[] node->channels;
-			}
-			node->channels = nullptr;
-			node->channel_count = 0;
-		}
 		~nmd_animation()
 		{
 			for ( uint16 n = 0; n < node_count; ++n )
-				clear_node( &nodes[n] );
+			{
+				if ( nodes[n].data ) delete nodes[n].data;
+			}
 			delete[] nodes;
 		}
@@ -149,11 +139,4 @@
 
 	// TODO: Temporary, find a better way and remove!
-	struct nmd_temp_node_data
-	{
-		key_animation_data* keys;
-		mat4                transform;
-		sint32              bone_id;
-		std::vector< nmd_temp_node_data* > children;
-	};
 
 	class nmd_temp_model;
@@ -171,5 +154,5 @@
 		void animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat );
 
-		std::vector< key_animation_data* > m_data;
+		std::vector< key_data* > m_data;
 		std::vector< sint16 > m_bone_ids;
 		std::vector< std::vector< uint32 > > m_children;
Index: trunk/nv/gfx/animation.hh
===================================================================
--- trunk/nv/gfx/animation.hh	(revision 284)
+++ trunk/nv/gfx/animation.hh	(revision 285)
@@ -52,10 +52,21 @@
 		}
 
+		uint32 get_raw( uint32 index, float* result ) const 
+		{
+			if ( count == 0 ) return 0;
+			uint32 keyfsize   = desc.size / 4;
+			const float* fdata = ((const float*)data) + keyfsize * index;
+			uint32 mod        = 0;
+			if ( desc.slots[0].vslot == animation_slot::TIME ) mod = 1;
+			std::copy_n( fdata + mod, keyfsize - mod, result );
+			return keyfsize - mod;
+		}
+
 		uint32 interpolate_raw( float time, float* result ) const 
 		{
+			if ( count == 0 ) return 0;
 			uint32 keyfsize   = desc.size / 4;
 			uint32 keyfresult = keyfsize;
 			const float* fdata = (const float*)data;
-			if ( count == 0 ) return 0;
 
 			uint32 slot = 0;
@@ -129,5 +140,5 @@
 		}
 
-		mat4 get_matrix( float time ) const
+		mat4 get_raw_matrix( uint32 index ) const 
 		{
 			float key[ 16 ];
@@ -135,10 +146,10 @@
 			for ( uint16 i = 0; i < m_channels.size(); ++i )
 			{
-				pkey += m_channels[i]->interpolate_raw( time, pkey );
+				pkey += m_channels[i]->get_raw( index, pkey );
 			}
 			return extract_matrix_raw( final_key, key );
 		};
 
-		transform get_transform( float time ) const
+		transform get_raw_transform( uint32 index ) const 
 		{
 			float key[ 16 ];
@@ -146,8 +157,33 @@
 			for ( uint16 i = 0; i < m_channels.size(); ++i )
 			{
+				pkey += m_channels[i]->get_raw( index, pkey );
+			}
+			return extract_transform_raw( final_key, key );
+		};
+
+		mat4 get_matrix( float time ) const
+		{
+			float key[ 16 ];
+			float* pkey = key;
+			for ( uint16 i = 0; i < m_channels.size(); ++i )
+			{
 				pkey += m_channels[i]->interpolate_raw( time, pkey );
 			}
+			return extract_matrix_raw( final_key, key );
+		};
+
+		transform get_transform( float time ) const
+		{
+			float key[ 16 ];
+			float* pkey = key;
+			for ( uint16 i = 0; i < m_channels.size(); ++i )
+			{
+				pkey += m_channels[i]->interpolate_raw( time, pkey );
+			}
 			return extract_transform_raw( final_key, key );
 		};
+
+		size_t get_channel_count() const { return m_channels.size(); }
+		const key_raw_channel* get_channel( size_t index ) const { return m_channels[ index ]; }
 
 		virtual ~key_data()
@@ -169,7 +205,7 @@
 	public:
 		key_channel_interpolator() : m_data( nullptr ) {}
-		key_channel_interpolator( key_raw_channel* data ) : m_data( nullptr ) { set_data( data ); }
-		key_channel_interpolator( key_raw_channel* data, bool ) : m_data( data ) {}
-		void set_data( key_raw_channel* data )
+		key_channel_interpolator( const key_raw_channel* data ) : m_data( nullptr ) { set_data( data ); }
+		key_channel_interpolator( const key_raw_channel* data, bool ) : m_data( data ) {}
+		void set_data( const key_raw_channel* data )
 		{
 			m_data = data;
@@ -194,5 +230,5 @@
 
 	private:
-		key_raw_channel* m_data;
+		const key_raw_channel* m_data;
 	};
  
@@ -202,6 +238,6 @@
 	public:
 		key_channel_interpolator() : m_data( nullptr ) {}
-		key_channel_interpolator( key_raw_channel* data ) : m_data( nullptr ) { set_data( data ); }
-		void set_data( key_raw_channel* data )
+		key_channel_interpolator( const key_raw_channel* data ) : m_data( nullptr ) { set_data( data ); }
+		void set_data( const key_raw_channel* data )
 		{
 			m_data = data;
@@ -233,5 +269,5 @@
 
 	private:
-		key_raw_channel* m_data;
+		const key_raw_channel* m_data;
 	};
  
@@ -260,5 +296,5 @@
 		struct key_s { float time; vec3 scale; };
 	public:
-		explicit key_vectors_prs( key_raw_channel* p, key_raw_channel* r, key_raw_channel* s )
+		explicit key_vectors_prs( const key_raw_channel* p, const key_raw_channel* r, const key_raw_channel* s )
 		{
 			m_pchannel = p;
@@ -304,7 +340,7 @@
 		}
 	protected:
-		key_raw_channel* m_pchannel;
-		key_raw_channel* m_rchannel;
-		key_raw_channel* m_schannel;
+		const key_raw_channel* m_pchannel;
+		const key_raw_channel* m_rchannel;
+		const key_raw_channel* m_schannel;
 		key_channel_interpolator< key_p, true > m_pinter;
 		key_channel_interpolator< key_r, true > m_rinter;
@@ -319,5 +355,5 @@
 		};
 	public:
-		explicit transform_vector( key_raw_channel* channel ) 
+		explicit transform_vector( const key_raw_channel* channel ) 
 		{
 			key_descriptor kd;
@@ -354,5 +390,5 @@
 	protected:
 		key_channel_interpolator< key, false > m_interpolator;
-		key_raw_channel* m_channel;
+		const key_raw_channel* m_channel;
 	};
 
Index: trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- trunk/nv/gfx/keyframed_mesh.hh	(revision 284)
+++ trunk/nv/gfx/keyframed_mesh.hh	(revision 285)
@@ -41,5 +41,5 @@
 		virtual void run_animation( animation_entry* a_anim );
 		size_t get_max_frames() const;
-		transform get_tag( const std::string& tag ) const;
+		transform get_tag( uint32 tag_id ) const;
 		virtual void setup_animation( uint32 start, uint32 count, uint32 fps, bool loop );
 		virtual void set_frame( uint32 frame );
Index: trunk/nv/interface/animated_mesh.hh
===================================================================
--- trunk/nv/interface/animated_mesh.hh	(revision 284)
+++ trunk/nv/interface/animated_mesh.hh	(revision 285)
@@ -41,5 +41,6 @@
 		virtual void update_animation( animation_entry*, uint32 ) {}
 		virtual void run_animation( animation_entry* ) {}
-		virtual transform get_tag( const std::string& ) const { return transform(); }
+		virtual transform get_tag( uint32 ) const { return transform(); }
+
 	};
 
Index: trunk/nv/interface/mesh_loader.hh
===================================================================
--- trunk/nv/interface/mesh_loader.hh	(revision 284)
+++ trunk/nv/interface/mesh_loader.hh	(revision 285)
@@ -30,26 +30,39 @@
 	{
 	public:
-		typedef std::unordered_map< std::string, key_raw_channel* > map;
+		typedef std::unordered_map< std::string, uint32 > map;
 
-		tag_map () {}
+		tag_map() {}
 
-		const transform* get_tag( const std::string& key ) const
+		const key_data* get_tag( uint32 id ) const
 		{
-			key_raw_channel* channel = m_map.at( key );
-			return ((transform*)(channel->data));
+			return m_data[id];
 		}
-		void insert( const std::string& key, key_raw_channel* chan )
+		uint32 get_tag_id( const std::string& key ) const
 		{
-			m_map[ key ] = chan;
+			return m_map.at( key );
+		}
+		void insert( const std::string& key, key_data* chan )
+		{
+			m_map[ key ] = m_data.size();
+			m_data.push_back( chan );
 		}
 		~tag_map()
 		{
-			for ( auto t : m_map )
-				delete t.second;
+			for ( auto t : m_data )
+				delete t;
 		}
 	private:
+		std::vector< key_data* > m_data;
 		map m_map;
 	};
 
+	struct mesh_node_data
+	{
+		string    name;
+		sint16    target_id;
+		sint16    parent_id;
+		mat4      transform;
+		key_data* data;
+	};
 
 	class mesh_loader
@@ -62,4 +75,6 @@
 		virtual mesh_data* release_mesh_data( size_t index = 0 ) = 0;
 		virtual size_t get_mesh_count() const = 0;
+		virtual mesh_node_data* release_mesh_node_data( size_t ) { return nullptr; }
+		virtual size_t get_node_count() const { return 0; }
 	};
 
Index: trunk/nv/interface/vertex.hh
===================================================================
--- trunk/nv/interface/vertex.hh	(revision 284)
+++ trunk/nv/interface/vertex.hh	(revision 285)
@@ -479,5 +479,5 @@
 		}
 
-		bool operator==( const key_descriptor& rhs )
+		bool operator==( const key_descriptor& rhs ) const
 		{
 			if ( size  != rhs.size )  return false;
Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 284)
+++ trunk/src/formats/assimp_loader.cc	(revision 285)
@@ -367,5 +367,5 @@
 	if (this_id == 0)
 		a_data.transform = mat4();
-	a_data.channel_count = 0;
+	a_data.data = nullptr;
 
 	if (anode) 
@@ -394,7 +394,8 @@
 	size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
 
-	data->channel_count = 1;
-	data->channels[0] = key_raw_channel::create<assimp_key_tr>( max_keys );
-	assimp_key_tr* channel = ((assimp_key_tr*)(data->channels[0]->data));
+	key_raw_channel* raw_channel = key_raw_channel::create<assimp_key_tr>( max_keys );
+	data->data = new key_data;
+	data->data->add_channel( raw_channel );
+	assimp_key_tr* channel = ((assimp_key_tr*)(raw_channel->data));
 
 	for ( unsigned n = 0; n < max_keys; ++n )
@@ -406,10 +407,10 @@
 		// TODO: only do the calculation when a rotate transform is present!
 		nv::transform ptr;
-		if ( parent )
-		{
-			key_raw_channel* pchannel = parent->channels[0];
-			if ( parent->channels[0] && parent->channels[0]->count > 0 )
-			{
-				ptr = ((assimp_key_tr*)pchannel->data)[ glm::min( n, parent->channels[0]->count-1 ) ].tform;
+		if ( parent && parent->data )
+		{
+			const key_raw_channel* pchannel = parent->data->get_channel(0);
+			if ( pchannel && pchannel->count > 0 )
+			{
+				ptr = ((assimp_key_tr*)pchannel->data)[ glm::min( n, pchannel->count-1 ) ].tform;
 			}
 		}
@@ -422,5 +423,4 @@
 void nv::assimp_loader::create_direct_keys( assimp_animated_node_data* data, const void* vnode )
 {
-	data->channel_count = 0;
 	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
 	if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 )
@@ -429,11 +429,14 @@
 	}
 
-	data->channel_count = 3;
-	data->channels[0] = key_raw_channel::create<assimp_key_p>( node->mNumPositionKeys );
-	data->channels[1] = key_raw_channel::create<assimp_key_r>( node->mNumRotationKeys );
-	data->channels[2] = key_raw_channel::create<assimp_key_s>( node->mNumScalingKeys );
-	assimp_key_p* pchannel = ((assimp_key_p*)(data->channels[0]->data));
-	assimp_key_r* rchannel = ((assimp_key_r*)(data->channels[1]->data));
-	assimp_key_s* schannel = ((assimp_key_s*)(data->channels[2]->data));
+	data->data = new key_data;
+	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 );
+	data->data->add_channel( raw_pchannel );
+	data->data->add_channel( raw_rchannel );
+	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));
 
 	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
Index: trunk/src/formats/md3_loader.cc
===================================================================
--- trunk/src/formats/md3_loader.cc	(revision 284)
+++ trunk/src/formats/md3_loader.cc	(revision 285)
@@ -407,12 +407,37 @@
 	for ( sint32 i = 0; i < md3->header.num_tags; ++i )
 	{
-		const md3_tag_t& rtag = md3->tags[i + md3->header.num_tags];
+		const md3_tag_t& rtag = md3->tags[i];
 		std::string name( (char*)(rtag.name) );
-		nv::key_raw_channel* keys = load_tags( name );
-		result->insert( name, keys );
+
+		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;
+	
+	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;
+}
+
 size_t md3_loader::get_max_frames() const
 {
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 284)
+++ trunk/src/formats/nmd_loader.cc	(revision 285)
@@ -138,9 +138,9 @@
 		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;
+		m_animation->nodes[i].data          = nullptr;
 		if ( ch_count > 0 )
 		{
-			m_animation->nodes[i].channels      = new key_raw_channel* [ch_count];
+			key_data* kdata = new key_data;
+			m_animation->nodes[i].data = kdata;
 			for ( uint32 c = 0; c < ch_count; ++c )
 			{
@@ -151,5 +151,5 @@
 				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;
+				kdata->add_channel( channel );
 			}
 		}
@@ -168,16 +168,6 @@
 	{
 		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 );
+		m_data.push_back( node.data );
+		node.data = nullptr;
 	}
 
@@ -239,5 +229,5 @@
 			nv::mat4 node_mat( node->transform );
 
-			if ( m_data[n] && !m_data[n]->empty() )
+			if ( m_data[n] )
 			{
 				node_mat = m_data[n]->get_matrix( anim_time );
@@ -257,5 +247,5 @@
 	mat4 node_mat( node->transform );
 
-	if ( m_data[ node_id ] && !m_data[ node_id ]->empty() )
+	if ( m_data[ node_id ] )
 	{
 		node_mat = m_data[ node_id ]->get_matrix( time );
Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 284)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 285)
@@ -40,10 +40,12 @@
 }
 
-transform keyframed_mesh::get_tag( const std::string& tag ) const
+transform keyframed_mesh::get_tag( uint32 tag ) const
 {
 	NV_ASSERT( m_tag_map, "TAGMAP FAIL" );
-	const transform* transforms = m_tag_map->get_tag( tag );
-	NV_ASSERT( transforms, "TAG FAIL" );
-	return interpolate( transforms[ m_last_frame ], transforms[ m_next_frame ], m_interpolation  );
+	const key_data* data = m_tag_map->get_tag( tag );
+	NV_ASSERT( data, "TAG FAIL" );
+	transform last = data->get_raw_transform( m_last_frame );
+	transform next = data->get_raw_transform( m_next_frame );
+	return interpolate( last, next, m_interpolation  );
 }
 
