Index: trunk/nv/formats/nmd_loader.hh
===================================================================
--- trunk/nv/formats/nmd_loader.hh	(revision 283)
+++ trunk/nv/formats/nmd_loader.hh	(revision 283)
@@ -0,0 +1,205 @@
+// 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_NMD_LOADER_HH
+#define NV_NMD_LOADER_HH
+
+#include <nv/common.hh>
+#include <nv/interface/mesh_loader.hh>
+#include <nv/interface/mesh_data.hh>
+#include <nv/io/string_table.hh>
+
+namespace nv 
+{
+
+	enum class nmd_type : uint16
+	{
+		MESH,
+		STREAM,
+		BONE_ARRAY,
+		STRING_TABLE,
+		ANIMATION,
+		ANIMATION_NODE,
+		ANIMATION_CHANNEL,
+	};
+
+	enum class nmd_key_type : uint16
+	{
+		NONE,
+		TRANSFORM,
+	};
+
+	struct nmd_header
+	{
+		uint32 id;
+		uint32 version;
+		uint32 elements;
+	};
+
+	struct nmd_element_header
+	{
+		nmd_type type;
+		uint16   children;
+		uint32   size;
+	};
+
+	struct nmd_animation_header
+	{
+		float fps;
+		float duration;
+		bool  flat;
+	};
+
+	struct nmd_animation_node_header
+	{
+		uint16 name;
+		sint16 parent_id;
+		mat4   transform;
+	};
+
+	struct nmd_animation_channel_header
+	{
+		key_descriptor format;
+		uint32         count;
+	};
+
+	struct nmd_stream_header
+	{
+		vertex_descriptor format;
+		uint32 count;
+	};
+
+	struct nmd_bone
+	{
+		uint16 name;
+		mat4   offset;
+	};
+
+	struct nmd_node
+	{
+		uint16 name;
+		sint16 parent_id;
+		mat4   transform;
+		uint16 channel_count;
+		key_raw_channel** channels;
+	};
+
+	struct nmd_animation
+	{
+		float     fps;
+		float     duration;
+		bool      flat;
+		uint16    node_count;
+		nmd_node* nodes;
+
+		nmd_animation() : node_count(0), nodes( nullptr ) {}
+		void clear_node( nmd_node* node )
+		{
+			if ( node->channel_count > 0 )
+			{
+				for ( uint16 c = 0; c < node->channel_count; ++c )
+					delete node->channels[c];
+				delete[] node->channels;
+			}
+			node->channels = nullptr;
+			node->channel_count = 0;
+		}
+		~nmd_animation()
+		{
+			for ( uint16 n = 0; n < node_count; ++n )
+				clear_node( &nodes[n] );
+			delete[] nodes;
+		}
+	};
+
+	struct nmd_bone_data
+	{
+		nmd_bone* bones;
+		uint16    count;
+
+		nmd_bone_data() : count(0), bones( nullptr ) {}
+		~nmd_bone_data()
+		{
+			if ( bones ) delete[] bones;
+		}
+	};
+
+
+
+	class nmd_loader : public mesh_loader
+	{
+	public:
+		nmd_loader() : m_animation( nullptr ), m_bone_data( nullptr ), m_strings( nullptr ) {}
+		virtual bool load( stream& source );
+		virtual mesh_data* release_mesh_data( size_t index = 0 );
+		virtual nmd_animation* release_animation();
+		virtual nmd_bone_data* release_bone_data();
+		virtual string_table* release_string_table();
+		virtual size_t get_mesh_count() const { return m_meshes.size(); }
+		virtual ~nmd_loader();
+	private:
+		void reset();
+		bool load_mesh( stream& source, uint32 children );
+		bool load_bones( stream& source, uint32 children ); 
+		bool load_strings( stream& source ); 
+		bool load_animation( stream& source, uint32 children );
+
+		nmd_animation*            m_animation;
+		nmd_bone_data*            m_bone_data;
+		string_table*             m_strings;
+		std::vector< mesh_data* > m_meshes;
+	};
+
+	// TODO: Temporary, find a better way and remove!
+	struct nmd_temp_node_data
+	{
+		key_animation_data* keys;
+		mat4                transform;
+		sint32              bone_id;
+		std::vector< nmd_temp_node_data* > children;
+	};
+
+	class nmd_temp_model;
+
+	class nmd_temp_animation
+	{
+	public:
+		nmd_temp_animation( nmd_loader* loader );
+		void prepare( const nmd_temp_model* model );
+		void animate( mat4* data, uint32 time );
+		virtual uint32 get_frame_rate() const { return uint32(m_animation->fps); }
+		virtual uint32 get_frame_count() const { return uint32(m_animation->duration) / uint32(m_animation->fps); }
+		~nmd_temp_animation();
+	private:
+		void animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat );
+
+		std::vector< key_animation_data* > m_data;
+		std::vector< sint16 > m_bone_ids;
+		std::vector< std::vector< uint32 > > m_children;
+		const mat4*    m_offsets;
+		string_table*  m_strings;
+		nmd_animation* m_animation;
+	};
+
+	// TODO: Temporary, find a better way and remove!
+	class nmd_temp_model
+	{
+		friend class nmd_temp_animation;
+	public:
+		nmd_temp_model( nmd_loader* loader );
+		const mesh_data* get_data( uint32 index ) const { return m_mesh_data[index]; }
+		uint32 get_bone_count() const { return m_bone_offsets.size(); }
+		uint32 get_count() const { return m_mesh_data.size(); }
+		~nmd_temp_model();
+	private:
+		std::unordered_map< std::string, nv::uint16 > m_bone_names;
+		std::vector< mat4 >      m_bone_offsets;
+		std::vector< mesh_data*> m_mesh_data;
+	};
+
+}
+
+#endif // NV_NMD_LOADER_HH
Index: trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- trunk/nv/gfx/keyframed_mesh.hh	(revision 282)
+++ trunk/nv/gfx/keyframed_mesh.hh	(revision 283)
@@ -44,5 +44,5 @@
 		virtual void setup_animation( uint32 start, uint32 count, uint32 fps, bool loop );
 		virtual void set_frame( uint32 frame );
-		virtual void update( uint32 ms );
+		virtual void update_animation( animation_entry*, uint32 a_anim_time );
 		virtual void update( program* a_program ) const;
 		virtual ~keyframed_mesh();
@@ -65,5 +65,4 @@
 		uint32 m_last_frame;
 		uint32 m_next_frame;
-		uint32 m_time;
 		uint32 m_fps;
 		f32    m_interpolation;
Index: trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- trunk/nv/gfx/skeletal_mesh.hh	(revision 282)
+++ trunk/nv/gfx/skeletal_mesh.hh	(revision 283)
@@ -12,4 +12,5 @@
 #include <nv/interface/animated_mesh.hh>
 #include <nv/formats/md5_loader.hh>
+#include <nv/formats/nmd_loader.hh>
 
 namespace nv
@@ -26,4 +27,6 @@
 		virtual uint32 get_frame_count() const { return m_animation->get_frame_count(); }
 		virtual bool is_looping() const { return m_looping; }
+		uint32 get_num_joints() const { return m_animation->get_num_joints();}
+		void update_skeleton( transform* tr, float time ) const { return m_animation->update_skeleton( tr, time );}
 	protected:
 		md5_animation* m_animation;
@@ -31,5 +34,4 @@
 	};
 
-	// TODO: INSTANCING! currently md5_mesh data is deleted!
 	class skeletal_mesh : public animated_mesh
 	{
@@ -38,13 +40,41 @@
 		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 );
-		virtual void update( uint32 ms );
+		virtual void update_animation( animation_entry* a_anim, uint32 a_anim_time );
 		virtual ~skeletal_mesh();
 	protected:
 		md5_mesh_instance* m_mesh_data;
-		md5_animation*     m_animation;
+		std::vector< transform > m_transform;
+	};
 
-		std::vector< transform > m_transform;
-		uint32                   m_animation_time;
+ 	class skeletal_animation_entry_gpu : public animation_entry
+ 	{
+ 	public:
+ 		skeletal_animation_entry_gpu( const std::string& name, nmd_temp_animation* anim, bool a_looping ) 
+ 			: animation_entry( name ), m_animation( anim ), m_looping( a_looping ), m_prepared( false ) {}
+ 		virtual uint32 get_frame_rate() const { return 0; }
+ 		virtual uint32 get_frame_count() const { return 0; }
+ 		virtual bool is_looping() const { return m_looping; }
+		void prepare( const nmd_temp_model* m_model );
+		void update_skeleton( mat4* tr, uint32 time );
+ 	protected:
+ 		nmd_temp_animation* m_animation;
+		bool           m_prepared;
+ 		bool           m_looping;
+ 	};
+
+	class skeletal_mesh_gpu : public animated_mesh
+	{
+	public:
+		skeletal_mesh_gpu( device* a_device, const nmd_temp_model* a_model, uint32 index, bool primary );
+		virtual size_t get_index_count() const { return m_index_count; }
+		virtual void run_animation( animation_entry* a_anim );
+		virtual void update( program* a_program ) const;
+		virtual void update_animation( animation_entry* a_anim, uint32 
+			a_anim_time );
+	protected:
+		const nmd_temp_model* m_model;
+		bool m_primary;
+		uint32 m_index_count;
+		std::vector< mat4 > m_transform;
 	};
 
Index: trunk/nv/interface/animated_mesh.hh
===================================================================
--- trunk/nv/interface/animated_mesh.hh	(revision 282)
+++ trunk/nv/interface/animated_mesh.hh	(revision 283)
@@ -39,4 +39,5 @@
 	public:
 		animated_mesh() {}
+		virtual void update_animation( animation_entry*, uint32 ) {}
 		virtual void run_animation( animation_entry* ) {}
 		virtual transform get_tag( const std::string& ) const { return transform(); }
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 283)
+++ trunk/src/formats/nmd_loader.cc	(revision 283)
@@ -0,0 +1,303 @@
+// 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
+
+#include "nv/formats/nmd_loader.hh"
+#include "nv/io/std_stream.hh"
+
+using namespace nv;
+
+bool nv::nmd_loader::load( stream& source )
+{
+	// TODO: proper error handling
+	reset();
+	nmd_header root_header;
+	source.read( &root_header, sizeof( root_header ), 1 );
+	for ( uint32 i = 0; i < root_header.elements; ++i )
+	{
+		nmd_element_header element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		switch ( element_header.type )
+		{
+		case nmd_type::MESH           : load_mesh( source, element_header.children ); break;
+		case nmd_type::ANIMATION      : load_animation( source, element_header.children ); break;
+		case nmd_type::BONE_ARRAY     : load_bones( source, element_header.children ); break;
+		case nmd_type::STRING_TABLE   : load_strings( source ); break;
+		default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
+		}
+	}
+	return true;
+}
+
+bool nv::nmd_loader::load_mesh( stream& source, uint32 children )
+{
+	mesh_data* mesh = new mesh_data();
+	for ( uint32 s = 0; s < children; ++s )
+	{
+		nmd_element_header element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		NV_ASSERT( element_header.type == nmd_type::STREAM, "STREAM expected!" );
+
+		nmd_stream_header stream_header;
+		source.read( &stream_header, sizeof( stream_header ), 1 );
+		mesh_raw_channel* channel = mesh_raw_channel::create( stream_header.format, stream_header.count );
+		source.read( channel->data, stream_header.format.size, stream_header.count );
+		mesh->add_channel( channel );
+	}
+	m_meshes.push_back( mesh );
+	return true;
+}
+
+mesh_data* nv::nmd_loader::release_mesh_data( size_t index )
+{
+	mesh_data* result = m_meshes[ index ];
+	m_meshes[ index ] = nullptr;
+	return result;
+}
+
+void nv::nmd_loader::reset()
+{
+	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
+	if ( m_animation ) delete m_animation;
+	if ( m_strings )   delete m_strings;
+	if ( m_bone_data ) delete m_bone_data;
+	m_meshes.clear();
+	m_animation = nullptr;
+	m_bone_data = nullptr;
+	m_strings   = nullptr;
+}
+
+nv::nmd_loader::~nmd_loader()
+{
+	reset();
+}
+
+bool nv::nmd_loader::load_strings( stream& source )
+{
+	NV_ASSERT( m_strings == nullptr, "MULTIPLE STRING ENTRIES!" );
+	m_strings = new string_table( &source );
+	return true;
+}
+
+bool nv::nmd_loader::load_bones( stream& source, uint32 children )
+{
+	NV_ASSERT( m_bone_data == nullptr, "MULTIPLE BONE ENTRIES!" );
+	m_bone_data = new nmd_bone_data;
+	m_bone_data->bones = new nmd_bone[ children ];
+	m_bone_data->count = (uint16)children;
+	source.read( m_bone_data->bones, sizeof( nmd_bone ), children );
+	return true;
+}
+
+nmd_animation* nv::nmd_loader::release_animation()
+{
+	nmd_animation* result = m_animation;
+	m_animation = nullptr;
+	return result;
+}
+
+nmd_bone_data* nv::nmd_loader::release_bone_data()
+{
+	nmd_bone_data* result = m_bone_data;
+	m_bone_data = nullptr;
+	return result;
+}
+
+string_table* nv::nmd_loader::release_string_table()
+{
+	string_table* result = m_strings;
+	m_strings = nullptr;
+	return result;
+}
+
+
+bool nv::nmd_loader::load_animation( stream& source, uint32 children )
+{
+	NV_ASSERT( m_animation == nullptr, "MULTIPLE ANIMATION ENTRIES!" );
+	nmd_animation_header header;
+	source.read( &header, sizeof( header ), 1 );
+	m_animation = new nmd_animation;
+	m_animation->fps        = header.fps;
+	m_animation->duration   = header.duration;
+	m_animation->flat       = header.flat;
+	m_animation->node_count = (uint16)children;
+	m_animation->nodes      = new nmd_node[ children ];
+	for ( uint32 i = 0; i < children; ++i )
+	{
+		nmd_element_header element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		NV_ASSERT( element_header.type == nmd_type::ANIMATION_NODE, "ANIMATION_NODE expected!" );
+
+		uint16 ch_count = element_header.children;
+
+		nmd_animation_node_header node_header;
+		source.read( &node_header, sizeof( node_header ), 1 );
+		m_animation->nodes[i].name          = node_header.name;
+		m_animation->nodes[i].parent_id     = node_header.parent_id;
+		m_animation->nodes[i].transform     = node_header.transform;
+		m_animation->nodes[i].channel_count = ch_count;
+		m_animation->nodes[i].channels      = nullptr;
+		if ( ch_count > 0 )
+		{
+			m_animation->nodes[i].channels      = new key_raw_channel* [ch_count];
+			for ( uint32 c = 0; c < ch_count; ++c )
+			{
+				source.read( &element_header, sizeof( element_header ), 1 );
+				NV_ASSERT( element_header.type == nmd_type::ANIMATION_CHANNEL, "ANIMATION_CHANNEL expected!" );
+				nv::nmd_animation_channel_header cheader;
+				source.read( &cheader, sizeof( cheader ), 1 );
+				key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count );
+				source.read( channel->data, channel->desc.size, channel->count );
+				m_animation->nodes[i].channels[c] = channel;
+			}
+		}
+	}
+	return true;
+}
+
+
+// TEMPORARY
+nv::nmd_temp_animation::nmd_temp_animation( nmd_loader* loader )
+{
+	m_animation = loader->release_animation();
+	m_strings   = loader->release_string_table();
+
+	for ( uint32 n = 0; n < m_animation->node_count; ++n )
+	{
+		nmd_node& node = m_animation->nodes[n];
+		key_animation_data* keys = nullptr;
+		if ( node.channel_count > 1 )
+		{
+			keys = new nv::key_vectors_prs( node.channels[0], node.channels[1], node.channels[2] );
+		}
+		else if ( node.channel_count == 1 )
+		{
+			keys      =  new nv::transform_vector( node.channels[0] );
+			node.channels[0] = nullptr;
+			node.channel_count = 0;
+		}
+		m_data.push_back( keys );
+	}
+
+	if ( !m_animation->flat )
+	{
+		m_children.resize( m_animation->node_count );
+		for ( nv::uint32 n = 0; n < m_animation->node_count; ++n )
+		{
+			const nmd_node& node = m_animation->nodes[n];
+			if ( node.parent_id != -1 )
+			{
+				m_children[ node.parent_id ].push_back( n );
+			}
+		}
+	}
+
+	m_bone_ids.resize( m_animation->node_count );
+}
+
+nv::nmd_temp_animation::~nmd_temp_animation()
+{
+	for ( auto node : m_data ) delete node;
+	delete m_animation;
+	delete m_strings;
+}
+
+void nv::nmd_temp_animation::prepare( const nmd_temp_model* model )
+{
+	m_offsets = model->m_bone_offsets.data();
+	for ( uint32 n = 0; n < m_animation->node_count; ++n )
+	{
+		const nmd_node& node = m_animation->nodes[n];
+		sint16 bone_id = -1;
+
+		auto bi = model->m_bone_names.find( m_strings->get( node.name ) );
+		if ( bi != model->m_bone_names.end() )
+		{
+			bone_id = bi->second;
+		}
+		m_bone_ids[n] = bone_id;
+	}
+}
+
+void nv::nmd_temp_animation::animate( mat4* data, uint32 time )
+{
+	float tick_time = ( time / 1000.0f ) * m_animation->fps;
+	float anim_time = fmodf( tick_time, m_animation->duration );
+
+	if ( !m_animation->flat )
+	{
+		animate_rec( data, anim_time, 0, mat4() );
+		return;
+	}
+
+	for ( uint32 n = 0; n < m_animation->node_count; ++n )
+		if ( m_bone_ids[n] >= 0 )
+		{
+			const nmd_node* node = &m_animation->nodes[ n ];
+			nv::mat4 node_mat( node->transform );
+
+			if ( m_data[n] && !m_data[n]->empty() )
+			{
+				node_mat = m_data[n]->get_matrix( anim_time );
+			}
+
+			sint16 bone_id = m_bone_ids[n];
+			data[ bone_id ] = node_mat * m_offsets[ bone_id ];
+		}
+
+}
+
+void nv::nmd_temp_animation::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat )
+{
+	// TODO: fix transforms, which are now embedded,
+	//       see note in assimp_loader.cc:load_node
+	const nmd_node* node = &m_animation->nodes[ node_id ];
+	mat4 node_mat( node->transform );
+
+	if ( m_data[ node_id ] && !m_data[ node_id ]->empty() )
+	{
+		node_mat = m_data[ node_id ]->get_matrix( time );
+	}
+
+	mat4 global_mat = parent_mat * node_mat;
+
+	sint16 bone_id = m_bone_ids[ node_id ];
+	if ( bone_id >= 0 )
+	{
+		data[ bone_id ] = global_mat * m_offsets[ bone_id ];
+	}
+
+	for ( auto child : m_children[ node_id ] )
+	{
+		animate_rec( data, time, child, global_mat );
+	}
+}
+
+nv::nmd_temp_model::nmd_temp_model( nmd_loader* loader )
+{
+	for ( unsigned m = 0; m < loader->get_mesh_count(); ++m )
+	{
+		m_mesh_data.push_back(loader->release_mesh_data(m));
+	}
+	nmd_bone_data* bone_data = loader->release_bone_data();
+	string_table*  strings   = loader->release_string_table();
+
+	for ( nv::uint16 bi = 0; bi < bone_data->count; ++bi )
+	{
+		m_bone_names[ strings->get( bone_data->bones[bi].name ) ] = bi;
+		m_bone_offsets.push_back( bone_data->bones[bi].offset );
+	}
+
+	delete bone_data;
+	delete strings;
+}
+
+nv::nmd_temp_model::~nmd_temp_model()
+{
+	for ( unsigned m = 0; m < m_mesh_data.size(); ++m )
+	{
+		delete m_mesh_data[m];
+	}
+}
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 );
+}
