Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 285)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 287)
@@ -15,5 +15,5 @@
 using namespace nv;
 
-nv::keyframed_mesh::keyframed_mesh( device* a_device, mesh_data* a_data, tag_map* a_tag_map )
+nv::keyframed_mesh::keyframed_mesh( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map )
 	: animated_mesh()
 	, m_mesh_data( a_data )
@@ -40,12 +40,18 @@
 }
 
-transform keyframed_mesh::get_tag( uint32 tag ) const
+transform keyframed_mesh::get_node_transform( uint32 node_id ) const
 {
 	NV_ASSERT( m_tag_map, "TAGMAP FAIL" );
-	const key_data* data = m_tag_map->get_tag( tag );
+	NV_ASSERT( node_id < m_tag_map->get_count(), "TAGMAP FAIL" );
+	const key_data* data = m_tag_map->get_node( node_id )->data;
 	NV_ASSERT( data, "TAG FAIL" );
 	transform last = data->get_raw_transform( m_last_frame );
 	transform next = data->get_raw_transform( m_next_frame );
 	return interpolate( last, next, m_interpolation  );
+}
+
+mat4 keyframed_mesh::get_node_matrix( uint32 node_id ) const
+{
+	return get_node_transform( node_id ).extract();
 }
 
@@ -125,5 +131,5 @@
 }
 
-nv::keyframed_mesh_gpu::keyframed_mesh_gpu( device* a_device, mesh_data* a_data, tag_map* a_tag_map, program* a_program )
+nv::keyframed_mesh_gpu::keyframed_mesh_gpu( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map, program* a_program )
 	: keyframed_mesh( a_device, a_data, a_tag_map )
 	, m_loc_next_position( 0 )
@@ -159,5 +165,5 @@
 }
 
-nv::keyframed_mesh_cpu::keyframed_mesh_cpu( device* a_device, mesh_data* a_data, tag_map* a_tag_map )
+nv::keyframed_mesh_cpu::keyframed_mesh_cpu( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map )
 	: keyframed_mesh( a_device, a_data, a_tag_map )
 {
Index: trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- trunk/src/gfx/skeletal_mesh.cc	(revision 285)
+++ trunk/src/gfx/skeletal_mesh.cc	(revision 287)
@@ -11,9 +11,17 @@
 
 
-nv::skeletal_mesh::skeletal_mesh( device* a_device, md5_mesh_data* a_mesh_data )
+nv::skeletal_mesh::skeletal_mesh( device* a_device, const mesh_data* a_mesh_data, const mesh_nodes_data* bones )
 	: animated_mesh()
-	, m_mesh_data( nullptr )
-{
-	m_mesh_data = a_mesh_data->spawn();
+	, m_data( a_mesh_data )
+{
+	const mesh_raw_channel* pnt_chan = a_mesh_data->get_channel<md5_vtx_pnt>();
+	m_pntdata.assign( (const md5_vtx_pnt*)pnt_chan->data, pnt_chan->count );
+	m_bone_offset.resize( bones->get_count() );
+	for ( uint32 i = 0; i < bones->get_count(); ++i )
+	{
+		m_bone_offset[i] = transform( bones->get_node(i)->transform );
+	}
+	m_vtx_data  = a_mesh_data->get_channel_data<md5_vtx_pntiw>();
+	m_indices   = a_mesh_data->get_count();
 	m_va        = a_device->create_vertex_array( a_mesh_data, nv::STREAM_DRAW );
 }
@@ -28,8 +36,36 @@
 		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() );
+
+		//m_mesh_data->apply( m_transform.data() );
+		{
+			size_t skeleton_size = m_bone_offset.size();
+			size_t vertex_count  = m_pntdata.size();
+			m_pos_offset.resize( skeleton_size );
+			for ( unsigned int i = 0; i < skeleton_size; ++i )
+			{
+				m_pos_offset[i] = m_transform[i] * m_bone_offset[i];
+			}
+
+			std::fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
+			for ( unsigned int i = 0; i < vertex_count; ++i )
+			{
+				const md5_vtx_pntiw& vert = m_vtx_data[i];
+
+				for ( size_t j = 0; j < 4; ++j )
+				{
+					int   index  = vert.boneindex[j];
+					float weight = vert.boneweight[j];
+					const quat& orient      = m_transform[index].get_orientation();
+					const transform& offset = m_pos_offset[index];
+					m_pntdata[i].position += offset.transformed( vert.position )        * weight;
+					m_pntdata[i].normal   += ( orient * vert.normal  ) * weight;
+					m_pntdata[i].tangent  += ( orient * vert.tangent ) * weight;
+				}
+			}
+		}
+
 		vertex_buffer* vb = m_va->find_buffer( nv::slot::POSITION );
 		vb->bind();
-		vb->update( m_mesh_data->data(), 0, m_mesh_data->size() );
+		vb->update( m_pntdata.data(), 0, m_pntdata.raw_size() );
 		vb->unbind();
 	}
@@ -39,5 +75,4 @@
 {
 	delete m_va;
-	delete m_mesh_data;
 }
 
@@ -52,29 +87,135 @@
 }
 
-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();
+
+nv::skeletal_animation_entry_gpu::skeletal_animation_entry_gpu( const std::string& name, const mesh_nodes_data* anim, bool a_looping ) 
+	: animation_entry( name )
+	, m_node_data( anim )
+{
+	uint32 node_count = m_node_data->get_count();
+
+	m_prepared  = false;
+	m_looping   = a_looping;
+	m_children  = nullptr;
+	m_offsets   = nullptr;
+	m_bone_ids  = new sint16[ node_count ];
+
+	if ( !m_node_data->is_flat() )
+	{
+		m_children = new std::vector< uint32 >[ node_count ];
+		for ( uint32 n = 0; n < node_count; ++n )
+		{
+			const mesh_node_data* node = m_node_data->get_node(n);
+			if ( node->parent_id != -1 )
+			{
+				m_children[ node->parent_id ].push_back( n );
+			}
+		}
+	}
+}
+
+void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time ) const
+{
+	float tick_time = ( time * 0.001f ) * m_node_data->get_frame_rate();
+	float anim_time = fmodf( tick_time, m_node_data->get_duration() );
+
+	if ( !m_node_data->is_flat() )
+	{
+		animate_rec( data, anim_time, 0, mat4() );
+		return;
+	}
+
+	for ( uint32 n = 0; n < m_node_data->get_count(); ++n )
+		if ( m_bone_ids[n] >= 0 )
+		{
+			const mesh_node_data* node = m_node_data->get_node(n);
+			nv::mat4 node_mat( node->transform );
+
+			if ( node->data )
+			{
+				node_mat = node->data->get_matrix( anim_time );
+			}
+
+			sint16 bone_id = m_bone_ids[n];
+			data[ bone_id ] = node_mat * m_offsets[ bone_id ];
+		}
+}
+
+void nv::skeletal_animation_entry_gpu::prepare( const mesh_nodes_data* bones )
+{
+	if ( m_prepared ) return;
+	std::unordered_map< std::string, nv::uint16 > bone_names;
+	m_offsets = new mat4[ bones->get_count() ];
+	for ( nv::uint16 bi = 0; bi < bones->get_count(); ++bi )
+	{
+		const mesh_node_data* bone = bones->get_node(bi);
+		bone_names[ bone->name ] = bi;
+		m_offsets[bi] = bone->transform;
+	}
+
+	for ( uint32 n = 0; n < m_node_data->get_count(); ++n )
+	{
+		const mesh_node_data* node = m_node_data->get_node(n);
+		sint16 bone_id = -1;
+
+		auto bi = bone_names.find( node->name );
+		if ( bi != bone_names.end() )
+		{
+			bone_id = bi->second;
+		}
+		m_bone_ids[n] = bone_id;
+	}
+	m_prepared = true;
+}
+
+void nv::skeletal_animation_entry_gpu::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
+{
+	// TODO: fix transforms, which are now embedded,
+	//       see note in assimp_loader.cc:load_node
+	const mesh_node_data* node = m_node_data->get_node( node_id );
+	mat4 node_mat( node->transform );
+
+	if ( node->data )
+	{
+		node_mat = node->data->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::skeletal_animation_entry_gpu::~skeletal_animation_entry_gpu()
+{
+	delete[] m_offsets;
+	delete[] m_children;
+	delete[] m_bone_ids;
+}
+
+nv::skeletal_mesh_gpu::skeletal_mesh_gpu( device* a_device, const mesh_data* a_mesh, const mesh_nodes_data* a_bone_data )
+	: animated_mesh(), m_bone_data( a_bone_data ), m_transform( nullptr )
+{
+	m_va          = a_device->create_vertex_array( a_mesh, nv::STATIC_DRAW );
+	m_index_count = a_mesh->get_count();
+	if ( m_bone_data )
+	{
+		m_transform = new mat4[ m_bone_data->get_count() ];
+	}
 }
 
 void nv::skeletal_mesh_gpu::run_animation( animation_entry* a_anim )
 {
-	if ( m_primary && a_anim != nullptr )
+	if ( m_bone_data && 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 );
+		anim->prepare( m_bone_data );
 		update_animation( a_anim, 0 );
 	}
@@ -83,8 +224,8 @@
 void nv::skeletal_mesh_gpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
 {
-	if ( m_primary && a_anim )
+	if ( m_bone_data && a_anim )
 	{
 		skeletal_animation_entry_gpu * anim = (skeletal_animation_entry_gpu*)a_anim;
-		anim->update_skeleton( m_transform.data(), a_anim_time );
+		anim->update_skeleton( m_transform, a_anim_time );
 	}
 }
@@ -92,5 +233,15 @@
 void nv::skeletal_mesh_gpu::update( program* a_program ) const
 {
-	if (m_primary)
-		a_program->set_uniform_array( "nv_m_bones", m_transform );
-}
+	if ( m_bone_data )
+		a_program->set_uniform_array( "nv_m_bones", m_transform, m_bone_data->get_count() );
+}
+
+nv::transform nv::skeletal_mesh_gpu::get_node_transform( uint32 node_id ) const
+{
+	return transform( m_transform[ node_id ] );
+}
+
+nv::mat4 nv::skeletal_mesh_gpu::get_node_matrix( uint32 node_id ) const
+{
+	return m_transform[ node_id ];
+}
