Index: trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- trunk/nv/gfx/keyframed_mesh.hh	(revision 158)
+++ trunk/nv/gfx/keyframed_mesh.hh	(revision 158)
@@ -0,0 +1,54 @@
+// Copyright (C) 2012-2013 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_KEYFRAMED_MESH_HH
+#define NV_KEYFRAMED_MESH_HH
+
+#include <nv/common.hh>
+#include <nv/formats/md3_loader.hh>
+
+namespace nv
+{
+
+	class keyframed_mesh
+	{
+	public:
+		keyframed_mesh( context* a_context, keyframed_mesh_data* a_data, program* a_program );
+		mat4 get_tag( const std::string& tag ) const;
+		uint32 get_max_frames() const;
+		void set_frame( uint32 frame );
+		void setup_animation( uint32 start, uint32 stop, uint32 fps, bool loop );
+		void update( uint32 ms );
+		void draw( render_state& rstate );
+		program* get_program() { return m_program; }
+		~keyframed_mesh();
+	private:
+		context*             m_context;
+		keyframed_mesh_data* m_data;
+		program*             m_program;
+		vertex_array*        m_va;
+
+		int m_loc_next_position;
+		int m_loc_last_position;
+		int m_loc_next_normal;
+		int m_loc_last_normal;
+
+		uint32 m_last_frame;
+		uint32 m_next_frame;
+		uint32 m_gpu_last_frame;
+		uint32 m_gpu_next_frame;
+		f32    m_interpolation;
+		uint32 m_start_frame;
+		uint32 m_stop_frame;
+		uint32 m_time;
+		uint32 m_fps;
+		bool   m_looping;
+		bool   m_active;
+	};
+
+} // namespace nv
+
+#endif // NV_KEYFRAMED_MESH_HH
Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 158)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 158)
@@ -0,0 +1,148 @@
+// Copyright (C) 2011 Kornel Kisielewicz
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/gfx/keyframed_mesh.hh"
+
+#include <glm/gtc/matrix_access.hpp>
+#include <glm/gtx/matrix_interpolation.hpp>
+#include "nv/interface/context.hh"
+#include "nv/interface/device.hh"
+
+
+#include "nv/logging.hh"
+
+using namespace nv;
+
+keyframed_mesh::keyframed_mesh( context* a_context, keyframed_mesh_data* a_data, program* a_program )
+	: m_context( a_context )
+	, m_data( a_data )
+	, m_program( a_program )
+	, m_va( nullptr )
+	, m_loc_next_position( 0 )
+	, m_loc_last_position( 0 )
+	, m_loc_next_normal( 0 )
+	, m_loc_last_normal( 0 )
+	, m_last_frame( 0 )
+	, m_next_frame( 0 )
+	, m_gpu_last_frame( 0xFFFFFFFF )
+	, m_gpu_next_frame( 0xFFFFFFFF )
+	, m_interpolation( 0.0f )
+	, m_start_frame( false )
+	, m_stop_frame( false )
+	, m_looping( false )
+	, m_active( false )
+{
+	m_va = m_context->get_device()->create_vertex_array();
+
+	nv::vertex_buffer* vb;
+	m_loc_next_position = m_program->get_attribute( "next_position" )->get_location();
+	m_loc_last_position = m_program->get_attribute( "last_position" )->get_location();
+	m_loc_next_normal   = m_program->get_attribute( "next_normal" )->get_location();
+	m_loc_last_normal   = m_program->get_attribute( "last_normal" )->get_location();
+
+	vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec3 ) * m_data->get_frame_count(), (void*)m_data->get_positions().data() );
+	m_va->add_vertex_buffer( m_loc_next_position, vb, nv::FLOAT, 3, 0, 0, false );
+	m_va->add_vertex_buffer( m_loc_last_position, vb, nv::FLOAT, 3 );
+
+	vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec3 ) * m_data->get_frame_count(), (void*)m_data->get_normals().data() );
+	m_va->add_vertex_buffer( m_loc_next_normal, vb, nv::FLOAT, 3, 0, 0, false );
+	m_va->add_vertex_buffer( m_loc_last_normal, vb, nv::FLOAT, 3 );
+
+	vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec2 ), (void*)m_data->get_texcoords().data() );
+	m_va->add_vertex_buffer( m_program->get_attribute( "texcoord" )->get_location(), vb, nv::FLOAT, 2 );
+	nv::index_buffer* ib = m_context->get_device()->create_index_buffer( nv::STATIC_DRAW, m_data->get_index_count() * sizeof( nv::uint16 ), (void*)m_data->get_indices().data() );
+	m_va->set_index_buffer( ib, nv::USHORT, true );
+}
+
+mat4 keyframed_mesh::get_tag( const std::string& tag ) const
+{
+	const std::vector< nv::mat4 >& transforms = m_data->get_tag_map().at( tag );
+	return glm::interpolate( transforms[ m_last_frame ], transforms[ m_next_frame ], m_interpolation );
+}
+
+uint32 keyframed_mesh::get_max_frames() const
+{
+	return m_data->get_frame_count();
+}
+
+void keyframed_mesh::set_frame( uint32 frame )
+{
+	m_last_frame    = frame;
+	m_next_frame    = frame;
+	m_interpolation = 0.0f;
+	m_active        = false;
+}
+
+void keyframed_mesh::setup_animation( uint32 start, uint32 stop, uint32 fps, bool loop )
+{
+	m_start_frame   = start;
+	m_stop_frame    = stop;
+	m_looping       = loop;
+	m_fps           = fps;
+	m_active        = ( stop > start );
+	m_last_frame    = start;
+	m_next_frame    = (stop > start ? start + 1 : start );
+	m_interpolation = 0.0f;
+	m_time          = 0;
+}
+
+void keyframed_mesh::update( uint32 ms )
+{
+	if ( m_active )
+	{
+		m_time += ms;
+		uint32 f_diff = (m_stop_frame - m_start_frame);
+		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 )
+		{
+			if ( m_looping )
+			{
+				uint32 left = m_time - f_max;
+				m_time = 0;
+				update( left );
+			}
+			else
+			{
+				m_active     = false;
+				m_last_frame = m_stop_frame;
+				m_next_frame = m_stop_frame;
+			}
+		}
+		m_interpolation = f_pos - glm::floor( f_pos );
+	}
+}
+
+void nv::keyframed_mesh::draw( render_state& rstate )
+{
+	nv::uint32 vtx_count = m_data->get_vertex_count();
+	if ( m_gpu_last_frame != m_last_frame )
+	{
+		m_va->update_vertex_buffer( m_loc_last_position, m_last_frame * vtx_count * sizeof( nv::vec3 ) );
+		m_va->update_vertex_buffer( m_loc_last_normal,   m_last_frame * vtx_count * sizeof( nv::vec3 ) );
+		m_gpu_last_frame = m_last_frame;
+	}
+	if ( m_gpu_next_frame != m_next_frame )
+	{
+		m_va->update_vertex_buffer( m_loc_next_position, m_next_frame * vtx_count * sizeof( nv::vec3 ) );
+		m_va->update_vertex_buffer( m_loc_next_normal,   m_next_frame * vtx_count * sizeof( nv::vec3 ) );
+		m_gpu_next_frame = m_next_frame;
+	}
+	m_program->set_uniform( "interpolate", m_interpolation );
+	m_context->draw( nv::TRIANGLES, rstate, m_program, m_va, m_data->get_index_count() );
+}
+
+nv::keyframed_mesh::~keyframed_mesh()
+{
+	delete m_va;
+}
