Index: /trunk/nv/formats/assimp_loader.hh
===================================================================
--- /trunk/nv/formats/assimp_loader.hh	(revision 281)
+++ /trunk/nv/formats/assimp_loader.hh	(revision 282)
@@ -16,4 +16,9 @@
 namespace nv 
 {
+	struct assimp_key_p  { float time; vec3 position; };
+	struct assimp_key_r  { float time; quat rotation; };
+	struct assimp_key_s  { float time; vec3 scale; };
+	struct assimp_key_tr { transform tform; };
+
 
 	struct assimp_bone
@@ -37,12 +42,18 @@
 		sint32                parent_id;
 		mat4                  transform;
-		key_animation_data*   keys;
-		sint32                bone_id;   // reconstructed
-		std::vector< assimp_animated_node_data* > children;  // reconstructed
+		uint16                channel_count;
+		key_raw_channel*      channels[4];
 
-		assimp_animated_node_data() : name(), parent_id( -1 ), keys( nullptr ), bone_id( -1 ) {}
+		assimp_animated_node_data() 
+			: name(), parent_id( -1 )
+		{
+			channels[0] = nullptr; channels[1] = nullptr; channels[2] = nullptr; channels[3] = nullptr;
+		}
 		~assimp_animated_node_data() 
 		{
-			if ( keys ) delete keys;
+			if ( channels[0] ) delete channels[0];
+			if ( channels[1] ) delete channels[1];
+			if ( channels[2] ) delete channels[2];
+			if ( channels[3] ) delete channels[3];
 		}
 	};
@@ -53,8 +64,6 @@
 		float duration;
 		bool  pretransformed;
-		std::vector< assimp_animated_node_data > nodes; // read-only!
+		dynamic_array< assimp_animated_node_data > nodes;
 	};
-
-
 
 	class assimp_loader : public mesh_loader
@@ -68,5 +77,5 @@
 		virtual ~assimp_loader();
 		assimp_model* release_merged_model();
-		assimp_animation* release_animation( size_t index, bool pre_transform, const std::vector< assimp_bone >* bone_data );
+		assimp_animation* release_animation( size_t index, bool pre_transform );
 		bool load_bones( size_t index, std::vector< assimp_bone >& bones );
 		void scene_report() const;
@@ -74,6 +83,6 @@
 		uint32 load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id );
 		uint32 count_nodes( const void* node ) const;
-		key_animation_data* create_transformed_keys( const void* vnode, const key_animation_data* parent_keys );
-		key_animation_data* create_direct_keys( const void* vnode );
+		void create_transformed_keys( assimp_animated_node_data* data, const void* vnode, const assimp_animated_node_data* parent );
+		void create_direct_keys( assimp_animated_node_data* data, const void* vnode );
 
 		string_table_creator m_strings;
Index: /trunk/nv/formats/md3_loader.hh
===================================================================
--- /trunk/nv/formats/md3_loader.hh	(revision 281)
+++ /trunk/nv/formats/md3_loader.hh	(revision 282)
@@ -23,4 +23,9 @@
 namespace nv 
 {
+	struct md3_key
+	{
+		transform tform;
+	};
+
 
 	class md3_loader : public mesh_loader
@@ -36,14 +41,6 @@
 		virtual tag_map* create_tag_map();
 	private:
-		void load_tags( transform_vector& t, const std::string& tag );
+		key_raw_channel* load_tags( const std::string& tag );
 		void* m_md3;
-
-		struct md3_tag
-		{
-			std::string name;
-			transform   trans;
-		};
-
-		std::unordered_map< std::string, md3_tag > m_tags;
 	};
 
Index: /trunk/nv/formats/md5_loader.hh
===================================================================
--- /trunk/nv/formats/md5_loader.hh	(revision 281)
+++ /trunk/nv/formats/md5_loader.hh	(revision 282)
@@ -57,8 +57,8 @@
 		struct md5_joint
 		{
-			int              parent;
-			transform_vector keys;
+			int                      parent;
+			std::vector< transform > keys;
 
-			md5_joint( int a_parent, size_t reserve ) : parent( a_parent ) { keys.reserve( reserve ); }
+			md5_joint( int a_parent ) : parent( a_parent ) {}
 		};
 
Index: /trunk/nv/gfx/animation.hh
===================================================================
--- /trunk/nv/gfx/animation.hh	(revision 281)
+++ /trunk/nv/gfx/animation.hh	(revision 282)
@@ -12,4 +12,5 @@
 #include <nv/interface/stream.hh>
 #include <nv/math.hh>
+#include <nv/interface/vertex.hh>
 #include <nv/transform.hh>
 #include <glm/gtc/matrix_transform.hpp>
@@ -18,61 +19,138 @@
 {
 
-	// TODO: time sorting or check?
-	template < typename KEY >
-	class key_vector
-	{
-	public:
-		struct key
-		{
-			float time;
-			KEY   value;
-			key() {}
-			key( float a_time, const KEY& a_value ) : time(a_time), value(a_value) {}
-		};
-		key_vector() {}
-		void insert( float a_time, const KEY& a_key ) { m_keys.emplace_back( a_time, a_key ); }
-		size_t size() const { return m_keys.size(); }
-		const key* data() const { return m_keys.data(); }
-		const KEY& get( size_t index ) const { return m_keys[index]; }
-		KEY get_interpolated( float time ) const
-		{
-			if ( m_keys.size() == 0 ) return KEY();
-			if ( m_keys.size() == 1 ) return m_keys[0].value;
+	struct key_raw_channel
+	{
+		key_descriptor desc;
+		uint8*         data;
+		uint32         count;
+
+		key_raw_channel() : data( nullptr ), count( 0 ) {}
+		~key_raw_channel() 
+		{
+			if ( data != nullptr ) delete[] data;
+		}
+
+		uint32 size() const { return count * desc.size; }
+
+		template < typename KEY >
+		static key_raw_channel* create( uint32 count = 0 )
+		{
+			key_raw_channel* result = new key_raw_channel();
+			result->desc.initialize<KEY>();
+			result->count = count;
+			result->data  = (count > 0 ? ( new uint8[ result->size() ] ) : nullptr );
+			return result;
+		}
+
+		static key_raw_channel* create( const key_descriptor& keydesc, uint32 count = 0 )
+		{
+			key_raw_channel* result = new key_raw_channel();
+			result->desc  = keydesc;
+			result->count = count;
+			result->data  = (count > 0 ? ( new uint8[ result->size() ] ) : nullptr );
+			return result;
+		}
+	};
+
+	class key_data
+	{
+	public:
+		key_data() {}
+
+		void add_channel( key_raw_channel* channel ) 
+		{
+			NV_ASSERT( channel, "nullptr passed to add_channel!" );
+			m_channels.push_back( channel );
+		}
+
+		virtual ~key_data()
+		{
+			for ( auto channel : m_channels ) delete channel;
+		}
+	private:
+		std::vector< key_raw_channel* > m_channels;
+	};
+
+	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( 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 )
+		{
+			m_data = data;
+			key_descriptor desc;
+			desc.initialize<KEY>();
+			NV_ASSERT( data->desc == desc, "Bad channel passed!" );
+		}
+		void get_interpolated( KEY& result, float frame ) const
+		{
+			NV_ASSERT( m_data, "Data is null!" );
+			if ( m_data->count == 0 ) return;
+			if ( m_data->count == 1 ) 
+			{
+				result = ((KEY*)m_data->data)[0];
+				return;
+			}
+			size_t index = glm::clamp<size_t>( size_t( frame ), 0, m_data->count - 2 );
+			float factor = glm::clamp<float> ( frame - index, 0.0f, 1.0f );
+			KEY* keys = ((KEY*)m_data->data);
+			interpolate_key( result, keys[index], keys[index+1], factor );
+		}
+
+	private:
+		key_raw_channel* m_data;
+	};
+ 
+ 	template < typename KEY >
+ 	class key_channel_interpolator< KEY, true >
+	{
+	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 )
+		{
+			m_data = data;
+			key_descriptor desc;
+			desc.initialize<KEY>();
+			NV_ASSERT( data->desc == desc, "Bad channel passed!" );
+		}
+		void get_interpolated( KEY& result, float time ) const
+		{
+			// TODO: this probably could be optimized
+			const KEY* keys = (const KEY*)(m_data->data);
+			NV_ASSERT( m_data, "Data is null!" );
+			if ( m_data->count == 0 ) return;
+			if ( m_data->count == 1 ) 
+			{
+				result = keys[0];
+				return;
+			}
 			int index = -1;
-			for ( int i = 0 ; i < (int)m_keys.size() - 1 ; i++ )
+			for ( int i = 0 ; i < (int)m_data->count - 1 ; i++ )
 			{
-				if ( time < m_keys[i + 1].time ) { index = i; break; }
+				if ( time < keys[i + 1].time ) { index = i; break; }
 			}
 			NV_ASSERT( index >= 0, "animation time fail!");
-			float delta  = m_keys[index + 1].time - m_keys[index].time;
-			float factor = glm::clamp( (time - m_keys[index].time) / delta, 0.0f, 1.0f );
-			return interpolate( m_keys[index].value, m_keys[index + 1].value, factor );
-		}
-		virtual uint32 raw_size() const 
-		{
-			return sizeof( size_t ) + m_keys.size() * sizeof( key );
-		}
-		virtual void dump( stream* out_stream ) const 
-		{
-			size_t sz = m_keys.size();
-			out_stream->write( &sz, sizeof( size_t ), 1 );
-			if ( sz > 0 )
-			{
-				out_stream->write( &m_keys[0], sizeof( key ), sz );
-			}
-		}
-		virtual void load( stream* in_stream )
-		{
-			size_t sz;
-			in_stream->read( &sz, sizeof( size_t ), 1 );
-			if ( sz > 0 )
-			{
-				m_keys.resize( sz );
-				in_stream->read( &m_keys[0], sizeof( key ), sz );
-			}
-		}
-	protected:
-		std::vector< key > m_keys;
-	};
+			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:
+		key_raw_channel* m_data;
+	};
+ 
+// 	template < typename KEY1, typename KEY2 = void, typename KEY3 = void >
+// 	class key_data_interpolator
+// 	{
+// 
+// 	};
 
 	class key_animation_data
@@ -81,7 +159,6 @@
 		virtual mat4 get_matrix( float time ) const = 0;
 		virtual transform get_transform( float time ) const = 0;
-		virtual void dump( stream* out_stream ) const = 0;
-		virtual void load( stream* in_stream ) = 0;
 		virtual bool empty() const = 0;
+		virtual size_t size() const = 0;
 		virtual uint32 raw_size() const = 0;
 		virtual ~key_animation_data() {}
@@ -91,86 +168,90 @@
 	class key_vectors_prs : public key_animation_data
 	{
-	public:
-		key_vectors_prs() {}
-		void insert_position( float a_time, const vec3& a_value ) { m_positions.insert( a_time, a_value ); }
-		void insert_rotation( float a_time, const quat& a_value ) { m_rotations.insert( a_time, a_value ); }
-		void insert_scale   ( float a_time, const vec3& a_value ) { m_scales.insert( a_time, a_value ); }
-		bool empty() const { return m_positions.size() == 0 && m_rotations.size() == 0 && m_scales.size() == 0; }
+		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( key_raw_channel* p, key_raw_channel* r, key_raw_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->count == 0 && m_rchannel->count == 0 && m_schannel->count == 0; }
 		virtual mat4 get_matrix( float time ) const
 		{
-			nv::mat4 position;
-			nv::mat4 rotation;
-			nv::mat4 scaling;
-
-			if ( m_positions.size() > 0 ) position = glm::translate( position, m_positions.get_interpolated( time ) );
-			if ( m_rotations.size() > 0 ) rotation = glm::mat4_cast( m_rotations.get_interpolated( time ) );
-			if ( m_scales.size()    > 0 ) scaling  = glm::scale( scaling, m_scales.get_interpolated( time ) );
-
-			return position * rotation * scaling;
+			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
 		{
-			transform result;
-			if ( m_positions.size() > 0 ) result.set_position( m_positions.get_interpolated( time ) );
-			if ( m_rotations.size() > 0 ) result.set_orientation( m_rotations.get_interpolated( time ) );
-			return result;
+			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 m_positions.raw_size() + m_rotations.raw_size() + m_scales.raw_size();
-		}
-		virtual void dump( stream* out_stream ) const 
-		{
-			m_positions.dump( out_stream );
-			m_rotations.dump( out_stream );
-			m_scales.dump( out_stream );
-		}
-		virtual void load( stream* in_stream )
-		{
-			m_positions.load( in_stream );
-			m_rotations.load( in_stream );
-			m_scales.load( in_stream );
+			return 3 * sizeof( size_t ) 
+				+ m_pchannel->count * sizeof( key_p )
+				+ m_rchannel->count * sizeof( key_r )
+				+ m_schannel->count * sizeof( key_s );
+		}
+		~key_vectors_prs()
+		{
 		}
 	protected:
-		key_vector< nv::vec3 > m_positions;
-		key_vector< nv::quat > m_rotations;
-		key_vector< nv::vec3 > m_scales;
+		key_raw_channel* m_pchannel;
+		key_raw_channel* m_rchannel;
+		key_raw_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
 	{
-	public:
-		transform_vector() {}
-		void reserve( size_t sz ) { m_keys.reserve( sz ); }
-		void insert( const transform& t ) { m_keys.push_back( t ); }
-		bool empty() const { return m_keys.empty(); }
-		size_t size() const { return m_keys.size(); }
-		const transform& get( size_t index ) const { return m_keys[ index ]; }
-		const transform* data() const { return m_keys.data(); }
+		struct key
+		{
+			transform tform;
+		};
+	public:
+		explicit transform_vector( key_raw_channel* channel ) 
+		{
+			key_descriptor kd;
+			kd.initialize<key>();
+			NV_ASSERT( kd == channel->desc, "bad channel!" );
+			m_channel = channel;
+			m_interpolator.set_data( m_channel );
+		}
+
+		~transform_vector()
+		{
+			delete m_channel;
+		}
+		bool empty() const { return m_channel->count == 0; }
+		size_t size() const { return m_channel->count; }
+		const transform& get( size_t index ) const { return ((key*)(m_channel->data))[ index ].tform; }
+		const transform* data() const { return (const transform*)m_channel->data; }
 
 		virtual uint32 raw_size() const 
 		{
-			return sizeof( size_t ) + m_keys.size() * sizeof( transform );
-		}
-
-		virtual void dump( stream* out_stream ) const 
-		{
-			size_t sz = m_keys.size();
-			out_stream->write( &sz, sizeof( size_t ), 1 );
-			if ( sz > 0 )
-			{
-				out_stream->write( &m_keys[0], sizeof( transform ), sz );
-			}
-		}
-		virtual void load( stream* in_stream )
-		{
-			size_t sz;
-			in_stream->read( &sz, sizeof( size_t ), 1 );
-			if ( sz > 0 )
-			{
-				m_keys.resize( sz );
-				in_stream->read( &m_keys[0], sizeof( transform ), sz );
-			}
-		}
+			return sizeof( size_t ) + m_channel->count * sizeof( key );
+		}
+
 		virtual mat4 get_matrix( float time ) const
 		{
@@ -179,13 +260,13 @@
 		virtual transform get_transform( float time ) const
 		{
-			if ( m_keys.size() == 0 ) return transform();
-			if ( m_keys.size() == 1 ) return m_keys[0];
-			size_t index = glm::clamp<size_t>( size_t( time ), 0, m_keys.size() - 2 );
-			float factor = glm::clamp<float> ( time - index, 0.0f, 1.0f );
-			return interpolate( m_keys[ index ], m_keys[ index + 1 ], factor );
+			key result;
+			m_interpolator.get_interpolated( result, time );
+			return extract_transform< key >( result );
 		}
 	protected:
-		std::vector< transform > m_keys;
-	};
+		key_channel_interpolator< key, false > m_interpolator;
+		key_raw_channel* m_channel;
+	};
+
 
 }
Index: /trunk/nv/interface/mesh_loader.hh
===================================================================
--- /trunk/nv/interface/mesh_loader.hh	(revision 281)
+++ /trunk/nv/interface/mesh_loader.hh	(revision 282)
@@ -26,16 +26,25 @@
 {
 
+	// TODO: change to generic nodes!
 	class tag_map
 	{
 	public:
-		typedef std::unordered_map< std::string, transform_vector > map;
+		typedef std::unordered_map< std::string, key_raw_channel* > map;
 
 		tag_map () {}
-		map& get_map()             { return m_map; }
-		const map& get_map() const { return m_map; }
-		const transform_vector* get_tag( const std::string& key ) const
+
+		const transform* get_tag( const std::string& key ) const
 		{
-			auto it = m_map.find( key );
-			return ( it != m_map.end() ? &(it->second) : nullptr );
+			key_raw_channel* channel = m_map.at( key );
+			return ((transform*)(channel->data));
+		}
+		void insert( const std::string& key, key_raw_channel* chan )
+		{
+			m_map[ key ] = chan;
+		}
+		~tag_map()
+		{
+			for ( auto t : m_map )
+				delete t.second;
 		}
 	private:
Index: /trunk/nv/interface/vertex.hh
===================================================================
--- /trunk/nv/interface/vertex.hh	(revision 281)
+++ /trunk/nv/interface/vertex.hh	(revision 282)
@@ -11,4 +11,5 @@
 
 #include <nv/common.hh>
+#include <nv/transform.hh>
 #include <nv/math.hh>
 
@@ -31,13 +32,14 @@
 	};
 
-	enum animation_slot
-	{
-// 		TIME     = 0,
-// 		POSITION = 1,
-// 		ROTATION = 2,
-// 		SCALE    = 3,
-// 		MATRIX   = 4,
-// 		ANIM_SLOT_MAX       = 4,
-// 		ANIM_SLOT_MAX_STORE = 5,
+	enum class animation_slot : uint8
+	{
+ 		TIME     = 0,
+ 		POSITION = 1,
+ 		ROTATION = 2,
+ 		SCALE    = 3,
+ 		TFORM    = 4,
+
+ 		SLOT_MAX       = 4,
+ 		SLOT_MAX_STORE = 8,
 	};
 
@@ -217,4 +219,137 @@
 		};
 
+		template < typename KEY >
+		struct key_has_slot_impl< KEY, animation_slot::TIME >
+		{
+		private:
+			struct fallback { int time; };
+			struct derived : KEY, fallback { };
+			template<typename C, C> struct cht; 
+			template<typename C> static char (&test(cht<int fallback::*, &C::time>*))[1]; 
+			template<typename C> static char (&test(...))[2]; 
+		public:
+			static bool const value = sizeof(test<derived>(0)) == 2;;
+		};
+
+		template < typename KEY >
+		struct key_has_slot_impl< KEY, animation_slot::POSITION >
+		{
+		private:
+			struct fallback { int position; };
+			struct derived : KEY, fallback { };
+			template<typename C, C> struct cht; 
+			template<typename C> static char (&test(cht<int fallback::*, &C::position>*))[1]; 
+			template<typename C> static char (&test(...))[2]; 
+		public:
+			static bool const value = sizeof(test<derived>(0)) == 2;;
+		};
+
+		template < typename KEY >
+		struct key_has_slot_impl< KEY, animation_slot::ROTATION >
+		{
+		private:
+			struct fallback { int rotation; };
+			struct derived : KEY, fallback { };
+			template<typename C, C> struct cht; 
+			template<typename C> static char (&test(cht<int fallback::*, &C::rotation>*))[1]; 
+			template<typename C> static char (&test(...))[2]; 
+		public:
+			static bool const value = sizeof(test<derived>(0)) == 2;;
+		};
+
+		template < typename KEY >
+		struct key_has_slot_impl< KEY, animation_slot::SCALE >
+		{
+		private:
+			struct fallback { int scale; };
+			struct derived : KEY, fallback { };
+			template<typename C, C> struct cht; 
+			template<typename C> static char (&test(cht<int fallback::*, &C::scale>*))[1]; 
+			template<typename C> static char (&test(...))[2]; 
+		public:
+			static bool const value = sizeof(test<derived>(0)) == 2;;
+		};
+
+		template < typename KEY >
+		struct key_has_slot_impl< KEY, animation_slot::TFORM >
+		{
+		private:
+			struct fallback { int tform; };
+			struct derived : KEY, fallback { };
+			template<typename C, C> struct cht; 
+			template<typename C> static char (&test(cht<int fallback::*, &C::tform>*))[1]; 
+			template<typename C> static char (&test(...))[2]; 
+		public:
+			static bool const value = sizeof(test<derived>(0)) == 2;;
+		};
+
+		template < typename KEY, animation_slot SLOT, bool HAS_SLOT >
+		struct key_slot_info_impl
+		{
+		};
+
+		template < typename KEY, animation_slot SLOT >
+		struct key_slot_info_impl < KEY, SLOT, false >
+		{
+			typedef empty_type value_type;
+			static const datatype etype  = datatype::NONE;
+			static const int      offset = 0;
+		};
+
+		template < typename KEY >
+		struct key_slot_info_impl< KEY, animation_slot::TIME, true >
+		{
+			typedef decltype( KEY::time ) value_type;
+			static const datatype etype  = type_to_enum< decltype( KEY::time ) >::type;
+			static const int      offset = offsetof( KEY, time );
+		};
+
+		template < typename KEY >
+		struct key_slot_info_impl< KEY, animation_slot::POSITION, true >
+		{
+			typedef decltype( KEY::position ) value_type;
+			static const datatype etype  = type_to_enum< decltype( KEY::position ) >::type;
+			static const int      offset = offsetof( KEY, position );
+			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
+			{
+				key.position = nv::interpolate( k1.position, k2.position, factor );
+			}
+		};
+
+		template < typename KEY >
+		struct key_slot_info_impl< KEY, animation_slot::ROTATION, true >
+		{
+			typedef decltype( KEY::rotation ) value_type;
+			static const datatype etype  = type_to_enum< decltype( KEY::rotation ) >::type;
+			static const int      offset = offsetof( KEY, rotation );
+			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
+			{
+				key.rotation = nv::interpolate( k1.rotation, k2.rotation, factor );
+			}
+		};
+
+		template < typename KEY >
+		struct key_slot_info_impl< KEY, animation_slot::SCALE, true >
+		{
+			typedef decltype( KEY::scale ) value_type;
+			static const datatype etype  = type_to_enum< decltype( KEY::scale ) >::type;
+			static const int      offset = offsetof( KEY, scale );
+			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
+			{
+				key.scale = nv::interpolate( k1.scale, k2.scale, factor );
+			}
+		};
+
+		template < typename KEY >
+		struct key_slot_info_impl< KEY, animation_slot::TFORM, true >
+		{
+			typedef decltype( KEY::tform ) value_type;
+			static const datatype etype  = type_to_enum< decltype( KEY::tform ) >::type;
+			static const int      offset = offsetof( KEY, tform );
+			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
+			{
+				key.tform = nv::interpolate( k1.tform, k2.tform, factor );
+			}
+		};
 	}
 
@@ -230,4 +365,15 @@
 	};
 
+	template < typename KEY, animation_slot SLOT >
+	struct key_has_slot : public detail::key_has_slot_impl< KEY, SLOT >
+	{
+	};
+
+
+	template < typename KEY, animation_slot SLOT >
+	struct key_slot_info : public detail::key_slot_info_impl< KEY, SLOT, detail::key_has_slot_impl< KEY, SLOT >::value >
+	{
+	};
+	
 	struct vertex_descriptor_slot
 	{
@@ -235,5 +381,4 @@
 		uint32   offset;
 		slot     vslot;
-		vertex_descriptor_slot() : etype(NONE), offset(0), vslot(slot::POSITION) {}
 	};
 
@@ -243,6 +388,4 @@
 		uint32                 count;
 		uint32                 size;
-
-		vertex_descriptor() : count(0), size(0) {}
 
 		template < typename IDX >
@@ -308,4 +451,216 @@
 	};
 
+	struct key_descriptor_slot
+	{
+		datatype       etype;
+		uint32         offset;
+		animation_slot vslot;
+		key_descriptor_slot() : etype(NONE), offset(0), vslot(animation_slot::TIME) {}
+	};
+
+	struct key_descriptor
+	{
+		key_descriptor_slot slots[ animation_slot::SLOT_MAX_STORE ];
+		uint32              count;
+		uint32              size;
+
+		key_descriptor() : count(0), size(0) {}
+
+		template < typename KEY >
+		void initialize()
+		{
+			count = 0;
+			initialize_slot< KEY, animation_slot::TIME >();
+			initialize_slot< KEY, animation_slot::POSITION >();
+			initialize_slot< KEY, animation_slot::ROTATION >();
+			initialize_slot< KEY, animation_slot::SCALE >();
+			initialize_slot< KEY, animation_slot::TFORM >();
+			size = sizeof( KEY );
+		}
+
+		bool operator==( const key_descriptor& rhs )
+		{
+			if ( size  != rhs.size )  return false;
+			if ( count != rhs.count ) return false;
+			for ( uint32 i = 0; i < count; ++i )
+			{
+				if ( slots[i].etype  != rhs.slots[i].etype )  return false;
+				if ( slots[i].offset != rhs.slots[i].offset ) return false;
+				if ( slots[i].vslot  != rhs.slots[i].vslot )  return false;
+			}
+			return true;
+		}
+
+	private:
+		template < typename KEY, animation_slot SLOT >
+		void initialize_slot()
+		{
+			typedef key_slot_info< KEY, SLOT > slot_info;
+			slots[ count ].etype  = slot_info::etype;
+			if ( slots[ count ].etype != datatype::NONE )
+			{
+				slots[ count ].vslot  = SLOT;
+				slots[ count ].offset = slot_info::offset;
+				count++;
+			}
+		}
+	};
+
+	template < typename KEY, animation_slot SLOT > 
+	void interpolate_slot( KEY& key, const KEY& k1, const KEY& k2, float factor, const std::true_type& )
+	{
+		key_slot_info< KEY, SLOT >::interpolate( key, k1, k2, factor );
+	}
+
+	template < typename KEY, animation_slot SLOT > 
+	void interpolate_slot( KEY&, const KEY&, const KEY&, float, const std::false_type& )
+	{
+	}
+
+	template < typename KEY >
+	void interpolate_key( KEY& key, const KEY& k1, const KEY& k2, float factor )
+	{
+		interpolate_slot< KEY, animation_slot::POSITION >( key, k1, k2, factor, std::integral_constant< bool, key_has_slot< KEY, animation_slot::POSITION >::value >() );
+		interpolate_slot< KEY, animation_slot::ROTATION >( key, k1, k2, factor, std::integral_constant< bool, key_has_slot< KEY, animation_slot::ROTATION >::value >() );
+		interpolate_slot< KEY, animation_slot::SCALE >( key, k1, k2, factor, std::integral_constant< bool, key_has_slot< KEY, animation_slot::SCALE >::value >() );
+		interpolate_slot< KEY, animation_slot::TFORM >( key, k1, k2, factor, std::integral_constant< bool, key_has_slot< KEY, animation_slot::TFORM >::value >() );
+	}
+
+	namespace detail
+	{
+		template < typename T >
+		mat4 extract_matrix_slot( const T& ) { static_assert( false, "extract_matrix_slot" ); }
+		template <> inline mat4 extract_matrix_slot( const mat4& m ) { return m; }
+		template <> inline mat4 extract_matrix_slot( const transform& m ) { return m.extract(); }
+
+		template < typename T >
+		transform extract_transfrom_slot( const T& ) { static_assert( false, "extract_matrix_slot" ); }
+		template <> inline transform extract_transfrom_slot( const mat4& m ) { return transform(m); }
+		template <> inline transform extract_transfrom_slot( const transform& m ) { return m; }
+
+		template < typename KEY >
+		mat4 extract_matrix_p_impl( const KEY& k ) { return glm::translate(mat4(),k.position); }
+		template < typename KEY >
+		mat4 extract_matrix_r_impl( const KEY& k ) { return glm::mat4_cast( k.rotation ); }
+		template < typename KEY >
+		mat4 extract_matrix_s_impl( const KEY& k ) { return glm::scale(mat4(),k.scale); }
+		template < typename KEY >
+		mat4 extract_matrix_pr_impl( const KEY& k )
+		{
+			// TODO: this is obviously unoptimized
+			mat4 result = glm::mat4_cast( k.rotation );
+			result[3] = vec4( k.position, 1.0f );
+			return result;
+		}
+		template < typename KEY >
+		mat4 extract_matrix_rs_impl( const KEY& k )
+		{
+			// TODO: this is obviously unoptimized
+			mat4 result = glm::mat4_cast( k.rotation );
+			return glm::scale(result,k.scale);
+		}
+		template < typename KEY >
+		mat4 extract_matrix_ps_impl( const KEY& k )
+		{
+			// TODO: this is obviously unoptimized
+			return glm::scale(glm::translate(mat4(),k.position),k.scale);
+		}
+		template < typename KEY >
+		mat4 extract_matrix_prs_impl( const KEY& k )
+		{
+			// TODO: this is obviously unoptimized
+			return glm::translate(mat4(),k.position) * glm::mat4_cast( k.rotation ) * glm::scale(mat4(),k.scale);
+		}
+
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::false_type&, const std::false_type&, const std::false_type& )
+		{ return mat4(); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::true_type&, const std::false_type&, const std::false_type& )
+		{ return extract_matrix_p_impl(k); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::false_type&, const std::true_type&, const std::false_type& )
+		{ return extract_matrix_r_impl(k); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::false_type&, const std::false_type&, const std::true_type& )
+		{ return extract_matrix_s_impl(k); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::true_type&, const std::true_type&, const std::false_type& )
+		{ return extract_matrix_pr_impl(k); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::false_type&, const std::true_type&, const std::true_type& )
+		{ return extract_matrix_rs_impl(k); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::true_type&, const std::false_type&, const std::true_type& )
+		{ return extract_matrix_ps_impl(k); }
+		template < typename KEY >
+		mat4 extract_matrix_prs( const KEY& k, const std::true_type&, const std::true_type&, const std::true_type& )
+		{ return extract_matrix_prs_impl(k); }
+
+
+		template < typename KEY >
+		transform extract_transform_pr_impl( const KEY& k, const std::false_type&, const std::false_type& ) { return transform(); }
+		template < typename KEY >
+		transform extract_transform_pr_impl( const KEY& k, const std::true_type&, const std::true_type& ) { return transform( k.position, k.rotation ); }
+		template < typename KEY >
+		transform extract_transform_pr_impl( const KEY& k, const std::true_type&, const std::false_type& ) { return transform( k.position ); }
+		template < typename KEY >
+		transform extract_transform_pr_impl( const KEY& k, const std::false_type&, const std::true_type& ) { return transform( k.rotation ); }
+
+
+
+		template < typename KEY >
+		mat4 extract_matrix_impl( const KEY& k, const std::true_type& )
+		{
+			static_assert( key_has_slot< KEY, animation_slot::POSITION >::value == false, "key!");
+			static_assert( key_has_slot< KEY, animation_slot::ROTATION >::value == false, "key!");
+			static_assert( key_has_slot< KEY, animation_slot::SCALE >::value == false, "key!");
+			return extract_matrix_slot( k.tform );
+		}
+
+		template < typename KEY >
+		mat4 extract_matrix_impl( const KEY& k, const std::false_type& )
+		{
+			static_assert( key_has_slot< KEY, animation_slot::TFORM >::value == false, "key!");
+			return extract_matrix_prs( k,
+				std::integral_constant< bool, key_has_slot< KEY, animation_slot::POSITION >::value >(),
+				std::integral_constant< bool, key_has_slot< KEY, animation_slot::ROTATION >::value >(),
+				std::integral_constant< bool, key_has_slot< KEY, animation_slot::SCALE >::value >()
+				);
+		}
+
+		template < typename KEY >
+		transform extract_transform_impl( const KEY& k, const std::true_type& )
+		{
+			static_assert( key_has_slot< KEY, animation_slot::POSITION >::value == false, "key!");
+			static_assert( key_has_slot< KEY, animation_slot::ROTATION >::value == false, "key!");
+			static_assert( key_has_slot< KEY, animation_slot::SCALE >::value == false, "key!");
+			return extract_transfrom_slot( k.tform );
+		}
+
+		template < typename KEY >
+		transform extract_transform_impl( const KEY& k, const std::false_type& )
+		{
+			static_assert( key_has_slot< KEY, animation_slot::TFORM >::value == false, "key!");
+			static_assert( key_has_slot< KEY, animation_slot::SCALE >::value == false, "key!");
+			return extract_transform_pr_impl( k, 
+				std::integral_constant< bool, key_has_slot< KEY, animation_slot::POSITION >::value >(),
+				std::integral_constant< bool, key_has_slot< KEY, animation_slot::ROTATION >::value >() 
+			);
+		}
+	}
+
+	template < typename KEY >
+	mat4 extract_matrix( const KEY& k )
+	{
+		return detail::extract_matrix_impl( k, std::integral_constant< bool, key_has_slot< KEY, animation_slot::TFORM >::value >() );
+	}
+
+	template < typename KEY >
+	transform extract_transform( const KEY& k )
+	{
+		return detail::extract_transform_impl( k, std::integral_constant< bool, key_has_slot< KEY, animation_slot::TFORM >::value >() );
+	}
+
 }
 
Index: /trunk/nv/math.hh
===================================================================
--- /trunk/nv/math.hh	(revision 281)
+++ /trunk/nv/math.hh	(revision 282)
@@ -104,4 +104,6 @@
 		BYTE_VECTOR_3,
 		BYTE_VECTOR_4,
+		QUAT,
+		TRANSFORM,
 		DATATYPE_COUNT,
 	};
@@ -138,4 +140,6 @@
 			{ 1 * 3,  BYTE, 3 },  // BYTE_VECTOR_3,
 			{ 1 * 4,  BYTE, 4 },  // BYTE_VECTOR_4,
+			{ 4 * 4,  FLOAT, 4 },      // QUAT,
+			{ 7 * 4,  FLOAT, 7 },      // TRANSFORM,
 		};
 		return info[dt];
@@ -168,4 +172,6 @@
 	template <> struct enum_to_type< FLOAT_MATRIX_3 > { typedef mat3 type; };
 	template <> struct enum_to_type< FLOAT_MATRIX_4 > { typedef mat4 type; };
+
+	template <> struct enum_to_type< QUAT > { typedef quat type; };
 
 	template < typename TYPE > struct type_to_enum {};
@@ -197,4 +203,5 @@
 	template <> struct type_to_enum< mat3 > { static const datatype type = FLOAT_MATRIX_3; };
 	template <> struct type_to_enum< mat4 > { static const datatype type = FLOAT_MATRIX_4; };
+	template <> struct type_to_enum< quat > { static const datatype type = QUAT; };
 
 	template < typename T >
Index: /trunk/nv/transform.hh
===================================================================
--- /trunk/nv/transform.hh	(revision 281)
+++ /trunk/nv/transform.hh	(revision 282)
@@ -20,4 +20,5 @@
 		explicit transform( const vec3& a_position ) : m_position( a_position ) {}
 		explicit transform( const quat& a_orientation ) : m_orientation( a_orientation ) {}
+		explicit transform( const mat4& a_matrix ) { set( a_matrix ); }
 		transform( const vec3& a_position, const quat& a_orientation ) : m_position( a_position ), m_orientation( a_orientation ) {}
 
@@ -91,5 +92,6 @@
 	}
 
-
+	template <> struct enum_to_type< TRANSFORM > { typedef transform type; };
+	template <> struct type_to_enum< transform > { static const datatype type = TRANSFORM; };
 
 	template<>
Index: /trunk/src/formats/assimp_loader.cc
===================================================================
--- /trunk/src/formats/assimp_loader.cc	(revision 281)
+++ /trunk/src/formats/assimp_loader.cc	(revision 282)
@@ -314,5 +314,5 @@
 }
 
-assimp_animation* nv::assimp_loader::release_animation( size_t, bool pre_transform, const std::vector< assimp_bone >* bone_data )
+assimp_animation* nv::assimp_loader::release_animation( size_t, bool pre_transform )
 {
 	if ( m_scene == nullptr ) return nullptr;
@@ -332,30 +332,4 @@
 
 	load_node( result, root, 0, -1 );
-	// TODO: this is not used when pretransformed, is it used otherwise?
-
-	if ( bone_data )
-	{
-		std::unordered_map< std::string, uint16 > names;
-		for ( uint16 bi = 0; bi < bone_data->size(); ++bi )
-		{
-			names[ (*bone_data)[bi].name ] = bi;
-		}
-
-		for ( unsigned i = 0; i < result->nodes.size(); ++i )
-		{
-			assimp_animated_node_data& node = result->nodes[i];
-			node.bone_id = -1;
-			auto bi = names.find( node.name );
-			if ( bi != names.end() )
-			{
-				node.bone_id = bi->second;
-			}
-			if ( node.parent_id != -1 ) 
-			{
-				result->nodes[ node.parent_id ].children.push_back( &node );
-			}
-		}
-	}
-
 	return result;
 }
@@ -391,5 +365,4 @@
 	a_data.name      = name;
 	a_data.parent_id = parent_id;
-	a_data.bone_id = -1;
 	// This value is ignored by the create_transformed_keys, but needed by create_direct_keys!
 	// TODO: find a common solution!
@@ -397,4 +370,5 @@
 	//       node's without keys
 	a_data.transform = nv::assimp_mat4_cast( node->mTransformation );
+	a_data.channel_count = 0;
 
 	if (anode) 
@@ -402,9 +376,9 @@
 		if ( data->pretransformed )
 		{
-			a_data.keys = create_transformed_keys( anode, parent_id >= 0 ? data->nodes[ parent_id ].keys : nullptr );
+			create_transformed_keys( &a_data, anode, parent_id >= 0 ? &(data->nodes[ parent_id ]) : nullptr );
 		}
 		else
 		{
-			a_data.keys = create_direct_keys( anode );
+			create_direct_keys( &a_data, anode );
 		}
 	}
@@ -418,9 +392,13 @@
 }
 
-key_animation_data* nv::assimp_loader::create_transformed_keys( const void* vnode, const key_animation_data* parent_keys )
+void nv::assimp_loader::create_transformed_keys( assimp_animated_node_data* data, const void* vnode, const assimp_animated_node_data* parent )
 {
 	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
 	size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
-	nv::transform_vector* keys = new nv::transform_vector;
+
+	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));
+
 	for ( unsigned n = 0; n < max_keys; ++n )
 	{
@@ -431,29 +409,44 @@
 		// TODO: only do the calculation when a rotate transform is present!
 		nv::transform ptr( vec3(), glm::quat_cast( m_rotate_transform ) );
-		if ( parent_keys )
-		{
-			const nv::transform_vector* pv = (const nv::transform_vector*)parent_keys;
-			if ( pv && pv->size() > 0 ) ptr = pv->get( glm::min( n, pv->size()-1 ) );
+		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;
+			}
 		}
 		nv::transform key( ptr * nv::transform( pos * m_scale, rot ) );
-		keys->insert( key );
-	}
-	return keys;
-}
-
-key_animation_data* nv::assimp_loader::create_direct_keys( const void* vnode )
-{
+		channel[n].tform = key;
+	}
+}
+
+void nv::assimp_loader::create_direct_keys( assimp_animated_node_data* data, const void* vnode )
+{
+	data->channel_count = 0;
 	// TODO : support for m_rotate_transform and m_scale! ( should be easy )
 	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
-	if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 ) return nullptr;
-	key_vectors_prs* keys = new key_vectors_prs;
+	if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 )
+	{
+		return;
+	}
+
+	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));
 
 	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
 	{
-		keys->insert_position( (float)node->mPositionKeys[np].mTime, m_scale * assimp_vec3_cast(node->mPositionKeys[np].mValue) );
+		pchannel[np].time     = (float)node->mPositionKeys[np].mTime;
+		pchannel[np].position = assimp_vec3_cast(node->mPositionKeys[np].mValue);
 	}
 	for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
 	{
-		keys->insert_rotation( (float)node->mRotationKeys[np].mTime, assimp_quat_cast(node->mRotationKeys[np].mValue) );
+		rchannel[np].time     = (float)node->mRotationKeys[np].mTime;
+		rchannel[np].rotation = assimp_quat_cast(node->mRotationKeys[np].mValue);
 	}
 	if ( node->mNumScalingKeys > 0 )
@@ -466,9 +459,9 @@
 			for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
 			{
-				keys->insert_scale( (float)node->mScalingKeys[np].mTime, assimp_vec3_cast(node->mScalingKeys[np].mValue) );
-			}
-		}
-	}
-	return keys;
-}
-
+				schannel[np].time  = (float)node->mScalingKeys[np].mTime;
+				schannel[np].scale = assimp_vec3_cast(node->mScalingKeys[np].mValue);
+			}
+		}
+	}
+}
+
Index: /trunk/src/formats/md3_loader.cc
===================================================================
--- /trunk/src/formats/md3_loader.cc	(revision 281)
+++ /trunk/src/formats/md3_loader.cc	(revision 282)
@@ -275,6 +275,4 @@
 bool nv::md3_loader::load( stream& source )
 {
-	m_tags.clear();
-
 	m_md3 = (void*)(new md3_t);
 	if ( !read_md3( (md3_t*)m_md3, source ) )
@@ -285,7 +283,9 @@
 }
 
-void nv::md3_loader::load_tags( transform_vector& t, const std::string& tag )
+nv::key_raw_channel* nv::md3_loader::load_tags( const std::string& tag )
 {
 	md3_t* md3 = (md3_t*)m_md3;
+	key_raw_channel* result = key_raw_channel::create<md3_key>( md3->header.num_frames );
+	// TODO: is this brain damaged in efficiency (loop nest order) or what?
 	for ( sint32 f = 0; f < md3->header.num_frames; ++f )
 	{
@@ -300,9 +300,10 @@
 				vec3 axisy  ( md3_vec3( rtag.axis[2] ) );
 				vec3 origin ( md3_vec3( rtag.origin )  );
-				t.insert( transform( origin, quat( mat3( axisx, axisy, axisz ) ) ) );
+				((md3_key*)(result->data))[f].tform = transform( origin, quat( mat3( axisx, axisy, axisz ) ) );
 			}
 		}
 
 	}
+	return result;
 }
 
@@ -408,5 +409,6 @@
 		const md3_tag_t& rtag = md3->tags[i + md3->header.num_tags];
 		std::string name( (char*)(rtag.name) );
-		load_tags( result->get_map()[ name ], name );
+		nv::key_raw_channel* keys = load_tags( name );
+		result->insert( name, keys );
 	}
 	return result;
Index: /trunk/src/formats/md5_loader.cc
===================================================================
--- /trunk/src/formats/md5_loader.cc	(revision 281)
+++ /trunk/src/formats/md5_loader.cc	(revision 282)
@@ -397,5 +397,5 @@
 				remove_quotes( joint.name );
 				joint_infos.push_back( joint );
-				m_joints.push_back( md5_joint( joint.parent_id, m_num_frames ) );
+				m_joints.emplace_back( joint.parent_id );
 				next_line( sstream ); 
 			}
@@ -490,6 +490,6 @@
 	for ( size_t i = 0; i < m_num_joints; ++i )
 	{
-		const transform_vector& keys = m_joints[i].keys;
-		skeleton[i] = interpolate( keys.get(frame0), keys.get(frame1), interpolation );
+		const std::vector< transform >& keys = m_joints[i].keys;
+		skeleton[i] = interpolate( keys[frame0], keys[frame1], interpolation );
 	}
 }
@@ -519,7 +519,7 @@
 		if ( parent_id >= 0 ) // Has a parent joint
 		{
-			const transform_vector& ptv = m_joints[ size_t( parent_id ) ].keys;
+			const std::vector< transform >& ptv = m_joints[ size_t( parent_id ) ].keys;
 			transform ptr;
-			if ( ptv.size() > index ) ptr = ptv.get( index );
+			if ( ptv.size() > index ) ptr = ptv[ index ];
 			glm::vec3 rot_pos = ptr.get_orientation() * pos;
 
@@ -530,5 +530,5 @@
 		}
 
-		m_joints[i].keys.insert( transform( pos, orient ) );
+		m_joints[i].keys.push_back( transform( pos, orient ) );
 	}
 }
Index: /trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- /trunk/src/gfx/keyframed_mesh.cc	(revision 281)
+++ /trunk/src/gfx/keyframed_mesh.cc	(revision 282)
@@ -44,7 +44,7 @@
 {
 	NV_ASSERT( m_tag_map, "TAGMAP FAIL" );
-	const transform_vector* transforms = m_tag_map->get_tag( tag );
+	const transform* transforms = m_tag_map->get_tag( tag );
 	NV_ASSERT( transforms, "TAG FAIL" );
-	return interpolate( transforms->get( m_last_frame ), transforms->get( m_next_frame ), m_interpolation  );
+	return interpolate( transforms[ m_last_frame ], transforms[ m_next_frame ], m_interpolation  );
 }
 
