Index: /trunk/nv/formats/nmd_loader.hh
===================================================================
--- /trunk/nv/formats/nmd_loader.hh	(revision 469)
+++ /trunk/nv/formats/nmd_loader.hh	(revision 470)
@@ -65,5 +65,5 @@
 	{
 		uint16 frame_rate;
-		float  duration;
+		uint16 frame_count;
 		bool   flat;
 	};
Index: /trunk/nv/gfx/animation.hh
===================================================================
--- /trunk/nv/gfx/animation.hh	(revision 469)
+++ /trunk/nv/gfx/animation.hh	(revision 470)
@@ -117,6 +117,7 @@
 					return keyfresult;
 				}
-				index0 = math::clamp( int( time ), 0, int( channel.size() ) - 2 );
+				index0 = math::clamp( int( time ), 0, int( channel.size() ) - 1 );
 				index1 = index0 + 1;
+				if ( index1 == int( channel.size() ) ) index1 = 0;
 				factor = math::clamp( time - index0, 0.0f, 1.0f );
 			}
Index: /trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- /trunk/nv/gfx/keyframed_mesh.hh	(revision 469)
+++ /trunk/nv/gfx/keyframed_mesh.hh	(revision 470)
@@ -26,5 +26,5 @@
 		virtual transform get_node_transform( uint32 node_id ) const;
 		virtual mat4 get_node_matrix( uint32 node_id ) const;
-		virtual void update_animation( animation_entry*, uint32 a_anim_time );
+		virtual void update_animation( animation_entry*, uint32 a_ms_anim_time );
 		virtual void update( program a_program );
 		virtual ~keyframed_mesh();
Index: /trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- /trunk/nv/gfx/skeletal_mesh.hh	(revision 469)
+++ /trunk/nv/gfx/skeletal_mesh.hh	(revision 470)
@@ -21,5 +21,5 @@
  	public:
  		skeletal_animation_entry( shash64 name, const mesh_nodes_data* anim, bool a_looping )
-			: animation_entry( name, a_looping, anim->get_frame_rate(), 0.0f, anim->get_duration() )
+			: animation_entry( name, a_looping, anim->get_fps(), 0, anim->get_frame_count() )
 			, m_node_data( anim )
 		{
@@ -27,6 +27,6 @@
 		}
 
-		skeletal_animation_entry( shash64 name, const mesh_nodes_data* anim, float time_start, float time_end, bool a_looping )
-			: animation_entry( name, a_looping, anim->get_frame_rate(), time_start, time_end )
+		skeletal_animation_entry( shash64 name, const mesh_nodes_data* anim, uint32 time_start, uint32 time_end, bool a_looping )
+			: animation_entry( name, a_looping, anim->get_fps(), time_start, time_end )
 			, m_node_data( anim )
 		{
@@ -35,6 +35,7 @@
 
 		void prepare( const mesh_nodes_data* bones );
-		void update_skeleton( mat4* tr, uint32 time ) const;
+		void update_skeleton( mat4* tr, uint32 a_ms_time ) const;
 		~skeletal_animation_entry();
+
  	protected:
 		void initialize();
@@ -46,4 +47,5 @@
 		sint16* m_bone_ids;
 		mat4* m_offsets;
+		uint32 m_root;
 		bool m_prepared;
 		data_descriptor m_interpolation_key;
@@ -65,4 +67,6 @@
 		virtual transform get_node_transform( uint32 node_id ) const;
 		virtual mat4 get_node_matrix( uint32 node_id ) const;
+		virtual sint16 get_parent_id() const { return m_parent_id; }
+		virtual uint32 get_node_count() const { return m_bone_data ? m_bone_data->size() : 0; }
 		~skeletal_mesh()
 		{
@@ -77,4 +81,5 @@
 		uint32 m_index_count;
 		mat4*  m_transform;
+		sint16 m_parent_id;
 	};
 
Index: /trunk/nv/interface/animated_mesh.hh
===================================================================
--- /trunk/nv/interface/animated_mesh.hh	(revision 469)
+++ /trunk/nv/interface/animated_mesh.hh	(revision 470)
@@ -25,30 +25,29 @@
 	{
 	public:
-		animation_entry( shash64 name, bool looping, uint32 frame_rate, float a_start, float a_end ) : m_name( name ), m_looping( looping ), m_frame_rate( frame_rate ),
-		m_start( a_start ), m_end( a_end ), m_duration( m_end - m_start ) {}
+		animation_entry( shash64 name, bool looping, uint32 frame_rate, uint32 a_start, uint32 a_end ) : m_name( name ), m_looping( looping ), m_fps( frame_rate ),
+		m_fstart( a_start ), m_fend( a_end ) {}
 		shash64 get_name() const { return m_name; }
-		uint32 get_frame_rate() const { return m_frame_rate; }
-		float get_duration() const { return m_duration; }
-		float get_start() const { return m_start; }
-		float get_end() const  { return m_end; }
+		uint32 get_fps() const { return m_fps; }
+		// TODO : if loop +1?
+		uint32 get_frame_count() const { int todo;  return m_fend - m_fstart; }
+		uint32 get_start_frame() const { return m_fstart; }
+		uint32 get_end_frame() const  { return m_fend; }
 		bool is_looping() const { return m_looping; }
-		void set_range( float a_start, float a_end )
+		void set_frame_range( uint32 a_start, uint32 a_end )
 		{
-			m_start = a_start;
-			m_end   = a_end;
-			m_duration = m_end - m_start;
+			m_fstart = a_start;
+			m_fend   = a_end;
 		}
-		void set_frame_rate( uint32 rate )
+		void set_fps( uint32 rate )
 		{
-			m_frame_rate = rate;
+			m_fps = rate;
 		}
 		virtual ~animation_entry() {}
 	protected:
-		shash64 m_name;
-		bool    m_looping;
-		uint32  m_frame_rate;
-		float   m_start;
-		float   m_end;
-		float   m_duration;
+		shash64 m_name;    /// string hash of name
+		bool    m_looping; /// does the animation loop
+		uint32  m_fps;     /// frames per second
+		uint32  m_fstart;  /// start time (in frames)
+		uint32  m_fend;    /// end time (in frames)
 	};
 
@@ -61,4 +60,7 @@
 		virtual transform get_node_transform( uint32 ) const { return transform(); }
 		virtual mat4 get_node_matrix( uint32 ) const { return mat4(); }
+		virtual uint32 get_node_count() const { return 0; }
+		virtual sint16 get_parent_id() const { return -1; }
+
 	};
 
Index: /trunk/nv/interface/mesh_data.hh
===================================================================
--- /trunk/nv/interface/mesh_data.hh	(revision 469)
+++ /trunk/nv/interface/mesh_data.hh	(revision 470)
@@ -33,10 +33,10 @@
 
 		explicit mesh_nodes_data( shash64 name )
-			: m_name( name ), m_frame_rate(0), m_duration(0.0f), m_flat( false )
+			: m_name( name ), m_frame_rate(0), m_frame_count(0), m_flat( false )
 		{
 		}
 
-		explicit mesh_nodes_data( shash64 name, uint16 a_fps, float a_frames, bool a_flat )
-			: m_name( name ), m_frame_rate(a_fps), m_duration(a_frames), m_flat( a_flat )
+		explicit mesh_nodes_data( shash64 name, uint16 a_fps, uint16 a_frames, bool a_flat )
+			: m_name( name ), m_frame_rate(a_fps), m_frame_count(a_frames), m_flat( a_flat )
 		{
 		}
@@ -84,6 +84,6 @@
 
 		bool is_flat() const { return m_flat; }
-		uint16 get_frame_rate() const { return m_frame_rate; }
-		float get_duration() const { return m_duration; }
+		uint16 get_fps() const { return m_frame_rate; }
+		uint16 get_frame_count() const { return m_frame_count; }
 		shash64 get_name() const { return m_name; }
 
@@ -97,5 +97,5 @@
 		shash64 m_name;
 		uint16  m_frame_rate;
-		float   m_duration;
+		uint16  m_frame_count;
 		bool    m_flat;
 	};
Index: /trunk/src/formats/assimp_loader.cc
===================================================================
--- /trunk/src/formats/assimp_loader.cc	(revision 469)
+++ /trunk/src/formats/assimp_loader.cc	(revision 470)
@@ -350,5 +350,6 @@
 
 	uint16 frame_rate     = static_cast<uint16>( anim->mTicksPerSecond );
-	float  duration       = static_cast<float>( anim->mDuration );
+	int check_this;
+	uint16 duration       = static_cast<uint16>( anim->mDuration );
 	bool   flat           = false;
 
Index: /trunk/src/formats/nmd_loader.cc
===================================================================
--- /trunk/src/formats/nmd_loader.cc	(revision 469)
+++ /trunk/src/formats/nmd_loader.cc	(revision 470)
@@ -98,5 +98,5 @@
 	nmd_animation_header animation_header;
 	source.read( &animation_header, sizeof( animation_header ), 1 );
-	m_node_data = new mesh_nodes_data( e.name, animation_header.frame_rate, animation_header.duration, animation_header.flat );
+	m_node_data = new mesh_nodes_data( e.name, animation_header.frame_rate, animation_header.frame_count, animation_header.flat );
 	for ( uint32 i = 0; i < e.children; ++i )
 	{
@@ -215,6 +215,6 @@
 
 	nmd_animation_header aheader;
-	aheader.frame_rate = nodes.get_frame_rate();
-	aheader.duration = nodes.get_duration();
+	aheader.frame_rate  = nodes.get_fps();
+	aheader.frame_count = nodes.get_frame_count();
 	aheader.flat = nodes.is_flat();
 	stream_out.write( &aheader, sizeof( aheader ), 1 );
Index: /trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- /trunk/src/gfx/keyframed_mesh.cc	(revision 469)
+++ /trunk/src/gfx/keyframed_mesh.cc	(revision 470)
@@ -72,29 +72,33 @@
 }
 
-void nv::keyframed_mesh::update_animation( animation_entry* anim, uint32 a_anim_time )
+void nv::keyframed_mesh::update_animation( animation_entry* anim, uint32 a_ms_anim_time )
 {
 	if ( m_active )
 	{
-		float tick_time = ( static_cast<float>( a_anim_time ) * 0.001f ) * anim->get_frame_rate();
-		float duration  = anim->is_looping() ? anim->get_duration() + 1.0f : anim->get_duration();
-		if ( tick_time >= duration )
+		float  fframe   = ( static_cast<float>( a_ms_anim_time ) * 0.001f ) * anim->get_fps();
+		uint32 frame    = uint32( fframe );
+		float  reminder = fframe - static_cast<float>( frame );
+		uint32 duration = anim->is_looping() ? anim->get_frame_count() + 1 : anim->get_frame_count();
+
+		if ( frame >= duration )
 		{
 			if ( anim->is_looping() )
 			{
-				tick_time = fmodf( tick_time, duration );
+				frame  = frame % duration;
+				fframe = static_cast<float>( frame ) + reminder;
 			}
 			else
 			{
-				m_active     = false;
-				m_last_frame = static_cast<uint32>( anim->get_end() );
-				m_next_frame = m_last_frame;
+				m_active        = false;
+				m_last_frame    = anim->get_end_frame();
+				m_next_frame    = m_last_frame;
 				m_interpolation = 0.0f;
 				return;
 			}
 		}
-		m_last_frame    = static_cast<uint32>( math::floor( tick_time ) + anim->get_start() );
+		m_last_frame    = frame + anim->get_start_frame();
 		m_next_frame    = m_last_frame + 1;
-		if ( m_next_frame > static_cast<uint32>( anim->get_end() ) ) m_next_frame = static_cast<uint32>( anim->get_start() );
-		m_interpolation = tick_time - math::floor( tick_time );
+		if ( m_next_frame > anim->get_end_frame() ) m_next_frame = anim->get_start_frame();
+		m_interpolation = reminder;
 	}
 }
Index: /trunk/src/gfx/mesh_creator.cc
===================================================================
--- /trunk/src/gfx/mesh_creator.cc	(revision 469)
+++ /trunk/src/gfx/mesh_creator.cc	(revision 470)
@@ -9,4 +9,6 @@
 #include "nv/interface/data_channel_access.hh"
 
+#include "nv/core/logging.hh"
+
 struct nv_key_transform { nv::transform tform; };
 
@@ -16,8 +18,49 @@
 	merge_keys();
 	uint32 max_frames = 0;
-	for ( auto keys : m_data->m_data )
-	{
+
+	nv::vector< sint16 > ids;
+	{
+		// TODO: simplify this shit!
+		// The complexity here is that we cannot pre-transform in any order
+		// as the bones depend on previous bones, but ARE NOT IN ORDER
+		// 
+		// Either rewrite this a lot nicer, or sort the bones on creation
+		// by tree-order.
+
+		ids.reserve( m_data->m_data.size() );
+		{
+			nv::vector< sint16 > ids_next;
+			ids_next.reserve( m_data->m_data.size() );
+			ids_next.push_back( -1 );
+			while ( !ids_next.empty() )
+			{
+				sint16 pid = ids_next.back();
+				ids_next.pop_back();
+				for ( sint16 i = 0; i < sint16(m_data->m_data.size()); ++i )
+					if ( m_data->m_data[i]->get_parent_id() == pid )
+					{
+						sint16* it = nv::find( ids.begin(), ids.end(), i );
+						if ( it == ids.end() )
+						{
+							ids.push_back( i );
+							ids_next.push_back( i );
+						}
+					}
+			}
+		}
+
+		if ( ids.size() != m_data->m_data.size() )
+		{
+			NV_LOG_WARNING( "Bad skeleton!" );
+		}
+	}
+
+	NV_LOG_DEBUG( "ID/PID" );
+	for ( auto id : ids )
+	{
+		data_channel_set* keys = m_data->m_data[id];
 		sint16 parent_id = keys->get_parent_id();
-		data_channel_set* pkeys  = ( parent_id != -1 ? m_data->m_data[parent_id] : nullptr );
+		NV_LOG_DEBUG( "Id : ", id, " PID", parent_id );
+		data_channel_set* pkeys = ( parent_id != -1 ? m_data->m_data[parent_id] : nullptr );
 		size_t count     = ( keys ? keys->get_channel_size(0) : 0 );
 		size_t pcount    = ( pkeys ? pkeys->get_channel_size(0) : 0 );
@@ -26,4 +69,5 @@
 		{
 			data_channel_access< nv_key_transform > channel_creator( keys, 0 );
+
 			nv_key_transform* channel = channel_creator.data();
 			const nv_key_transform* pchannel = pkeys->get_channel(0)->data_cast< nv_key_transform >();
@@ -38,6 +82,6 @@
 	if ( m_data->m_frame_rate == 1 )
 	{
-		m_data->m_frame_rate = 32;
-		m_data->m_duration   = static_cast<float>( max_frames );
+		m_data->m_frame_rate  = 32;
+		m_data->m_frame_count = max_frames;
 	}
 
Index: /trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- /trunk/src/gfx/skeletal_mesh.cc	(revision 469)
+++ /trunk/src/gfx/skeletal_mesh.cc	(revision 470)
@@ -13,4 +13,5 @@
 void nv::skeletal_animation_entry::initialize()
 {
+	m_root      = uint32(-1);
 	m_prepared  = false;
 	m_children  = nullptr;
@@ -30,17 +31,53 @@
 				m_children[ node->get_parent_id()].push_back( n );
 			}
-		}
-	}
-}
-
-void nv::skeletal_animation_entry::update_skeleton( mat4* data, uint32 time ) const
-{
-	float tick_time = ( time * 0.001f ) * m_frame_rate;
-	float anim_time = m_start;
-	if ( m_duration > 0.0f ) anim_time += fmodf( tick_time, m_duration );
+			else
+			{
+				if ( m_root >= 0 )
+				{
+					m_root = uint32( -2 );
+				}
+				else
+					m_root = n;
+			}
+		}
+		NV_ASSERT( m_root != uint32( -1 ), "Animation without root!" );
+	}
+}
+
+void nv::skeletal_animation_entry::update_skeleton( mat4* data, uint32 a_ms_time ) const
+{
+	float  fframe   = ( a_ms_time * 0.001f ) * m_fps;
+	uint32 frame    = math::floor( fframe );
+	float  reminder = fframe - static_cast<float>( frame );
+	uint32 duration = get_frame_count();
+	if ( duration == 0 )
+	{
+		frame  = get_start_frame();
+		fframe = static_cast<float>( frame );
+	}
+	else if ( frame >= duration )
+	{
+		if ( is_looping() )
+		{
+			frame  = frame % duration;
+			fframe = static_cast<float>( frame ) + reminder;
+		}
+		else
+		{
+			frame  = get_end_frame();
+			fframe = static_cast<float>( frame );
+		}
+	}
 
 	if ( !m_node_data->is_flat() )
 	{
-		animate_rec( data, anim_time, 0, mat4() );
+		if ( m_root == uint32( -2 ) ) // multi-root
+		{
+			for ( uint32 n = 0; n < m_node_data->size(); ++n )
+				if ( ( *m_node_data )[n]->get_parent_id() == -1 )
+					animate_rec( data, fframe, n, mat4() );
+		}
+		else
+			animate_rec( data, fframe, m_root, mat4() );
 		return;
 	}
@@ -55,5 +92,5 @@
 			{
 				raw_channel_interpolator interpolator( node, m_interpolation_key );
-				node_mat = interpolator.get< mat4 >( anim_time );
+				node_mat = interpolator.get< mat4 >( fframe );
 			}
 
@@ -66,10 +103,10 @@
 {
 	if ( m_prepared ) return;
-	unordered_map< uint64, uint16 > bone_names;
+	nv::hash_store< shash64, uint16 > bone_names;
 	m_offsets = new mat4[ bones->size() ];
 	for ( nv::uint16 bi = 0; bi < bones->size(); ++bi )
 	{
 		const data_channel_set* bone = (*bones)[ bi ];
-		bone_names[ bone->get_name().value() ] = bi;
+		bone_names[ bone->get_name() ] = bi;
 		m_offsets[bi] = bone->get_transform();
 	}
@@ -80,5 +117,5 @@
 		sint16 bone_id = -1;
 
-		auto bi = bone_names.find( node->get_name().value() );
+		auto bi = bone_names.find( node->get_name() );
 		if ( bi != bone_names.end() )
 		{
@@ -129,5 +166,5 @@
 
 nv::skeletal_mesh::skeletal_mesh( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data )
-	: m_context( a_context ), m_bone_data( a_bone_data ), m_index_count( 0 ), m_transform( nullptr )
+	: m_context( a_context ), m_bone_data( a_bone_data ), m_index_count( 0 ), m_transform( nullptr ), m_parent_id(-1)
 {
 	if ( a_mesh )
@@ -135,4 +172,5 @@
 		m_va = a_context->create_vertex_array( a_mesh, nv::STATIC_DRAW );
 		m_index_count = a_mesh->get_channel_size( slot::INDEX );
+		m_parent_id = a_mesh->get_parent_id();
 	}
 	if ( m_bone_data )
Index: /trunk/src/wx/wx_canvas.cc
===================================================================
--- /trunk/src/wx/wx_canvas.cc	(revision 469)
+++ /trunk/src/wx/wx_canvas.cc	(revision 470)
@@ -6,4 +6,6 @@
 
 #include <nv/wx/wx_canvas.hh>
+
+#include <nv/core/time.hh>
 
 wxBEGIN_EVENT_TABLE( nv::wx_gl_canvas, wxWindow )
@@ -51,4 +53,5 @@
 void nv::wx_gl_canvas::on_idle( wxIdleEvent& evt )
 {
+	nv::sleep( 10 );
 	if ( m_render )
 	{
