Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 282)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 283)
@@ -23,5 +23,4 @@
 	, m_last_frame( 0 )
 	, m_next_frame( 0 )
-	, m_time( 0 )
 	, m_fps( 0 )
 	, m_interpolation( 0.0f )
@@ -56,5 +55,4 @@
 	m_fps           = fps;
 	m_active        = count > 1;
-	m_time          = 0;
 	m_last_frame    = start;
 	m_next_frame    = (count > 1 ? start + 1 : start );
@@ -70,28 +68,17 @@
 }
 
-void keyframed_mesh::update( uint32 ms )
+void nv::keyframed_mesh::update_animation( animation_entry*, uint32 a_anim_time )
 {
 	if ( m_active )
 	{
-		m_time += ms;
 		uint32 f_diff = (m_stop_frame - m_start_frame);
-		float f_time  = 1000 / (float)m_fps;
+		float  f_time = 1000 / (float)m_fps;
 		float f_max   = ( m_looping ? ( f_diff + 1 ) : f_diff ) * f_time;
-		float f_pos   = m_time / f_time;
-
-		m_last_frame    = (uint32)glm::floor( f_pos ) + m_start_frame;
-		m_next_frame    = m_last_frame + 1;
-		if ( m_next_frame > m_stop_frame )
-		{
-			m_next_frame = m_start_frame;
-		}
-
-		if ( m_time >= f_max )
+		uint32 time   = a_anim_time;
+		if ( time >= f_max )
 		{
 			if ( m_looping )
 			{
-				uint32 left = m_time - static_cast< uint32 >( f_max );
-				m_time = 0;
-				update( left );
+				time = time % static_cast< uint32 >( f_max );
 			}
 			else
@@ -102,7 +89,13 @@
 			}
 		}
+		float f_pos   = time / f_time;
+
+		m_last_frame    = (uint32)glm::floor( f_pos ) + m_start_frame;
+		m_next_frame    = m_last_frame + 1;
+		if ( m_next_frame > m_stop_frame ) m_next_frame = m_start_frame;
 		m_interpolation = f_pos - glm::floor( f_pos );
 	}
 }
+
 
 void nv::keyframed_mesh::update( program* a_program ) const
@@ -148,5 +141,5 @@
 void nv::keyframed_mesh_gpu::update( uint32 ms )
 {
-	keyframed_mesh::update( ms );
+	animated_mesh::update( ms );
 
 	if ( m_gpu_last_frame != m_last_frame )
@@ -181,5 +174,5 @@
 void nv::keyframed_mesh_cpu::update( uint32 ms )
 {
-	keyframed_mesh::update( ms );
+	animated_mesh::update( ms );
 
 	const vertex_pn* data = m_mesh_data->get_channel_data<vertex_pn>();
Index: trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- trunk/src/gfx/skeletal_mesh.cc	(revision 282)
+++ trunk/src/gfx/skeletal_mesh.cc	(revision 283)
@@ -14,6 +14,4 @@
 	: animated_mesh()
 	, m_mesh_data( nullptr )
-	, m_animation( nullptr )
-	, m_animation_time( 0 )
 {
 	m_mesh_data = a_mesh_data->spawn();
@@ -21,25 +19,13 @@
 }
 
-
-void nv::skeletal_mesh::setup_animation( md5_animation* a_anim )
+void nv::skeletal_mesh::update_animation( animation_entry* a_anim, uint32 a_anim_time )
 {
-	m_animation      = a_anim;
-	m_animation_time = 0;
-	if ( m_animation )
+	if ( a_anim )
 	{
-		m_transform.resize( m_animation->get_num_joints() );
-		update( 0 );
-	}
-}
-
-void nv::skeletal_mesh::update( uint32 ms )
-{
-	if ( 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.data(), (float)m_animation_time * 0.001f );
+		skeletal_animation_entry * anim = (skeletal_animation_entry*)a_anim;
+		float frame_duration = 1000.f / (float)anim->get_frame_rate();
+		uint32 anim_duration = uint32( frame_duration * (float)anim->get_frame_count() );
+		uint32 new_time = a_anim_time % anim_duration;
+		anim->update_skeleton( m_transform.data(), (float)new_time * 0.001f );
 		m_mesh_data->apply( m_transform.data() );
 		vertex_buffer* vb = m_va->find_buffer( nv::slot::POSITION );
@@ -60,10 +46,51 @@
 	if ( a_anim != nullptr )
 	{
-		skeletal_animation_entry * anim = down_cast<skeletal_animation_entry>(a_anim);
-		setup_animation( anim->m_animation );
-	}
-	else
-	{
-		setup_animation( nullptr );
+		skeletal_animation_entry * anim = (skeletal_animation_entry*)(a_anim);
+		m_transform.resize( anim->get_num_joints() );
+		update_animation( a_anim, 0 );
 	}
 }
+
+void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time )
+{
+	m_animation->animate( data, time );
+}
+
+void nv::skeletal_animation_entry_gpu::prepare( const nmd_temp_model* m_model )
+{
+	m_animation->prepare( m_model );
+}
+
+nv::skeletal_mesh_gpu::skeletal_mesh_gpu( device* a_device, const nmd_temp_model* a_model, uint32 index, bool primary )
+	: animated_mesh(), m_primary( primary ), m_model( a_model )
+{
+	const mesh_data* data = a_model->get_data( index );
+	m_va          = a_device->create_vertex_array( data, nv::STATIC_DRAW );
+	m_index_count = data->get_count();
+}
+
+void nv::skeletal_mesh_gpu::run_animation( animation_entry* a_anim )
+{
+	if ( m_primary && a_anim != nullptr )
+	{
+		skeletal_animation_entry_gpu * anim = (skeletal_animation_entry_gpu*)(a_anim);
+		m_transform.resize( m_model->get_bone_count() );
+		anim->prepare( m_model );
+		update_animation( a_anim, 0 );
+	}
+}
+
+void nv::skeletal_mesh_gpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
+{
+	if ( m_primary && a_anim )
+	{
+		skeletal_animation_entry_gpu * anim = (skeletal_animation_entry_gpu*)a_anim;
+		anim->update_skeleton( m_transform.data(), a_anim_time );
+	}
+}
+
+void nv::skeletal_mesh_gpu::update( program* a_program ) const
+{
+	if (m_primary)
+		a_program->set_uniform_array( "nv_m_bones", m_transform );
+}
