Index: /trunk/nv/formats/md3_loader.hh
===================================================================
--- /trunk/nv/formats/md3_loader.hh	(revision 240)
+++ /trunk/nv/formats/md3_loader.hh	(revision 241)
@@ -36,5 +36,5 @@
 		virtual tag_map* create_tag_map();
 	private:
-		void load_tags( std::vector<transform>& t, const std::string& tag );
+		void load_tags( transform_vector& t, const std::string& tag );
 		void* m_md3;
 
Index: /trunk/nv/formats/md5_loader.hh
===================================================================
--- /trunk/nv/formats/md5_loader.hh	(revision 240)
+++ /trunk/nv/formats/md5_loader.hh	(revision 241)
@@ -61,44 +61,12 @@
 			size_t      start_index;
 		};
-		typedef std::vector<md5_joint_info> md5_joint_info_list;
 
-		struct md5_bound
+		struct md5_joint
 		{
-			glm::vec3 min;
-			glm::vec3 max;
+			int              parent;
+			transform_vector keys;
+
+			md5_joint( int a_parent, size_t reserve ) : parent( a_parent ) { keys.reserve( reserve ); }
 		};
-		typedef std::vector<md5_bound> md5_bound_list;
-
-		struct md5_base_frame
-		{
-			glm::vec3 pos;
-			glm::quat orient;
-		};
-		typedef std::vector<md5_base_frame> md5_base_frame_list;
-
-		struct md5_frame_data
-		{
-			int frame_id;
-			std::vector<float> frame_data;
-		};
-		typedef std::vector<md5_frame_data> md5_frame_data_list;
-
-		struct md5_skeleton_joint
-		{
-			md5_skeleton_joint() : parent(-1), pos(0) {}
-			md5_skeleton_joint( const md5_base_frame& copy ) : pos( copy.pos ), orient( copy.orient ) {}
-
-			int       parent;
-			glm::vec3 pos;
-			glm::quat orient;
-		};
-		typedef std::vector<md5_skeleton_joint> md5_skeleton_joint_list;
-
-		struct md5_frame_skeleton
-		{
-			md5_skeleton_joint_list joints;
-		};
-		typedef std::vector<md5_frame_skeleton> md5_frame_skeleton_list;
-
 
 	public:
@@ -107,49 +75,13 @@
 
 		bool load_animation( stream& source );
-		void update( float delta_time );
-		void reset_animation()
-		{
-			m_anim_time = 0;
-		}
-
-		const md5_frame_skeleton& get_skeleton() const
-		{
-			return m_animated_skeleton;
-		}
-
-		size_t get_num_joints() const
-		{
-			return m_num_joints;
-		}
-
-		size_t get_frame_rate() const
-		{
-			return m_frame_rate;
-		}
-
-		size_t get_frame_count() const
-		{
-			return m_num_frames;
-		}
-
-
-		const md5_joint_info& get_joint_info( uint32 index ) const
-		{
-			assert( index < m_joint_infos.size() );
-			return m_joint_infos[index];
-		}
+		void update_skeleton( std::vector<transform>& skeleton, float anim_time ) const;
+		
+		size_t get_num_joints() const { return m_num_joints; }
+		size_t get_frame_rate() const { return m_frame_rate; }
+		size_t get_frame_count() const { return m_num_frames; }
 
 	protected:
-
-		md5_joint_info_list     m_joint_infos;
-		md5_bound_list          m_bounds;
-		md5_base_frame_list     m_base_frames;
-		md5_frame_data_list     m_frames;
-		md5_frame_skeleton_list m_skeletons;
-
-		md5_frame_skeleton      m_animated_skeleton;
-
-		void build_frame_skeleton( md5_frame_skeleton_list& skeletons, const md5_joint_info_list& joint_info, const md5_base_frame_list& base_frames, const md5_frame_data& frame_data );
-		void interpolate_skeletons( md5_frame_skeleton& final_skeleton, const md5_frame_skeleton& skeleton0, const md5_frame_skeleton& skeleton1, float interpolate );
+		std::vector<md5_joint>  m_joints;
+		void build_frame_skeleton( const std::vector<md5_joint_info>& joint_info, const std::vector<transform>& base_frames, const std::vector<float>& frame_data );
 
 	private:
@@ -159,8 +91,25 @@
 		size_t m_frame_rate;
 		size_t m_num_animated_components;
+		float m_frame_duration;
+		float m_anim_duration;
+	};
 
-		float m_anim_duration;
-		float m_frame_duration;
-		float m_anim_time;
+	class md5_mesh_instance 
+	{
+		friend class md5_mesh_data;
+	public:
+		const void* data() const { return m_pntdata; }
+		uint32 get_index_count() const { return m_indices; }
+		size_t size() const { return m_size * sizeof( md5_vtx_pnt ); }
+		void apply( const std::vector< transform >& skeleton );
+		const md5_mesh_data* get_mesh_data() const { return m_data; }
+		~md5_mesh_instance() { delete[] m_pntdata; }
+	private:
+		md5_mesh_instance( const md5_mesh_data* a_data );
+
+		uint32               m_size;
+		uint32               m_indices;
+		md5_vtx_pnt*         m_pntdata;
+		const md5_mesh_data* m_data;
 	};
 
@@ -169,6 +118,6 @@
 	public:
 		friend class md5_loader;
-
-		void apply( const md5_animation& animation );
+		friend class md5_mesh_instance;
+		md5_mesh_instance* spawn() const;
 	private:
 		uint32*                     m_idata;
@@ -189,5 +138,4 @@
 		virtual mesh_data* release_mesh_data( size_t index = 0 );
 		virtual size_t get_mesh_count() const { return m_meshes.size(); }
-		bool check_animation( const md5_animation& animation ) const;
 	protected:
 
@@ -195,5 +143,4 @@
 		{
 			std::string name;
-			int   parent_id;
 			vec3  pos;
 			quat  orient;
Index: /trunk/nv/gfx/animation.hh
===================================================================
--- /trunk/nv/gfx/animation.hh	(revision 241)
+++ /trunk/nv/gfx/animation.hh	(revision 241)
@@ -0,0 +1,177 @@
+// Copyright (C) 2014 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#ifndef NV_ANIMATION_HH
+#define NV_ANIMATION_HH
+
+#include <nv/common.hh>
+#include <vector>
+#include <nv/interface/stream.hh>
+#include <nv/math.hh>
+#include <nv/transform.hh>
+#include <glm/gtc/matrix_transform.hpp>
+
+namespace nv
+{
+
+	// 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;
+			int index = -1;
+			for ( int i = 0 ; i < (int)m_keys.size() - 1 ; i++ )
+			{
+				if ( time < m_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 void dump( stream* out_stream )
+		{
+			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;
+	};
+
+	class key_animation_data
+	{
+	public:
+		virtual mat4 get_matrix( float time ) const = 0;
+		virtual transform get_transform( float time ) const = 0;
+		virtual void dump( stream* out_stream ) = 0;
+		virtual void load( stream* in_stream ) = 0;
+		virtual ~key_animation_data() {}
+	};
+
+
+	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; }
+		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;
+		}
+		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;
+		}
+		virtual void dump( stream* out_stream )
+		{
+			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 );
+		}
+	protected:
+		key_vector< nv::vec3 > m_positions;
+		key_vector< nv::quat > m_rotations;
+		key_vector< nv::vec3 > m_scales;
+	};
+
+	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 ); }
+		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(); }
+
+		virtual void dump( stream* out_stream )
+		{
+			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 );
+			}
+		}
+		virtual mat4 get_matrix( float time ) const
+		{
+			return get_transform( time ).extract();
+		}
+		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() - 1 );
+			float factor = glm::clamp<float> ( time - index, 0.0f, 1.0f );
+			return interpolate( m_keys[ index ], m_keys[ index + 1 ], factor );
+		}
+	protected:
+		std::vector< transform > m_keys;
+	};
+
+}
+
+#endif // NV_ANIMATION_HH
Index: /trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- /trunk/nv/gfx/skeletal_mesh.hh	(revision 240)
+++ /trunk/nv/gfx/skeletal_mesh.hh	(revision 241)
@@ -36,5 +36,5 @@
 	public:
 		skeletal_mesh( context* a_context, md5_mesh_data* a_mesh_data );
-		virtual size_t get_index_count() const { return m_mesh_data->get_count(); }
+		virtual size_t get_index_count() const { return m_mesh_data->get_index_count(); }
 		virtual void run_animation( animation_entry* a_anim );
 		virtual void setup_animation( md5_animation* a_anim );
@@ -42,6 +42,9 @@
 		virtual ~skeletal_mesh();
 	protected:
-		md5_mesh_data* m_mesh_data;
-		md5_animation* m_animation;
+		md5_mesh_instance* m_mesh_data;
+		md5_animation*     m_animation;
+
+		std::vector< transform > m_transform;
+		uint32                   m_animation_time;
 	};
 
Index: /trunk/nv/interface/mesh_data.hh
===================================================================
--- /trunk/nv/interface/mesh_data.hh	(revision 240)
+++ /trunk/nv/interface/mesh_data.hh	(revision 241)
@@ -43,4 +43,13 @@
 			return result;
 		}
+		static mesh_raw_channel* create( const vertex_descriptor& vtxdesc, uint32 count = 0 )
+		{
+			mesh_raw_channel* result = new mesh_raw_channel();
+			result->desc  = vtxdesc;
+			result->count = count;
+			result->size  = count * sizeof( vtxdesc.size );
+			result->data  = (count > 0 ? ( new uint8[ result->size ] ) : nullptr );
+			return result;
+		}
 	};
 
@@ -65,4 +74,14 @@
 			result->count = count;
 			result->size  = count * sizeof( ITYPE );
+			result->data  = (count > 0 ? ( new uint8[ result->size ] ) : nullptr );
+			return result;
+		}
+
+		static mesh_raw_index_channel* create( datatype etype, uint32 count = 0 )
+		{
+			mesh_raw_index_channel* result = new mesh_raw_index_channel();
+			result->etype = etype;
+			result->count = count;
+			result->size  = count * get_datatype_info( etype ).size;
 			result->data  = (count > 0 ? ( new uint8[ result->size ] ) : nullptr );
 			return result;
Index: /trunk/nv/interface/mesh_loader.hh
===================================================================
--- /trunk/nv/interface/mesh_loader.hh	(revision 240)
+++ /trunk/nv/interface/mesh_loader.hh	(revision 241)
@@ -19,4 +19,5 @@
 #include <nv/transform.hh>
 #include <nv/string.hh>
+#include <nv/gfx/animation.hh>
 #include <nv/interface/mesh_data.hh>
 #include <nv/interface/stream.hh>
@@ -28,11 +29,10 @@
 	{
 	public:
-		typedef std::vector< transform > transforms;
-		typedef std::unordered_map< std::string, transforms > map;
+		typedef std::unordered_map< std::string, transform_vector > map;
 
 		tag_map () {}
 		map& get_map()             { return m_map; }
 		const map& get_map() const { return m_map; }
-		const transforms* get_tag( const std::string& key ) const
+		const transform_vector* get_tag( const std::string& key ) const
 		{
 			auto it = m_map.find( key );
Index: /trunk/nv/math.hh
===================================================================
--- /trunk/nv/math.hh	(revision 240)
+++ /trunk/nv/math.hh	(revision 241)
@@ -216,5 +216,15 @@
 	};
 
-
+	template < typename T >
+	T interpolate( const T& lhs, const T& rhs, float f )
+	{
+		return glm::mix( lhs, rhs, f );
+	}
+
+	template <>
+	inline quat interpolate( const quat& lhs, const quat& rhs, float f )
+	{
+		return glm::slerp( lhs, rhs, f );
+	}
 
 } // namespace nv
Index: /trunk/nv/transform.hh
===================================================================
--- /trunk/nv/transform.hh	(revision 240)
+++ /trunk/nv/transform.hh	(revision 241)
@@ -62,5 +62,6 @@
 	};
 
-	inline transform interpolate( const transform& a, const transform& b, f32 value )
+	template<>
+	inline transform interpolate( const transform& a, const transform& b, float value )
 	{
 		return transform( 
Index: /trunk/src/formats/md3_loader.cc
===================================================================
--- /trunk/src/formats/md3_loader.cc	(revision 240)
+++ /trunk/src/formats/md3_loader.cc	(revision 241)
@@ -285,8 +285,7 @@
 }
 
-void nv::md3_loader::load_tags( std::vector<transform>& t, const std::string& tag )
+void nv::md3_loader::load_tags( transform_vector& t, const std::string& tag )
 {
 	md3_t* md3 = (md3_t*)m_md3;
-	t.clear();
 	for ( sint32 f = 0; f < md3->header.num_frames; ++f )
 	{
@@ -301,5 +300,5 @@
 				vec3 axisy  ( md3_vec3( rtag.axis[2] ) );
 				vec3 origin ( md3_vec3( rtag.origin )  );
-				t.emplace_back( origin, quat( mat3( axisx, axisy, axisz ) ) );
+				t.insert( transform( origin, quat( mat3( axisx, axisy, axisz ) ) ) );
 			}
 		}
Index: /trunk/src/formats/md5_loader.cc
===================================================================
--- /trunk/src/formats/md5_loader.cc	(revision 240)
+++ /trunk/src/formats/md5_loader.cc	(revision 241)
@@ -75,5 +75,6 @@
 			for ( size_t i = 0; i < m_num_joints; ++i )
 			{
-				sstream >> joint.name >> joint.parent_id;
+				int parent_id;
+				sstream >> joint.name >> parent_id;
 				discard( sstream, "(" );
 				sstream >> joint.pos.x >> joint.pos.y >> joint.pos.z;
@@ -290,5 +291,4 @@
 	, m_anim_duration( 0 )
 	, m_frame_duration( 0 )
-	, m_anim_time( 0 )
 {
 
@@ -302,9 +302,6 @@
 bool md5_animation::load_animation( stream& source )
 {
-	m_joint_infos.clear();
-	m_bounds.clear();
-	m_base_frames.clear();
-	m_frames.clear();
-	m_animated_skeleton.joints.clear();
+	std::vector<md5_joint_info> joint_infos;
+	std::vector<transform>      base_frames;
 	m_num_frames = 0;
 
@@ -332,4 +329,5 @@
 		{
 			sstream >> m_num_joints;
+			m_joints.reserve( m_num_joints );
 			next_line( sstream ); 
 		}
@@ -352,5 +350,6 @@
 				sstream >> joint.name >> joint.parent_id >> joint.flags >> joint.start_index;
 				remove_quotes( joint.name );
-				m_joint_infos.push_back( joint );
+				joint_infos.push_back( joint );
+				m_joints.push_back( md5_joint( joint.parent_id, m_num_frames ) );
 				next_line( sstream ); 
 			}
@@ -363,35 +362,36 @@
 			for ( size_t i = 0; i < m_num_frames; ++i ) 
 			{
-				md5_bound bound;
+//  				vec3 min;
+//  				vec3 max;
+//  				discard( sstream, "(" );
+//  				sstream >> min.x >> min.y >> min.z;
+//  				discard( sstream, ")" );
+//  				discard( sstream, "(" );
+//  				sstream >> max.x >> max.y >> max.z;
+// 				m_bounds.push_back( bound );
+				next_line( sstream ); 
+			}
+
+			discard( sstream, "}" );
+			next_line( sstream ); 
+		}
+		else if ( command == "baseframe" )
+		{
+			discard( sstream, "{" );
+			next_line( sstream ); 
+
+			for ( size_t i = 0; i < m_num_joints; ++i )
+			{
+				transform base_frame;
+				vec3 pos;
+				quat orient;
 				discard( sstream, "(" );
-				sstream >> bound.min.x >> bound.min.y >> bound.min.z;
+				sstream >> pos.x >> pos.y >> pos.z;
 				discard( sstream, ")" );
 				discard( sstream, "(" );
-				sstream >> bound.max.x >> bound.max.y >> bound.max.z;
-
-				m_bounds.push_back( bound );
-
+				sstream >> orient.x >> orient.y >> orient.z;
 				next_line( sstream ); 
-			}
-
-			discard( sstream, "}" );
-			next_line( sstream ); 
-		}
-		else if ( command == "baseframe" )
-		{
-			discard( sstream, "{" );
-			next_line( sstream ); 
-
-			for ( size_t i = 0; i < m_num_joints; ++i )
-			{
-				md5_base_frame base_frame;
-				discard( sstream, "(" );
-				sstream >> base_frame.pos.x >> base_frame.pos.y >> base_frame.pos.z;
-				discard( sstream, ")" );
-				discard( sstream, "(" );
-				sstream >> base_frame.orient.x >> base_frame.orient.y >> base_frame.orient.z;
-				next_line( sstream ); 
-
-				m_base_frames.push_back( base_frame );
+
+				base_frames.emplace_back( pos, orient );
 			}
 			discard( sstream, "}" );
@@ -400,20 +400,19 @@
 		else if ( command == "frame" )
 		{
-			md5_frame_data frame;
-			sstream >> frame.frame_id;
+			std::vector<float> frame;
+			int frame_id;
+			sstream >> frame_id;
 			discard( sstream, "{" );
 			next_line( sstream ); 
 
-			frame.frame_data.reserve( m_num_animated_components );
+			frame.reserve( m_num_animated_components );
 			char buf[50];
 			for ( size_t i = 0; i < m_num_animated_components; ++i )
 			{
 				sstream >> buf;
-				frame.frame_data.push_back((float)atof(buf));
+				frame.push_back((float)atof(buf));
 			}
 
-			m_frames.push_back(frame);
-
-			build_frame_skeleton( m_skeletons, m_joint_infos, m_base_frames, frame );
+			build_frame_skeleton( joint_infos, base_frames, frame );
 
 			discard( sstream, "}" );
@@ -424,29 +423,17 @@
 	} 
 
-	m_animated_skeleton.joints.assign( m_num_joints, md5_skeleton_joint() );
 
 	m_frame_duration = 1.0f / (float)m_frame_rate;
 	m_anim_duration = ( m_frame_duration * (float)m_num_frames );
-	m_anim_time = 0.0f;
-
-	assert( m_joint_infos.size() == m_num_joints );
-	assert( m_bounds.size()      == m_num_frames );
-	assert( m_base_frames.size() == m_num_joints );
-	assert( m_frames.size()      == m_num_frames );
-	assert( m_skeletons.size()   == m_num_frames );
 
 	return true;
 }
 
-void md5_animation::update( float delta_time )
-{
-	if ( m_num_frames < 1 ) return;
-
-	m_anim_time += delta_time;
-
-	while ( m_anim_time > m_anim_duration ) m_anim_time -= m_anim_duration;
-	while ( m_anim_time < 0.0f ) m_anim_time += m_anim_duration;
-
-	float frame_num = m_anim_time * (float)m_frame_rate;
+
+void nv::md5_animation::update_skeleton( std::vector<transform>& skeleton, float anim_time ) const
+{
+	NV_ASSERT( skeleton.size() == m_num_joints, "Incompatible skeleton passed!" );
+	anim_time = glm::clamp( anim_time, 0.0f, m_anim_duration );
+	float frame_num = anim_time * (float)m_frame_rate;
 	size_t frame0 = (size_t)floorf( frame_num );
 	size_t frame1 = (size_t)ceilf( frame_num );
@@ -454,13 +441,16 @@
 	frame1 = frame1 % m_num_frames;
 
-	float interpolate = fmodf( m_anim_time, m_frame_duration ) / m_frame_duration;
-
-	interpolate_skeletons( m_animated_skeleton, m_skeletons[frame0], m_skeletons[frame1], interpolate );
-}
-
-void md5_animation::build_frame_skeleton( md5_frame_skeleton_list& skeletons, const md5_joint_info_list& joint_infos, const md5_base_frame_list& base_frames, const md5_frame_data& frame_data )
-{
-	md5_frame_skeleton skeleton;
-
+	float interpolation = fmodf( anim_time, m_frame_duration ) / m_frame_duration;
+
+	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 );
+	}
+}
+
+void md5_animation::build_frame_skeleton( const std::vector<md5_joint_info>& joint_infos, const std::vector<transform>& base_frames, const std::vector<float>& frame_data )
+{
+	size_t index = m_joints[0].keys.size();
 	for ( unsigned int i = 0; i < joint_infos.size(); ++i )
 	{
@@ -468,68 +458,33 @@
 
 		const md5_joint_info& jinfo = joint_infos[i];
-		md5_skeleton_joint animated_joint = base_frames[i];
-
-		animated_joint.parent = jinfo.parent_id;
-
-		if ( jinfo.flags & 1 )  animated_joint.pos.x    = frame_data.frame_data[ jinfo.start_index + j++ ];
-		if ( jinfo.flags & 2 )  animated_joint.pos.y    = frame_data.frame_data[ jinfo.start_index + j++ ];
-		if ( jinfo.flags & 4 )  animated_joint.pos.z    = frame_data.frame_data[ jinfo.start_index + j++ ];
-		if ( jinfo.flags & 8 )  animated_joint.orient.x = frame_data.frame_data[ jinfo.start_index + j++ ];
-		if ( jinfo.flags & 16 ) animated_joint.orient.y = frame_data.frame_data[ jinfo.start_index + j++ ];
-		if ( jinfo.flags & 32 )	animated_joint.orient.z = frame_data.frame_data[ jinfo.start_index + j++ ];
-
-		unit_quat_w( animated_joint.orient );
-
-		if ( animated_joint.parent >= 0 ) // Has a parent joint
-		{
-			md5_skeleton_joint& pjoint = skeleton.joints[static_cast< size_t >( animated_joint.parent ) ];
-			glm::vec3 rot_pos = pjoint.orient * animated_joint.pos;
-
-			animated_joint.pos    = pjoint.pos + rot_pos;
-			animated_joint.orient = pjoint.orient * animated_joint.orient;
-
-			animated_joint.orient = glm::normalize( animated_joint.orient );
-		}
-
-		skeleton.joints.push_back( animated_joint );
-	}
-
-	skeletons.push_back( skeleton );
-}
-
-void md5_animation::interpolate_skeletons( md5_frame_skeleton& final_skeleton, const md5_frame_skeleton& skeleton0, const md5_frame_skeleton& skeleton1, float interpolate )
-{
-	for ( size_t i = 0; i < m_num_joints; ++i )
-	{
-		md5_skeleton_joint& final_joint = final_skeleton.joints[i];
-		const md5_skeleton_joint& joint0 = skeleton0.joints[i]; 
-		const md5_skeleton_joint& joint1 = skeleton1.joints[i];
-
-		final_joint.parent = joint0.parent;
-
-		final_joint.orient = glm::slerp( joint0.orient, joint1.orient, interpolate );
-		final_joint.pos    = glm::mix( joint0.pos, joint1.pos, interpolate );
-	}
-}
-
-bool md5_loader::check_animation( const md5_animation& animation ) const
-{
-	if ( m_num_joints != animation.get_num_joints() )
-	{
-		return false;
-	}
-
-	for ( uint32 i = 0; i < m_joints.size(); ++i )
-	{
-		const md5_joint& mjoint = m_joints[i];
-		const md5_animation::md5_joint_info& ajoint = animation.get_joint_info( i );
-
-		if ( mjoint.name != ajoint.name || mjoint.parent_id != ajoint.parent_id )
-		{
-			return false;
-		}
-	}
-
-	return true;
+
+
+		int parent_id = jinfo.parent_id;
+
+		vec3 pos    = base_frames[i].get_position();
+		quat orient = base_frames[i].get_orientation();
+		if ( jinfo.flags & 1 )  pos.x    = frame_data[ jinfo.start_index + j++ ];
+		if ( jinfo.flags & 2 )  pos.y    = frame_data[ jinfo.start_index + j++ ];
+		if ( jinfo.flags & 4 )  pos.z    = frame_data[ jinfo.start_index + j++ ];
+		if ( jinfo.flags & 8 )  orient.x = frame_data[ jinfo.start_index + j++ ];
+		if ( jinfo.flags & 16 ) orient.y = frame_data[ jinfo.start_index + j++ ];
+		if ( jinfo.flags & 32 )	orient.z = frame_data[ jinfo.start_index + j++ ];
+		unit_quat_w( orient );
+
+		if ( parent_id >= 0 ) // Has a parent joint
+		{
+			const transform_vector& ptv = m_joints[ size_t( parent_id ) ].keys;
+			transform ptr;
+			if ( ptv.size() > index ) ptr = ptv.get( index );
+			glm::vec3 rot_pos = ptr.get_orientation() * pos;
+
+			pos    = ptr.get_position() + rot_pos;
+			orient = ptr.get_orientation() * orient;
+
+			orient = glm::normalize( orient );
+		}
+
+		m_joints[i].keys.insert( transform( pos, orient ) );
+	}
 }
 
@@ -541,11 +496,28 @@
 }
 
-void nv::md5_mesh_data::apply( const md5_animation& animation )
-{
-	const md5_animation::md5_frame_skeleton& skeleton = animation.get_skeleton();
-
-	for ( unsigned int i = 0; i < m_vtx_data.size(); ++i )
-	{
-		const md5_vtx_data& vert = m_vtx_data[i];
+md5_mesh_instance* nv::md5_mesh_data::spawn() const
+{
+	return new md5_mesh_instance( this );
+}
+
+nv::md5_loader::~md5_loader()
+{
+	for ( auto m : m_meshes ) { if (m) delete m; }
+}
+
+nv::md5_mesh_instance::md5_mesh_instance( const md5_mesh_data* a_data ) 
+	: m_data( a_data ), m_size( 0 ), m_indices( 0 ), m_pntdata( nullptr )
+{
+	m_size = m_data->m_vtx_data.size();
+	m_indices = m_data->get_count();
+	m_pntdata = new md5_vtx_pnt[ m_size ];
+	std::copy_n( m_data->m_pntdata, m_size, m_pntdata );
+}
+
+void nv::md5_mesh_instance::apply( const std::vector< transform >& skeleton )
+{
+	for ( unsigned int i = 0; i < m_size; ++i )
+	{
+		const md5_vtx_data& vert = m_data->m_vtx_data[i];
 		md5_vtx_pnt& result = m_pntdata[i];
 
@@ -556,18 +528,13 @@
 		for ( size_t j = 0; j < vert.weight_count; ++j )
 		{
-			const md5_weight& weight = m_weights[vert.start_weight + j];
-			const md5_animation::md5_skeleton_joint& joint = skeleton.joints[weight.joint_id];
-
-			glm::vec3 rot_pos = joint.orient * weight.pos;
-			result.position += ( joint.pos + rot_pos ) * weight.bias;
-
-			result.normal  += ( joint.orient * vert.normal  ) * weight.bias;
-			result.tangent += ( joint.orient * vert.tangent ) * weight.bias;
-		}
-	}
-}
-
-nv::md5_loader::~md5_loader()
-{
-	for ( auto m : m_meshes ) { if (m) delete m; }
-}
+			const md5_weight& weight = m_data->m_weights[vert.start_weight + j];
+			const transform& joint = skeleton[weight.joint_id];
+
+			glm::vec3 rot_pos = joint.get_orientation() * weight.pos;
+			result.position += ( joint.get_position() + rot_pos ) * weight.bias;
+
+			result.normal  += ( joint.get_orientation() * vert.normal  ) * weight.bias;
+			result.tangent += ( joint.get_orientation() * vert.tangent ) * weight.bias;
+		}
+	}
+}
Index: /trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- /trunk/src/gfx/keyframed_mesh.cc	(revision 240)
+++ /trunk/src/gfx/keyframed_mesh.cc	(revision 241)
@@ -44,7 +44,7 @@
 {
 	NV_ASSERT( m_tag_map, "TAGMAP FAIL" );
-	const std::vector< transform >* transforms = m_tag_map->get_tag( tag );
+	const transform_vector* transforms = m_tag_map->get_tag( tag );
 	NV_ASSERT( transforms, "TAG FAIL" );
-	return interpolate( (*transforms)[ m_last_frame ], (*transforms)[ m_next_frame ], m_interpolation );
+	return interpolate( transforms->get( m_last_frame ), transforms->get( m_next_frame ), m_interpolation  );
 }
 
Index: /trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- /trunk/src/gfx/skeletal_mesh.cc	(revision 240)
+++ /trunk/src/gfx/skeletal_mesh.cc	(revision 241)
@@ -13,8 +13,10 @@
 nv::skeletal_mesh::skeletal_mesh( context* a_context, md5_mesh_data* a_mesh_data )
 	: animated_mesh()
-	, m_mesh_data( a_mesh_data )
+	, m_mesh_data( nullptr )
 	, m_animation( nullptr )
+	, m_animation_time( 0 )
 {
-	m_va = a_context->get_device()->create_vertex_array( a_mesh_data, nv::STREAM_DRAW );
+	m_mesh_data = a_mesh_data->spawn();
+	m_va        = a_context->get_device()->create_vertex_array( a_mesh_data, nv::STREAM_DRAW );
 }
 
@@ -22,20 +24,26 @@
 void nv::skeletal_mesh::setup_animation( md5_animation* a_anim )
 {
-	if ( m_animation ) m_animation->reset_animation();
-	m_animation = a_anim;
-	// TODO : INSTANCE!
-	m_animation->reset_animation();
+	m_animation      = a_anim;
+	m_animation_time = 0;
+	if ( m_animation )
+	{
+		m_transform.resize( m_animation->get_num_joints() );
+		update( 0 );
+	}
 }
 
 void nv::skeletal_mesh::update( uint32 ms )
 {
-	if (m_animation != nullptr)
+	if ( m_animation )
 	{
-		m_animation->update( ms * 0.001f );
-		m_mesh_data->apply( *m_animation );
+		m_animation_time += ms;
+		float frame_duration = 1000.f / (float)m_animation->get_frame_rate();
+		uint32 anim_duration = uint32( frame_duration * (float)m_animation->get_frame_count() );
+		while ( m_animation_time >= anim_duration ) m_animation_time -= anim_duration;
+		m_animation->update_skeleton( m_transform, (float)m_animation_time * 0.001f );
+		m_mesh_data->apply( m_transform );
 		vertex_buffer* vb = m_va->find_buffer( nv::POSITION );
-		const mesh_raw_channel* pch = m_mesh_data->get_channel_data()[0];
 		vb->bind();
-		vb->update( (const void*)pch->data, 0, pch->size );
+		vb->update( m_mesh_data->data(), 0, m_mesh_data->size() );
 		vb->unbind();
 	}
@@ -45,6 +53,5 @@
 {
 	delete m_va;
-	// TODO : INSTANCE!
-	//	delete m_mesh_data;
+	delete m_mesh_data;
 }
 
@@ -52,5 +59,4 @@
 {
 	skeletal_animation_entry * anim = down_cast<skeletal_animation_entry>(a_anim);
-	// TODO : INSTANCE!
 	setup_animation( anim->m_animation );
 }
