Index: trunk/nv/gfx/animation.hh
===================================================================
--- trunk/nv/gfx/animation.hh	(revision 418)
+++ trunk/nv/gfx/animation.hh	(revision 419)
@@ -20,52 +20,46 @@
 {
 
-	class key_channel_set : public data_channel_set
+	class raw_channel_interpolator
 	{
 	public:
-		mat4 get_raw_matrix( uint32 index ) const 
-		{
-			float key[ 16 ];
+		explicit raw_channel_interpolator( const data_channel_set* set )
+			: m_set( set ), m_interpolation_key( set->get_interpolation_key() )
+		{
+		}
+		raw_channel_interpolator( const data_channel_set* set, const data_descriptor& ikey )
+			: m_set( set ), m_interpolation_key( ikey )
+		{ }
+
+		template < typename T >
+		T get_raw( uint32 index ) const
+		{
+			float key[16];
 			float* pkey = key;
-			for ( uint16 i = 0; i < size(); ++i )
-			{
-				pkey += get_raw( m_channels[i], index, pkey );
-			}
-			return extract_matrix_raw( m_final_key, key );
-		}
-
-		transform get_raw_transform( uint32 index ) const 
-		{
-			float key[ 16 ];
+			for ( const auto& channel : *m_set )
+			{
+				pkey += get_raw( channel, index, pkey );
+			}
+			return extract_key_raw< T >( m_interpolation_key, key );
+		}
+
+		template < typename T >
+		T get( float time ) const
+		{
+			float key[16];
 			float* pkey = key;
-			for ( uint16 i = 0; i < size(); ++i )
-			{
-				pkey += get_raw( m_channels[i], index, pkey );
-			}
-			return extract_transform_raw( m_final_key, key );
-		}
-
-		mat4 get_matrix( float time ) const
-		{
-			float key[ 16 ];
-			float* pkey = key;
-			for ( uint16 i = 0; i < size(); ++i )
-			{
-				pkey += interpolate_raw( m_channels[i], time, pkey );
-			}
-			return extract_matrix_raw( m_final_key, key );
-		}
-
-		transform get_transform( float time ) const
-		{
-			float key[ 16 ];
-			float* pkey = key;
-			for ( uint16 i = 0; i < size(); ++i )
-			{
-				pkey += interpolate_raw( m_channels[i], time, pkey );
-			}
-			return extract_transform_raw( m_final_key, key );
-		}
-
-		const data_descriptor& get_final_key() const { return m_final_key; }
+			for ( const auto& channel : *m_set )
+			{
+				pkey += interpolate_raw( channel, time, pkey );
+			}
+			return extract_key_raw< T >( m_interpolation_key, key );
+		}
+
+		template < typename T >
+		T  get( uint32 index1, uint32 index2, float interpolation ) const
+		{
+			T t1 = get_raw< T >( index1 );
+			T t2 = get_raw< T >( index2 );
+			return interpolate( t1, t2, interpolation );
+		}
 
 		static uint32 get_raw( const raw_data_channel& channel, uint32 index, float* result )
@@ -138,91 +132,88 @@
 			return ret;
 		}
-
-	private:
-		friend class key_channel_set_creator;
-
-		key_channel_set() {}
-
-		data_descriptor m_final_key;
+		const data_descriptor& get_interpolation_key() { return m_interpolation_key; }
+	protected:
+		const data_channel_set* m_set;
+		data_descriptor m_interpolation_key;
 	};
 
-	template < typename KEY, bool TIMED >
-	class key_channel_interpolator;
-
-
- 	template < typename KEY >
-	class key_channel_interpolator< KEY, false >
-	{
-	public:
-		key_channel_interpolator() : m_data( nullptr ) {}
-		key_channel_interpolator( const raw_data_channel* data ) : m_data( nullptr ) { set_data( data ); }
-		key_channel_interpolator( const raw_data_channel* data, bool ) : m_data( data ) {}
-		void set_data( const raw_data_channel* data )
-		{
-			m_data = data;
-			data_descriptor desc;
-			desc.initialize<KEY>();
-			NV_ASSERT( data->descriptor() == desc, "Bad channel passed!" );
-		}
-		void get_interpolated( KEY& result, float frame ) const
-		{
-			NV_ASSERT( m_data, "Data is null!" );
-			const KEY* keys = m_data->data_cast<KEY>( );
-			uint32 count = m_data->size();
-			if ( count == 0 ) return;
-			if ( count == 1 )
-			{
-				result = keys[0];
-				return;
-			}
-			size_t index = glm::clamp<size_t>( size_t( frame ), 0, count - 2 );
-			float factor = glm::clamp<float> ( frame - index, 0.0f, 1.0f );
-			interpolate_key( result, keys[index], keys[index+1], factor );
-		}
-
-	private:
-		const raw_data_channel* m_data;
-	};
- 
- 	template < typename KEY >
- 	class key_channel_interpolator< KEY, true >
-	{
-	public:
-		key_channel_interpolator() : m_data( nullptr ) {}
-		key_channel_interpolator( const raw_data_channel* data ) : m_data( nullptr ) { set_data( data ); }
-		void set_data( const raw_data_channel* data )
-		{
-			m_data = data;
-			data_descriptor desc;
-			desc.initialize<KEY>();
-			NV_ASSERT( data->descriptor() == desc, "Bad channel passed!" );
-		}
-		void get_interpolated( KEY& result, float time ) const
-		{
-			// TODO: this probably could be optimized
-			NV_ASSERT( m_data, "Data is null!" );
-			const KEY* keys = m_data->data_cast<KEY>( );
-			uint32 count = m_data->size();
-			if ( count == 0 ) return;
-			if ( count == 1 ) 
-			{
-				result = keys[0];
-				return;
-			}
-			int index = -1;
-			for ( int i = 0 ; i < int( count ) - 1 ; i++ )
-			{
-				if ( time < keys[i + 1].time ) { index = i; break; }
-			}
-			NV_ASSERT( index >= 0, "animation time fail!");
-			float delta  = keys[index + 1].time - keys[index].time;
-			float factor = glm::clamp( (time - keys[index].time) / delta, 0.0f, 1.0f );
-			interpolate_key( result, keys[index], keys[index+1], factor );
-		}
-
-	private:
-		const raw_data_channel* m_data;
-	};
- 
+// 	template < typename KEY, bool TIMED >
+// 	class key_channel_interpolator;
+// 
+// 
+//  	template < typename KEY >
+// 	class key_channel_interpolator< KEY, false >
+// 	{
+// 	public:
+// 		key_channel_interpolator() : m_data( nullptr ) {}
+// 		key_channel_interpolator( const raw_data_channel* data ) : m_data( nullptr ) { set_data( data ); }
+// 		key_channel_interpolator( const raw_data_channel* data, bool ) : m_data( data ) {}
+// 		void set_data( const raw_data_channel* data )
+// 		{
+// 			m_data = data;
+// 			data_descriptor desc;
+// 			desc.initialize<KEY>();
+// 			NV_ASSERT( data->descriptor() == desc, "Bad channel passed!" );
+// 		}
+// 		void get_interpolated( KEY& result, float frame ) const
+// 		{
+// 			NV_ASSERT( m_data, "Data is null!" );
+// 			const KEY* keys = m_data->data_cast<KEY>( );
+// 			uint32 count = m_data->size();
+// 			if ( count == 0 ) return;
+// 			if ( count == 1 )
+// 			{
+// 				result = keys[0];
+// 				return;
+// 			}
+// 			size_t index = glm::clamp<size_t>( size_t( frame ), 0, count - 2 );
+// 			float factor = glm::clamp<float> ( frame - index, 0.0f, 1.0f );
+// 			interpolate_key( result, keys[index], keys[index+1], factor );
+// 		}
+// 
+// 	private:
+// 		const raw_data_channel* m_data;
+// 	};
+//  
+//  	template < typename KEY >
+//  	class key_channel_interpolator< KEY, true >
+// 	{
+// 	public:
+// 		key_channel_interpolator() : m_data( nullptr ) {}
+// 		key_channel_interpolator( const raw_data_channel* data ) : m_data( nullptr ) { set_data( data ); }
+// 		void set_data( const raw_data_channel* data )
+// 		{
+// 			m_data = data;
+// 			data_descriptor desc;
+// 			desc.initialize<KEY>();
+// 			NV_ASSERT( data->descriptor() == desc, "Bad channel passed!" );
+// 		}
+// 		void get_interpolated( KEY& result, float time ) const
+// 		{
+// 			// TODO: this probably could be optimized
+// 			NV_ASSERT( m_data, "Data is null!" );
+// 			const KEY* keys = m_data->data_cast<KEY>( );
+// 			uint32 count = m_data->size();
+// 			if ( count == 0 ) return;
+// 			if ( count == 1 ) 
+// 			{
+// 				result = keys[0];
+// 				return;
+// 			}
+// 			int index = -1;
+// 			for ( int i = 0 ; i < int( count ) - 1 ; i++ )
+// 			{
+// 				if ( time < keys[i + 1].time ) { index = i; break; }
+// 			}
+// 			NV_ASSERT( index >= 0, "animation time fail!");
+// 			float delta  = keys[index + 1].time - keys[index].time;
+// 			float factor = glm::clamp( (time - keys[index].time) / delta, 0.0f, 1.0f );
+// 			interpolate_key( result, keys[index], keys[index+1], factor );
+// 		}
+// 
+// 	private:
+// 		const raw_data_channel* m_data;
+// 	};
+//  
 // 	template < typename KEY1, typename KEY2 = void, typename KEY3 = void >
 // 	class key_data_interpolator
@@ -230,122 +221,60 @@
 // 
 // 	};
-
-	class key_animation_data
-	{
-	public:
-		virtual mat4 get_matrix( float time ) const = 0;
-		virtual transform get_transform( float time ) const = 0;
-		virtual bool empty() const = 0;
-		virtual size_t size() const = 0;
-		virtual uint32 raw_size() const = 0;
-		virtual ~key_animation_data() {}
-	};
-
-
-	class key_vectors_prs : public key_animation_data
-	{
-		struct key_p { float time; vec3 position; };
-		struct key_r { float time; quat rotation; };
-		struct key_s { float time; vec3 scale; };
-	public:
-		explicit key_vectors_prs( const raw_data_channel* p, const raw_data_channel* r, const raw_data_channel* s )
-		{
-			m_pchannel = p;
-			m_rchannel = r;
-			m_schannel = s;
-			m_pinter.set_data( m_pchannel );
-			m_rinter.set_data( m_rchannel );
-			m_sinter.set_data( m_schannel );
-		}
-		size_t size() const { return 0; } // TODO: remove?
-		bool empty() const { 
-			return m_pchannel->size() == 0
-				&& m_rchannel->size() == 0
-				&& m_schannel->size() == 0; }
-		virtual mat4 get_matrix( float time ) const
-		{
-			key_p p;
-			key_r r;
-			key_s s;
-
-			m_pinter.get_interpolated( p, time );
-			m_rinter.get_interpolated( r, time );
-			m_sinter.get_interpolated( s, time );
-
-			return extract_matrix( p ) * extract_matrix( r ) * extract_matrix( s );
-		}
-		virtual transform get_transform( float time ) const
-		{
-			key_p p;
-			key_r r;
-
-			m_pinter.get_interpolated( p, time );
-			m_rinter.get_interpolated( r, time );
-
-			return transform( p.position, r.rotation );
-		}
-		virtual uint32 raw_size() const 
-		{
-			return 3 * sizeof( size_t ) 
-				+ m_pchannel->size() * sizeof( key_p )
-				+ m_rchannel->size() * sizeof( key_r )
-				+ m_schannel->size() * sizeof( key_s );
-		}
-		~key_vectors_prs()
-		{
-		}
-	protected:
-		const raw_data_channel* m_pchannel;
-		const raw_data_channel* m_rchannel;
-		const raw_data_channel* m_schannel;
-		key_channel_interpolator< key_p, true > m_pinter;
-		key_channel_interpolator< key_r, true > m_rinter;
-		key_channel_interpolator< key_s, true > m_sinter;
-	};
-
-	class transform_vector : public key_animation_data
-	{
-		struct key
-		{
-			transform tform;
-		};
-	public:
-		explicit transform_vector( const raw_data_channel* channel )
-		{
-			data_descriptor kd;
-			kd.initialize<key>();
-			NV_ASSERT( kd == channel->descriptor(), "bad channel!" );
-			m_channel = channel;
-			m_interpolator.set_data( m_channel );
-		}
-
-		~transform_vector()
-		{
-			delete m_channel;
-		}
-		bool empty() const { return m_channel->size() == 0; }
-		size_t size() const { return m_channel->size(); }
-		const transform& get( size_t index ) const { return m_channel->data_cast< key >()[ index ].tform; }
-		const transform* data() const { return &(m_channel->data_cast< key >()[0].tform); }
-
-		virtual uint32 raw_size() const 
-		{
-			return sizeof( size_t ) + m_channel->size() * sizeof( key );
-		}
-
-		virtual mat4 get_matrix( float time ) const
-		{
-			return get_transform( time ).extract();
-		}
-		virtual transform get_transform( float time ) const
-		{
-			key result;
-			m_interpolator.get_interpolated( result, time );
-			return extract_transform< key >( result );
-		}
-	protected:
-		key_channel_interpolator< key, false > m_interpolator;
-		const raw_data_channel* m_channel;
-	};
+// 
+// 	class key_animation_data
+// 	{
+// 	public:
+// 		virtual mat4 get_matrix( float time ) const = 0;
+// 		virtual transform get_transform( float time ) const = 0;
+// 		virtual bool empty() const = 0;
+// 		virtual size_t size() const = 0;
+// 		virtual uint32 raw_size() const = 0;
+// 		virtual ~key_animation_data() {}
+// 	};
+
+// 	class transform_vector : public key_animation_data
+// 	{
+// 		struct key
+// 		{
+// 			transform tform;
+// 		};
+// 	public:
+// 		explicit transform_vector( const raw_data_channel* channel )
+// 		{
+// 			data_descriptor kd;
+// 			kd.initialize<key>();
+// 			NV_ASSERT( kd == channel->descriptor(), "bad channel!" );
+// 			m_channel = channel;
+// 			m_interpolator.set_data( m_channel );
+// 		}
+// 
+// 		~transform_vector()
+// 		{
+// 			delete m_channel;
+// 		}
+// 		bool empty() const { return m_channel->size() == 0; }
+// 		size_t size() const { return m_channel->size(); }
+// 		const transform& get( size_t index ) const { return m_channel->data_cast< key >()[ index ].tform; }
+// 		const transform* data() const { return &(m_channel->data_cast< key >()[0].tform); }
+// 
+// 		virtual uint32 raw_size() const 
+// 		{
+// 			return sizeof( size_t ) + m_channel->size() * sizeof( key );
+// 		}
+// 
+// 		virtual mat4 get_matrix( float time ) const
+// 		{
+// 			return get_transform( time ).extract();
+// 		}
+// 		virtual transform get_transform( float time ) const
+// 		{
+// 			key result;
+// 			m_interpolator.get_interpolated( result, time );
+// 			return extract_transform< key >( result );
+// 		}
+// 	protected:
+// 		key_channel_interpolator< key, false > m_interpolator;
+// 		const raw_data_channel* m_channel;
+// 	};
 
 
Index: trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- trunk/nv/gfx/keyframed_mesh.hh	(revision 418)
+++ trunk/nv/gfx/keyframed_mesh.hh	(revision 419)
@@ -53,4 +53,5 @@
 		const mesh_nodes_data*  m_tag_map;
 
+		data_descriptor m_interpolation_key;
 		buffer       m_pbuffer;
 		vertex_array m_va;
Index: trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- trunk/nv/gfx/skeletal_mesh.hh	(revision 418)
+++ trunk/nv/gfx/skeletal_mesh.hh	(revision 419)
@@ -32,4 +32,5 @@
 		vertex_array m_va;
 		context*     m_context;
+		data_descriptor m_interpolation_key;
 	};
 
@@ -38,10 +39,18 @@
 	public:
 		skeletal_animation_entry_cpu( const std::string& name, const mesh_nodes_data* a_animation, bool a_looping ) 
-			: animation_entry( name, a_looping, a_animation->get_frame_rate(), 0.0f, a_animation->get_duration() ), m_node_data( a_animation ) {}
+			: animation_entry( name, a_looping, a_animation->get_frame_rate(), 0.0f, a_animation->get_duration() ), m_node_data( a_animation )
+		{
+			initialize();
+		}
 		skeletal_animation_entry_cpu( const std::string& name, mesh_nodes_data* a_animation, float time_start, float time_end, bool a_looping )
-			: animation_entry( name, a_looping, a_animation->get_frame_rate(), time_start, time_end ), m_node_data( a_animation ) {}
+			: animation_entry( name, a_looping, a_animation->get_frame_rate(), time_start, time_end ), m_node_data( a_animation )
+		{
+			initialize();
+		}
+		void initialize();
 		void update_skeleton( transform* skeleton, float time ) const;
 	protected:
 		const mesh_nodes_data* m_node_data;
+		data_descriptor m_interpolation_key;
 	};
 
@@ -93,4 +102,5 @@
 		mat4* m_offsets;
 		bool m_prepared;
+		data_descriptor m_interpolation_key;
  	};
 
Index: trunk/nv/interface/data_channel.hh
===================================================================
--- trunk/nv/interface/data_channel.hh	(revision 418)
+++ trunk/nv/interface/data_channel.hh	(revision 419)
@@ -68,5 +68,4 @@
 		friend class data_channel_creator;
 		friend class data_channel_set;
-		friend class key_channel_set_creator;
 
 		raw_data_channel() : m_data( nullptr ), m_size( 0 ) {}
@@ -168,4 +167,16 @@
 		}
 
+		data_descriptor get_interpolation_key() const
+		{
+			data_descriptor result;
+			for ( uint32 c = 0; c < m_size; ++c )
+			{
+				for ( const auto& cslot : m_channels[c].descriptor() )
+					if ( cslot.vslot != slot::TIME )
+					result.push_slot( cslot.etype, cslot.vslot );
+			}
+			return result;
+		}
+
 		const_iterator begin() const { return &m_channels[0]; }
 		const_iterator end()   const { return &m_channels[ m_size ]; }
Index: trunk/nv/interface/data_channel_access.hh
===================================================================
--- trunk/nv/interface/data_channel_access.hh	(revision 418)
+++ trunk/nv/interface/data_channel_access.hh	(revision 419)
@@ -10,5 +10,4 @@
 #include <nv/common.hh>
 #include <nv/interface/data_channel.hh>
-#include <nv/gfx/animation.hh> // TODO: remove, key creator separate file?
 
 namespace nv
@@ -162,54 +161,4 @@
 
 
-	class key_channel_set_creator
-	{
-		typedef data_channel_set::const_iterator const_iterator;
-	public:
-
-		static key_channel_set* create( size_t )
-		{
-			return new key_channel_set;
-		}
-
-		static key_channel_set* create_array( size_t size, size_t )
-		{
-			return new key_channel_set[size];
-		}
-
-		explicit key_channel_set_creator( key_channel_set* set ) : m_set( set ) {}
-
-		template < typename Struct >
-		data_channel_access< Struct > add_channel( uint32 size )
-		{
-			raw_data_channel channel = data_channel_creator::create( data_descriptor::create< Struct >(), size );
-			for ( const auto& cslot : channel.descriptor() )
-				if ( cslot.vslot != slot::TIME )
-				m_set->m_final_key.push_slot( cslot.etype, cslot.vslot );
-			uint32 index = m_set->m_size;
-			m_set->m_channels[index] = move( channel );
-			m_set->m_size++;
-			return data_channel_access< Struct >( &m_set->m_channels[index] );
-		}
-
-		raw_data_channel_access add_channel( const data_descriptor& desc, uint32 size )
-		{
-			raw_data_channel channel = data_channel_creator::create( desc, size );
-			for ( const auto& cslot : channel.descriptor() )
-				if ( cslot.vslot != slot::TIME )
-				m_set->m_final_key.push_slot( cslot.etype, cslot.vslot );
-			uint32 index = m_set->m_size;
-			m_set->m_channels[index] = move( channel );
-			m_set->m_size++;
-			return raw_data_channel_access( &m_set->m_channels[index] );
-		}
-
-		const raw_data_channel* operator []( uint32 i ) const { return &m_set->m_channels[i]; }
-		const_iterator begin() const { return m_set->begin(); }
-		const_iterator end()   const { return m_set->end(); }
-
-	protected:
-		key_channel_set* m_set;
-	};
-
 }
 
Index: trunk/nv/interface/data_descriptor.hh
===================================================================
--- trunk/nv/interface/data_descriptor.hh	(revision 418)
+++ trunk/nv/interface/data_descriptor.hh	(revision 419)
@@ -263,4 +263,6 @@
 		}
 
+
+
 		const_iterator begin() const { return &m_slots[0]; }
 		const_iterator end() const { return &m_slots[m_size]; }
Index: trunk/nv/interface/interpolation_raw.hh
===================================================================
--- trunk/nv/interface/interpolation_raw.hh	(revision 418)
+++ trunk/nv/interface/interpolation_raw.hh	(revision 419)
@@ -57,5 +57,9 @@
 	}
 
-	inline mat4 extract_matrix_raw( const data_descriptor& desc, const float* data )
+	template < typename T >
+	inline T extract_key_raw( const data_descriptor& desc, const float* data );
+
+	template <>
+	inline mat4 extract_key_raw( const data_descriptor& desc, const float* data )
 	{
 		if ( desc.size() == 1 )
@@ -98,5 +102,6 @@
 	}
 
-	inline transform extract_transform_raw( const data_descriptor& desc, const float* data )
+	template <>
+	inline transform extract_key_raw( const data_descriptor& desc, const float* data )
 	{
 		if ( desc.size() == 1 )
Index: trunk/nv/interface/mesh_data.hh
===================================================================
--- trunk/nv/interface/mesh_data.hh	(revision 418)
+++ trunk/nv/interface/mesh_data.hh	(revision 419)
@@ -28,5 +28,5 @@
 		sint16    parent_id;
 		mat4      transform;
-		key_channel_set* data;
+		data_channel_set* data;
 	};
 
@@ -75,7 +75,7 @@
 		}
 
-		key_channel_set* release_node_data( size_t i )
+		data_channel_set* release_node_data( size_t i )
 		{
-			key_channel_set* result = m_nodes[i].data;
+			data_channel_set* result = m_nodes[i].data;
 			m_nodes[i].data = nullptr;
 			return result;
Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 418)
+++ trunk/src/formats/assimp_loader.cc	(revision 419)
@@ -418,6 +418,6 @@
 	}
 
-	data->data = key_channel_set_creator::create( 2 );
-	key_channel_set_creator key_set( data->data );
+	data->data = data_channel_set_creator::create( 2 );
+	data_channel_set_creator key_set( data->data );
 
 	assimp_key_p* pchannel = key_set.add_channel< assimp_key_p >( node->mNumPositionKeys ).data();
Index: trunk/src/formats/md3_loader.cc
===================================================================
--- trunk/src/formats/md3_loader.cc	(revision 418)
+++ trunk/src/formats/md3_loader.cc	(revision 419)
@@ -429,6 +429,6 @@
 		nodes[i].parent_id = -1;
 		nodes[i].target_id = -1;
-		nodes[i].data      = key_channel_set_creator::create( 1 );
-		load_tags( key_channel_set_creator( nodes[i].data ).add_channel<md3_key>( uint32( md3->header.num_frames ) ).channel(), name );
+		nodes[i].data      = data_channel_set_creator::create( 1 );
+		load_tags( data_channel_set_creator( nodes[i].data ).add_channel<md3_key>( uint32( md3->header.num_frames ) ).channel(), name );
 	}
 	return new mesh_nodes_data( "tags", node_count, nodes );
Index: trunk/src/formats/md5_loader.cc
===================================================================
--- trunk/src/formats/md5_loader.cc	(revision 418)
+++ trunk/src/formats/md5_loader.cc	(revision 419)
@@ -250,6 +250,6 @@
 				nodes[i].transform = mat4();
 				nodes[i].target_id = -1;
-				nodes[i].data = key_channel_set_creator::create( 1 );
-				key_channel_set_creator( nodes[i].data ).add_channel< md5_key_t >( num_frames );
+				nodes[i].data = data_channel_set_creator::create( 1 );
+				data_channel_set_creator( nodes[i].data ).add_channel< md5_key_t >( num_frames );
 				next_line( sstream );
 			}
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 418)
+++ trunk/src/formats/nmd_loader.cc	(revision 419)
@@ -121,6 +121,6 @@
 		if ( ch_count > 0 )
 		{
-			key_channel_set* kdata = key_channel_set_creator::create( ch_count );
-			key_channel_set_creator kaccess( kdata );
+			data_channel_set* kdata = data_channel_set_creator::create( ch_count );
+			data_channel_set_creator kaccess( kdata );
 			m_node_array[i].data = kdata;
 			for ( uint32 c = 0; c < ch_count; ++c )
Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 418)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 419)
@@ -39,4 +39,9 @@
 	m_frame_count  = pos_size / m_vertex_count;
 	m_pbuffer      = buffer();
+
+	if ( m_tag_map && m_tag_map->get_count() > 0 )
+	{
+		m_interpolation_key = m_tag_map->get_node( 0 )->data->get_interpolation_key();
+	}
 }
 
@@ -50,9 +55,8 @@
 	if ( !m_tag_map ) return transform();
 	NV_ASSERT( node_id < m_tag_map->get_count(), "TAGMAP FAIL" );
-	const key_channel_set* data = m_tag_map->get_node( node_id )->data;
+	const data_channel_set* data = m_tag_map->get_node( node_id )->data;
 	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  );
+	raw_channel_interpolator interpolator( data, m_interpolation_key );
+	return interpolator.get< transform >( m_last_frame, m_next_frame, m_interpolation );
 }
 
Index: trunk/src/gfx/mesh_creator.cc
===================================================================
--- trunk/src/gfx/mesh_creator.cc	(revision 418)
+++ trunk/src/gfx/mesh_creator.cc	(revision 419)
@@ -19,6 +19,6 @@
 	{
 		sint16 parent_id = m_data->m_nodes[i].parent_id;
-		key_channel_set* keys   = m_data->m_nodes[i].data;
-		key_channel_set* pkeys  = ( parent_id != -1 ? m_data->m_nodes[parent_id].data : nullptr );
+		data_channel_set* keys   = m_data->m_nodes[i].data;
+		data_channel_set* pkeys  = ( parent_id != -1 ? m_data->m_nodes[parent_id].data : nullptr );
 		size_t count     = ( keys ? keys->get_channel_size(0) : 0 );
 		size_t pcount    = ( pkeys ? pkeys->get_channel_size(0) : 0 );
@@ -46,8 +46,4 @@
 }
 
-// TODO: DELETE
-struct assimp_key_p  { float time; nv::vec3 position; };
-struct assimp_key_r  { float time; nv::quat rotation; };
-
 
 void nv::mesh_nodes_creator::merge_keys()
@@ -55,5 +51,5 @@
 	for ( size_t i = 0; i < m_data->get_count(); ++i )
 	{
-		key_channel_set* old_keys = m_data->m_nodes[i].data;
+		data_channel_set* old_keys = m_data->m_nodes[i].data;
 		if ( old_keys && old_keys->size() > 0 )
 		{
@@ -69,9 +65,10 @@
 			}
 
-			key_channel_set* new_keys = key_channel_set_creator::create( 1 );
-			key_channel_set_creator nk_access( new_keys );
+			data_channel_set* new_keys = data_channel_set_creator::create( 1 );
+			data_channel_set_creator nk_access( new_keys );
 			data_channel_access< nv_key_transform > kt_channel( nk_access.add_channel<nv_key_transform>( max_keys ) );
 
-			data_descriptor final_key = old_keys->get_final_key();
+			raw_channel_interpolator interpolator( old_keys );
+			data_descriptor final_key = interpolator.get_interpolation_key();
 
 			for ( unsigned n = 0; n < max_keys; ++n )
@@ -83,7 +80,7 @@
 				{
 					size_t idx = nv::min( old_keys->get_channel_size(c) - 1, n );
-					pkey += old_keys->get_raw( *old_keys->get_channel(c), idx, pkey );
-				}
-				kt_channel.data()[n].tform = extract_transform_raw( final_key, key );
+					pkey += raw_channel_interpolator::get_raw( *old_keys->get_channel(c), idx, pkey );
+				}
+				kt_channel.data()[n].tform = extract_key_raw< nv::transform >( final_key, key );
 			}
 
@@ -106,5 +103,5 @@
 		if ( node.data )
 		{
-			key_channel_set* kdata  = node.data;
+			data_channel_set* kdata  = node.data;
 			for ( size_t c = 0; c < kdata->size(); ++c )
 			{
@@ -332,5 +329,5 @@
 		}
 	}
-	delete tangents2;
+	delete[] tangents2;
 
 	data_channel_set_creator( m_data ).set_channel( n_channel_index, merge_channels( *n_channel, g_channel ) );
Index: trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- trunk/src/gfx/skeletal_mesh.cc	(revision 418)
+++ trunk/src/gfx/skeletal_mesh.cc	(revision 419)
@@ -91,4 +91,16 @@
 
 
+void nv::skeletal_animation_entry_cpu::initialize()
+{
+	for ( size_t i = 0; i < m_node_data->get_count(); ++i )
+	{
+		if ( m_node_data->get_node( i )->data )
+		{
+			m_interpolation_key = m_node_data->get_node( i )->data->get_interpolation_key();
+			break;
+		}
+	}
+}
+
 void nv::skeletal_animation_entry_cpu::update_skeleton( transform* skeleton, float time ) const
 {
@@ -100,5 +112,6 @@
 	for ( size_t i = 0; i < m_node_data->get_count(); ++i )
 	{
-		skeleton[i] = m_node_data->get_node(i)->data->get_transform( frame_num );
+		raw_channel_interpolator interpolator( m_node_data->get_node( i )->data, m_interpolation_key );
+		skeleton[i] = interpolator.get< transform >( frame_num );
 	}
 }
@@ -112,4 +125,6 @@
 	m_bone_ids  = new sint16[ node_count ];
 
+	NV_ASSERT( m_node_data, "node data empty!" );
+
 	if ( !m_node_data->is_flat() )
 	{
@@ -146,5 +161,6 @@
 			if ( node->data )
 			{
-				node_mat = node->data->get_matrix( anim_time );
+				raw_channel_interpolator interpolator( node->data, m_interpolation_key );
+				node_mat = interpolator.get< mat4 >( anim_time );
 			}
 
@@ -177,4 +193,8 @@
 		}
 		m_bone_ids[n] = bone_id;
+
+		if ( m_interpolation_key.size() == 0 && node->data )
+			m_interpolation_key = node->data->get_interpolation_key();
+
 	}
 	m_prepared = true;
@@ -190,5 +210,6 @@
 	if ( node->data )
 	{
-		node_mat = node->data->get_matrix( time );
+		raw_channel_interpolator interpolator( node->data, m_interpolation_key );
+		node_mat = interpolator.get< mat4 >( time );
 	}
 
