Index: /trunk/nv/core/resource.hh
===================================================================
--- /trunk/nv/core/resource.hh	(revision 480)
+++ /trunk/nv/core/resource.hh	(revision 481)
@@ -146,5 +146,4 @@
 	class resource_lock
 	{
-		static const uint64 hash_value = rtti_type_hash<T>::value;
 	public:
 		resource_lock() = delete;
@@ -171,5 +170,5 @@
 			if ( this != &other )
 			{
-				if ( m_handler ) m_handler->unlock( m_id, shash64( hash_value ) );
+				if ( m_handler ) m_handler->unlock( m_id, shash64( rtti_type_hash<T>::value ) );
 				m_id       = other.m_id;
 				m_handler  = other.m_handler;
@@ -185,5 +184,5 @@
 		~resource_lock()
 		{
-			if ( m_handler ) m_handler->unlock( m_id, shash64( hash_value ) );
+			if ( m_handler ) m_handler->unlock( m_id, shash64( rtti_type_hash<T>::value ) );
 		}
 	private:
Index: /trunk/nv/gfx/skeleton_instance.hh
===================================================================
--- /trunk/nv/gfx/skeleton_instance.hh	(revision 480)
+++ /trunk/nv/gfx/skeleton_instance.hh	(revision 481)
@@ -16,24 +16,84 @@
 {
 
+	struct frame_range
+	{
+		uint32 start;
+		uint32 end;
+		bool   is_looping;
+
+		constexpr frame_range() : start( 0 ), end( 0 ), is_looping( false ) {}
+		constexpr frame_range( uint32 fstart, uint32 fend, bool fis_looping )
+			: start( fstart )
+			, end( fend )
+			, is_looping( fis_looping )
+		{
+		}
+		constexpr uint32 duration() const { return end - start; }
+
+		inline float clamp_frame( float f ) const
+		{
+			if ( end <= start ) return float( start );
+			float max = float( end - start );
+			if ( f < max ) return f;
+			return is_looping ? nv::fmodf( f, max ) : float( end );
+		}
+
+		inline float frame_from_time_fps( float time, uint32 fps ) const
+		{
+			return clamp_frame( time * float( fps ) );
+		}
+
+		inline float frame_from_ms_fps( uint32 ms, uint32 fps ) const
+		{
+			return clamp_frame( ( ms * 0.001f ) * float( fps ) );
+		}
+	};
+
+	class bone_transforms
+	{
+	public:
+		bone_transforms() {}
+		void prepare( const mesh_nodes_data* bone_data );
+		uint32 size() const { return m_offsets.size(); };
+	protected:
+		dynamic_array< mat4 >   m_offsets;
+
+		friend class skeleton_instance;
+	};
+
 	class skeleton_binding
 	{
 	public:
-		explicit skeleton_binding()
-			: m_indices( nullptr )
-			, m_offsets( nullptr )
+		skeleton_binding() {}
+		void prepare( const mesh_nodes_data* node_data, const mesh_nodes_data* bone_data );
+		uint32 size() const { return m_indices.size(); };
+		uint32 skeleton_size() const { return m_bone_count; };
+	protected:
+		dynamic_array< sint16 > m_indices;
+		data_descriptor         m_key;
+		uint32                  m_bone_count;
+
+		friend class skeleton_instance;
+		friend class skeleton_transforms;
+	};
+
+	class skeleton_transforms
+	{
+	public:
+		skeleton_transforms() {}
+		const transform* transforms() const { return m_transforms.data(); }
+		size_t size() const { return m_transforms.size(); }
+		void animate( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame )
 		{
-		}
-		void prepare( const mesh_nodes_data* node_data, const mesh_nodes_data* bone_data );
-		~skeleton_binding()
-		{
-			delete[] m_indices;
-			delete[] m_offsets;
+			if ( m_transforms.size() != binding.skeleton_size() )
+				m_transforms.resize( binding.skeleton_size() );
+			NV_ASSERT( !node_data->is_flat(), "animation data is flat!" );
+			for ( uint32 n = 0; n < node_data->size(); ++n )
+				if ( ( *node_data )[n]->get_parent_id() == -1 )
+					animate_rec( node_data, binding, frame, n, transform() );
 		}
 	protected:
-		sint16*                m_indices;
-		mat4*                  m_offsets;
-		data_descriptor        m_key;
-
-		friend class skeleton_instance;
+		void animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent );
+		dynamic_array< transform > m_transforms;
 	};
 
@@ -41,22 +101,15 @@
 	{
 	public:
-		explicit skeleton_instance( uint32 bone_count )
-		{
-			initialize( bone_count );
-		}
-		void initialize( uint32 bone_count )
-		{
-			if ( bone_count > m_transform.size() )
-				m_transform.resize( bone_count );
-		}
-		const mat4* transforms() const { return m_transform.data(); }
-		size_t size() const { return m_transform.size(); }
+		skeleton_instance() {}
+		const mat4* transforms() const { return m_matrix.data(); }
+		size_t size() const { return m_matrix.size(); }
+		void assign( const skeleton_transforms& skeleton, const bone_transforms& binding );
+		void assign( const bone_transforms& binding );
+		//		void animate_( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame );
+	protected:
+// 		void animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent );
+// 		void animate_flat( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame );
 
-		void animate( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame );
-	protected:
-		void animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent );
-		void animate_flat( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame );
-
-		dynamic_array< mat4 > m_transform;
+		dynamic_array< mat4 >      m_matrix;
 	};
 
Index: /trunk/src/gfx/skeleton_instance.cc
===================================================================
--- /trunk/src/gfx/skeleton_instance.cc	(revision 480)
+++ /trunk/src/gfx/skeleton_instance.cc	(revision 481)
@@ -11,16 +11,14 @@
 void nv::skeleton_binding::prepare( const mesh_nodes_data* node_data, const mesh_nodes_data* bone_data )
 {
-	if ( !m_offsets || !m_indices )
+	if ( m_indices.empty() )
 	{
 		// TODO: either fixed size struct or static allocator
 		hash_store< shash64, uint16 > bone_names;
-		m_offsets = new mat4[bone_data->size()];
-		m_indices = new sint16[node_data->size()];
-
+		m_indices.resize( node_data->size() );
+		
 		for ( nv::uint16 bi = 0; bi < bone_data->size(); ++bi )
 		{
 			const data_channel_set* bone = ( *bone_data )[bi];
 			bone_names[bone->get_name()] = bi;
-			m_offsets[bi] = bone->get_transform();
 		}
 
@@ -38,4 +36,5 @@
 
 		}
+		m_bone_count = bone_data->size();
 	}
 
@@ -50,27 +49,86 @@
 	}
 
+
 }
 
-void nv::skeleton_instance::animate( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame )
+// void nv::skeleton_instance::animate_( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame )
+// {
+// 	if ( m_matrix.size() > 0 )
+// 	{
+// 		if ( node_data->is_flat() )
+// 		{
+// 			animate_flat( node_data, binding, frame );
+// 		}
+// 		else
+// 		{
+// 			for ( uint32 n = 0; n < node_data->size(); ++n )
+// 				if ( ( *node_data )[n]->get_parent_id() == -1 )
+// 					animate_rec( node_data, binding, frame, n, transform() );
+// 		}
+// 	}
+// }
+// 
+// void nv::skeleton_instance::animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent )
+// {
+// 	// TODO: fix transforms, which are now embedded,
+// 	//       see note in assimp_loader.cc:load_node
+// 	const data_channel_set* node = ( *node_data )[id];
+// 	transform node_mat( node->get_transform() );
+// 
+// 	if ( node->size() > 0 )
+// 	{
+// 		raw_channel_interpolator interpolator( node, binding.m_key );
+// 		node_mat = interpolator.get< transform >( frame );
+// 	}
+// 
+// 	transform global_mat = parent * node_mat;
+// 
+//  	sint16 bone_id = binding.m_indices[id];
+//  	if ( bone_id >= 0 )
+//  	{
+//  		m_matrix[bone_id] = global_mat.extract() * binding.m_offsets[bone_id];
+//  	}
+// 
+// 	for ( auto child : node_data->children( id ) )
+// 	{
+// 		animate_rec( node_data, binding, frame, child, global_mat );
+// 	}
+// }
+// 
+// void nv::skeleton_instance::animate_flat( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame ) 
+// {
+// 	for ( uint32 n = 0; n < node_data->size(); ++n )
+// 		if ( binding.m_indices[n] >= 0 )
+// 		{
+// 			const data_channel_set* node = ( *node_data )[n];
+// 			nv::mat4 node_mat( node->get_transform() );
+// 
+// 			if ( node->size() > 0 )
+// 			{
+// 				raw_channel_interpolator interpolator( node, binding.m_key );
+// 				node_mat = interpolator.get< mat4 >( frame );
+// 			}
+// 			sint16 bone_id = binding.m_indices[n];
+// 			m_matrix[bone_id] = node_mat * binding.m_offsets[bone_id];
+// 		}
+// }
+
+void nv::skeleton_instance::assign( const skeleton_transforms& skeleton, const bone_transforms& bones )
 {
-	if ( m_transform.size() > 0 )
-	{
-		if ( node_data->is_flat() )
-		{
-			animate_flat( node_data, binding, frame );
-		}
-		else
-		{
-			for ( uint32 n = 0; n < node_data->size(); ++n )
-				if ( ( *node_data )[n]->get_parent_id() == -1 )
-					animate_rec( node_data, binding, frame, n, transform() );
-		}
-	}
+	if ( bones.size() != m_matrix.size() ) 
+		m_matrix.resize( bones.size() );
+	const transform* transforms = skeleton.transforms();
+	for ( uint32 n = 0; n < skeleton.size(); ++n )
+		m_matrix[n] = transforms[n].extract() * bones.m_offsets[n];
 }
 
-void nv::skeleton_instance::animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent )
+void nv::skeleton_instance::assign( const bone_transforms& bones )
 {
-	// TODO: fix transforms, which are now embedded,
-	//       see note in assimp_loader.cc:load_node
+	if ( bones.size() != m_matrix.size() )
+		m_matrix.resize( bones.size() );
+}
+
+void nv::skeleton_transforms::animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent )
+{
 	const data_channel_set* node = ( *node_data )[id];
 	transform node_mat( node->get_transform() );
@@ -81,13 +139,10 @@
 		node_mat = interpolator.get< transform >( frame );
 	}
-
+	sint16 bone_id = binding.m_indices[id];
 	transform global_mat = parent * node_mat;
-
-	sint16 bone_id = binding.m_indices[id];
 	if ( bone_id >= 0 )
 	{
-		m_transform[bone_id] = global_mat.extract() * binding.m_offsets[bone_id];
+		m_transforms[bone_id] = global_mat;
 	}
-
 	for ( auto child : node_data->children( id ) )
 	{
@@ -96,20 +151,15 @@
 }
 
-void nv::skeleton_instance::animate_flat( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame ) 
+void nv::bone_transforms::prepare( const mesh_nodes_data* bone_data )
 {
-	for ( uint32 n = 0; n < node_data->size(); ++n )
-		if ( binding.m_indices[n] >= 0 )
+	if ( m_offsets.empty() )
+	{
+		m_offsets.resize( bone_data->size() );
+
+		for ( nv::uint16 bi = 0; bi < bone_data->size(); ++bi )
 		{
-			const data_channel_set* node = ( *node_data )[n];
-			nv::mat4 node_mat( node->get_transform() );
-
-			if ( node->size() > 0 )
-			{
-				raw_channel_interpolator interpolator( node, binding.m_key );
-				node_mat = interpolator.get< mat4 >( frame );
-			}
-
-			sint16 bone_id = binding.m_indices[n];
-			m_transform[bone_id] = node_mat * binding.m_offsets[bone_id];
+			const data_channel_set* bone = ( *bone_data )[bi];
+			m_offsets[bi] = bone->get_transform();
 		}
+	}
 }
