Index: trunk/nv/formats/assimp_loader.hh
===================================================================
--- trunk/nv/formats/assimp_loader.hh	(revision 283)
+++ trunk/nv/formats/assimp_loader.hh	(revision 284)
@@ -70,5 +70,5 @@
 	{
 	public:
-		assimp_loader( const string& a_ext, const mat4& a_rotate_transform, float a_scale, uint32 a_assimp_flags = 0 );
+		assimp_loader( const string& a_ext, const mat3& a_rotate_transform, float a_scale, uint32 a_assimp_flags = 0 );
 		explicit assimp_loader( const string& a_ext, uint32 a_assimp_flags = 0 );
 		virtual bool load( stream& source );
@@ -81,4 +81,5 @@
 		void scene_report() const;
 	private:
+		void initialize( const string& a_ext, const mat3& a_rotate_transform, float a_scale, uint32 a_assimp_flags );
 		uint32 load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id );
 		uint32 count_nodes( const void* node ) const;
@@ -88,5 +89,6 @@
 		string_table_creator m_strings;
 		string m_ext;
-		mat4   m_rotate_transform;
+		mat3   m_r33;
+		mat3   m_ri33;
 		float  m_scale;
 		uint32 m_assimp_flags;
Index: trunk/nv/formats/nmd_loader.hh
===================================================================
--- trunk/nv/formats/nmd_loader.hh	(revision 283)
+++ trunk/nv/formats/nmd_loader.hh	(revision 284)
@@ -27,10 +27,4 @@
 	};
 
-	enum class nmd_key_type : uint16
-	{
-		NONE,
-		TRANSFORM,
-	};
-
 	struct nmd_header
 	{
@@ -43,4 +37,5 @@
 	{
 		nmd_type type;
+		uint16   name;
 		uint16   children;
 		uint32   size;
@@ -56,5 +51,4 @@
 	struct nmd_animation_node_header
 	{
-		uint16 name;
 		sint16 parent_id;
 		mat4   transform;
@@ -143,8 +137,8 @@
 	private:
 		void reset();
-		bool load_mesh( stream& source, uint32 children );
-		bool load_bones( stream& source, uint32 children ); 
+		bool load_mesh( stream& source, const nmd_element_header& e );
+		bool load_bones( stream& source, const nmd_element_header& e ); 
 		bool load_strings( stream& source ); 
-		bool load_animation( stream& source, uint32 children );
+		bool load_animation( stream& source, const nmd_element_header& e );
 
 		nmd_animation*            m_animation;
Index: trunk/nv/gfx/animation.hh
===================================================================
--- trunk/nv/gfx/animation.hh	(revision 283)
+++ trunk/nv/gfx/animation.hh	(revision 284)
@@ -51,4 +51,56 @@
 			return result;
 		}
+
+		uint32 interpolate_raw( float time, float* result ) const 
+		{
+			uint32 keyfsize   = desc.size / 4;
+			uint32 keyfresult = keyfsize;
+			const float* fdata = (const float*)data;
+			if ( count == 0 ) return 0;
+
+			uint32 slot = 0;
+			int index    = -1;
+			float factor = 1.0f;
+			if ( desc.slots[0].vslot == animation_slot::TIME )
+			{
+				slot++;
+				keyfresult--;
+				if ( count == 1 ) 
+				{
+					std::copy_n( fdata + 1, keyfresult, result );
+					return keyfresult;
+				}
+				uint32 toffset = desc.slots[0].offset / 4;
+				for ( int i = 0 ; i < (int)count - 1 ; i++ )
+				{
+					if ( time < fdata[ i * keyfsize + keyfsize + toffset ] ) { index = i; break; }
+				}
+				NV_ASSERT( index >= 0, "animation time fail!");
+				float time0  = fdata[ index * keyfsize + toffset ];
+				float time1  = fdata[ index * keyfsize + keyfsize + toffset ];
+				float delta  = time1 - time0;
+				factor = glm::clamp( (time - time0) / delta, 0.0f, 1.0f );
+			}
+			else
+			{
+				if ( count == 1 ) 
+				{
+					std::copy_n( fdata, keyfresult, result );
+					return keyfresult;
+				}
+				index  = glm::clamp<int>( int( time ), 0, count - 2 );
+				factor = glm::clamp<float> ( time - index, 0.0f, 1.0f );
+			}
+			uint32 ret = 0;
+			for ( ; slot < desc.count; ++slot )
+			{
+				ret += nv::interpolate_raw( 
+					desc.slots[slot], factor, 
+					fdata + index * keyfsize + desc.slots[slot].offset / 4,
+					fdata + index * keyfsize + keyfsize + desc.slots[slot].offset / 4,
+					result + ret );
+			}
+			return ret;
+		}
 	};
 
@@ -62,5 +114,40 @@
 			NV_ASSERT( channel, "nullptr passed to add_channel!" );
 			m_channels.push_back( channel );
-		}
+			for ( uint16 i = 0; i < channel->desc.count; ++i )
+			{
+				const key_descriptor_slot& ksi = channel->desc.slots[i];
+				if ( ksi.vslot != animation_slot::TIME )
+				{
+					uint32 index = final_key.count;
+					final_key.slots[ index ].offset = final_key.size;
+					final_key.slots[ index ].etype  = ksi.etype;
+					final_key.slots[ index ].vslot  = ksi.vslot;
+					final_key.size += get_datatype_info( ksi.etype ).size;
+					final_key.count++;
+				}
+			}
+		}
+
+		mat4 get_matrix( float time ) const
+		{
+			float key[ 16 ];
+			float* pkey = key;
+			for ( uint16 i = 0; i < m_channels.size(); ++i )
+			{
+				pkey += m_channels[i]->interpolate_raw( time, pkey );
+			}
+			return extract_matrix_raw( final_key, key );
+		};
+
+		transform get_transform( float time ) const
+		{
+			float key[ 16 ];
+			float* pkey = key;
+			for ( uint16 i = 0; i < m_channels.size(); ++i )
+			{
+				pkey += m_channels[i]->interpolate_raw( time, pkey );
+			}
+			return extract_transform_raw( final_key, key );
+		};
 
 		virtual ~key_data()
@@ -69,4 +156,5 @@
 		}
 	private:
+		key_descriptor final_key;
 		std::vector< key_raw_channel* > m_channels;
 	};
Index: trunk/nv/interface/vertex.hh
===================================================================
--- trunk/nv/interface/vertex.hh	(revision 283)
+++ trunk/nv/interface/vertex.hh	(revision 284)
@@ -663,4 +663,123 @@
 	}
 
+	inline uint32 interpolate_raw_linear( uint32 count, float factor, const float* k1, const float* k2, float* result )
+	{
+		for ( uint32 i = 0; i < count; ++i )
+		{
+			result[i] = k1[i] + factor * (k2[i] - k1[i]); 
+		}
+		return count;
+	}
+
+	inline uint32 interpolate_raw_quat( float factor, const float* k1, const float* k2, float* result )
+	{
+		*((quat*)(result)) = interpolate( *((quat*)(k1)), *((quat*)(k2)), factor );
+		return 4;
+	}
+
+	inline uint32 interpolate_raw( const key_descriptor_slot& slot, float factor, const float* k1, const float* k2, float* result )
+	{
+		uint32 count = get_datatype_info( slot.etype ).elements;
+		switch ( slot.vslot )
+		{
+		case animation_slot::TIME:     return 0;
+		case animation_slot::POSITION: return interpolate_raw_linear( count, factor, k1, k2, result );
+		case animation_slot::ROTATION: return interpolate_raw_quat( factor, k1, k2, result );
+		case animation_slot::SCALE:    return interpolate_raw_linear( count, factor, k1, k2, result );
+		case animation_slot::TFORM:    return 
+										   interpolate_raw_linear( 3, factor, k1, k2, result ) + 
+										   interpolate_raw_quat( factor, k1 + 3, k2 + 3, result + 3 );
+		default:
+			return 0;
+		};
+	}
+
+	inline quat make_quat_fixed( const float* data )
+	{
+		quat result;
+		memcpy((float*)(&result), data, sizeof(quat));
+		return result;
+	}
+
+	inline mat4 extract_matrix_raw( const key_descriptor& desc, const float* data )
+	{
+		if ( desc.count == 1 )
+		{
+			switch ( desc.slots[0].vslot )
+			{
+			case animation_slot::TIME:     return mat4();
+			case animation_slot::POSITION: return glm::translate( mat4(),glm::make_vec3( data ) );
+			case animation_slot::ROTATION: return glm::mat4_cast( make_quat_fixed( data ) );
+			case animation_slot::SCALE:    return glm::scale( mat4(),glm::make_vec3( data ) );
+			case animation_slot::TFORM:    return transform( glm::make_vec3( data ), make_quat_fixed( data + 3 ) ).extract();
+			default:
+				return mat4();
+			};
+		}
+		else
+		{
+			mat4 position;
+			mat4 rotation;
+			mat4 scale;
+			for ( uint32 i = 0; i < desc.count; ++i )
+			{
+				uint32 offset = desc.slots[i].offset / 4;
+				switch ( desc.slots[i].vslot )
+				{
+				case animation_slot::TIME:     break;
+				case animation_slot::POSITION: 
+					position = glm::translate( position,glm::make_vec3( data + offset ) ); break;
+				case animation_slot::ROTATION: 
+					rotation = glm::mat4_cast( make_quat_fixed( data + offset ) ); break;
+				case animation_slot::SCALE:    
+					scale    = glm::scale( mat4(),glm::make_vec3( data + offset ) ); break;
+				case animation_slot::TFORM:    return transform( glm::make_vec3( data + offset ), make_quat_fixed( data + offset + 3 ) ).extract();
+				default:
+					break;
+				}
+			}
+			return position * rotation * scale;
+		}
+	}
+
+	inline transform extract_transform_raw( const key_descriptor& desc, const float* data )
+	{
+		if ( desc.count == 1 )
+		{
+			switch ( desc.slots[0].vslot )
+			{
+			case animation_slot::TIME:     return transform();
+			case animation_slot::POSITION: return transform( glm::make_vec3( data ) );
+			case animation_slot::ROTATION: return transform( make_quat_fixed( data ) );
+			case animation_slot::SCALE:    return transform();
+			case animation_slot::TFORM:    return transform( glm::make_vec3( data ), make_quat_fixed( data + 3 ) );
+			default:
+				return transform();
+			};
+		}
+		else
+		{
+			vec3 position;
+			quat rotation;
+			for ( uint32 i = 0; i < desc.count; ++i )
+			{
+				uint32 offset = desc.slots[i].offset / 4;
+				switch ( desc.slots[i].vslot )
+				{
+				case animation_slot::TIME:     break;
+				case animation_slot::POSITION: 
+					position = glm::make_vec3( data + offset ); break;
+				case animation_slot::ROTATION: 
+					rotation = make_quat_fixed( data + offset ); break;
+				case animation_slot::SCALE:	   break;
+				case animation_slot::TFORM:    return transform( glm::make_vec3( data + offset ), make_quat_fixed( data + 3 ) );
+				default:
+					break;
+				}
+			}
+			return transform( position, rotation );
+		}
+	}
+
 }
 
Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 283)
+++ trunk/src/formats/assimp_loader.cc	(revision 284)
@@ -15,6 +15,22 @@
 const int MAX_BONES = 64;
 
-nv::assimp_loader::assimp_loader( const string& a_ext, const mat4& a_rotate_transform, float a_scale, uint32 a_assimp_flags /*= 0 */ ) : m_ext( a_ext ), m_rotate_transform( a_rotate_transform ), m_scale( a_scale ), m_assimp_flags( a_assimp_flags ), m_mesh_count(0), m_scene( nullptr )
-{
+nv::assimp_loader::assimp_loader( const string& a_ext, const mat3& a_rotate_transform, float a_scale, uint32 a_assimp_flags /*= 0 */ ) : m_mesh_count(0), m_scene( nullptr )
+{
+	initialize( a_ext, a_rotate_transform, a_scale, a_assimp_flags );
+}
+
+nv::assimp_loader::assimp_loader( const string& a_ext, uint32 a_assimp_flags /*= 0 */ ) : m_mesh_count(0), m_scene( nullptr )
+{
+	initialize( a_ext, mat3(), 1.0f, a_assimp_flags );
+}
+
+
+void nv::assimp_loader::initialize( const string& a_ext, const mat3& a_rotate_transform, float a_scale, uint32 a_assimp_flags )
+{
+	m_ext   = a_ext;
+	m_r33   = a_rotate_transform;
+	m_ri33  = glm::transpose( m_r33 );
+	m_scale = a_scale;
+	m_assimp_flags = a_assimp_flags;
 	if ( m_assimp_flags == 0 )
 	{
@@ -36,24 +52,5 @@
 }
 
-nv::assimp_loader::assimp_loader( const string& a_ext, uint32 a_assimp_flags /*= 0 */ ) : m_ext( a_ext ), m_rotate_transform(), m_scale( 1.0f ), m_assimp_flags( a_assimp_flags ), m_mesh_count(0), m_scene( nullptr )
-{
-	if ( m_assimp_flags == 0 )
-	{
-		m_assimp_flags = ( 
-			aiProcess_CalcTangentSpace				|  
-			aiProcess_GenSmoothNormals				|  
-			aiProcess_JoinIdenticalVertices			|   
-			aiProcess_ImproveCacheLocality			|  
-			aiProcess_LimitBoneWeights				|  
-			aiProcess_RemoveRedundantMaterials      |  
-			aiProcess_SplitLargeMeshes				|  
-			aiProcess_Triangulate					|  
-			aiProcess_GenUVCoords                   |  
-			aiProcess_SortByPType                   |  
-			aiProcess_FindDegenerates               |  
-			aiProcess_FindInvalidData               |  
-			0 );
-	}
-}
+
 bool nv::assimp_loader::load( stream& source )
 {
@@ -86,9 +83,7 @@
 	const aiMesh*  mesh  = scene->mMeshes[ index ];
 
-	mat3 scaled_rotatation = glm::mat3( glm::scale( m_scale, m_scale, m_scale ) * m_rotate_transform );
-	//mat3 scaled_rotatation = glm::mat3( m_rotate_transform );
 	vec3 vertex_offset     = glm::vec3(); 
-	mat3 vertex_transform  = scaled_rotatation;
-	mat3 normal_transform  = glm::mat3( m_rotate_transform );
+	mat3 vertex_transform  = m_scale * m_r33;
+	mat3 normal_transform  = m_r33;
 
 	bool skinned = mesh->mNumBones > 0;
@@ -170,6 +165,6 @@
 	if ( mesh->mNumBones == 0 ) return false;
 
-	mat4 bone_transform    = glm::scale( 1.f/m_scale, 1.f/m_scale, 1.f/m_scale ) *  glm::inverse( glm::mat4(m_rotate_transform) ); 
-	mat4 bone_pre_transform    = glm::scale( m_scale, m_scale, m_scale );
+	mat4 bone_transform     = mat4( ( 1.f/m_scale * m_ri33 ) ); 
+	mat4 bone_pre_transform = mat4( m_scale * m_r33 );
 
 	for (unsigned int m=0; m<mesh->mNumBones; m++)
@@ -291,5 +286,5 @@
 			}
 		}
-		if ( m > 0 )
+		if ( m > 0 && bones.size() > 0 )
 		{
 			mesh_data* mesh = model->meshes[m];
@@ -318,5 +313,5 @@
 	if ( m_scene == nullptr ) return nullptr;
 	const aiScene* scene = (const aiScene*)m_scene;
-	if ( scene->mRootNode == nullptr || scene->mAnimations[0] == nullptr ) return nullptr;
+	if ( scene->mRootNode == nullptr || scene->mAnimations == nullptr || scene->mAnimations[0] == nullptr) return nullptr;
 	assimp_animation* result = new assimp_animation;
 
@@ -370,4 +365,6 @@
 	//       node's without keys
 	a_data.transform = nv::assimp_mat4_cast( node->mTransformation );
+	if (this_id == 0)
+		a_data.transform = mat4();
 	a_data.channel_count = 0;
 
@@ -405,8 +402,8 @@
 		size_t pn = glm::min( node->mNumPositionKeys - 1, n );
 		size_t rn = glm::min( node->mNumRotationKeys - 1, n );
-		nv::vec3 pos = nv::assimp_vec3_cast(node->mPositionKeys[pn].mValue);
-		nv::quat rot = nv::assimp_quat_cast(node->mRotationKeys[rn].mValue);
+		nv::vec3 pos = m_r33 * nv::assimp_vec3_cast(node->mPositionKeys[pn].mValue) * m_scale;
+		nv::quat rot = glm::quat_cast( m_r33 * glm::mat3_cast( assimp_quat_cast(node->mRotationKeys[rn].mValue ) ) * m_ri33 );
 		// TODO: only do the calculation when a rotate transform is present!
-		nv::transform ptr( vec3(), glm::quat_cast( m_rotate_transform ) );
+		nv::transform ptr;
 		if ( parent )
 		{
@@ -417,5 +414,6 @@
 			}
 		}
-		nv::transform key( ptr * nv::transform( pos * m_scale, rot ) );
+
+		nv::transform key( ptr * nv::transform( pos, rot ) );
 		channel[n].tform = key;
 	}
@@ -425,5 +423,4 @@
 {
 	data->channel_count = 0;
-	// TODO : support for m_rotate_transform and m_scale! ( should be easy )
 	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
 	if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 )
@@ -443,10 +440,10 @@
 	{
 		pchannel[np].time     = (float)node->mPositionKeys[np].mTime;
-		pchannel[np].position = assimp_vec3_cast(node->mPositionKeys[np].mValue);
+		pchannel[np].position = m_r33 * assimp_vec3_cast(node->mPositionKeys[np].mValue) * m_scale;
 	}
 	for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
 	{
 		rchannel[np].time     = (float)node->mRotationKeys[np].mTime;
-		rchannel[np].rotation = assimp_quat_cast(node->mRotationKeys[np].mValue);
+		rchannel[np].rotation = glm::quat_cast( m_r33 * glm::mat3_cast( assimp_quat_cast(node->mRotationKeys[np].mValue ) ) * m_ri33 );
 	}
 	if ( node->mNumScalingKeys > 0 )
@@ -463,5 +460,11 @@
 			}
 		}
-	}
-}
-
+		else 
+		{
+			schannel[0].time  = (float)node->mScalingKeys[0].mTime;
+			schannel[0].scale = assimp_vec3_cast(node->mScalingKeys[0].mValue);
+		}
+	}
+
+}
+
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 283)
+++ trunk/src/formats/nmd_loader.cc	(revision 284)
@@ -22,7 +22,7 @@
 		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::MESH           : load_mesh( source, element_header ); break;
+		case nmd_type::ANIMATION      : load_animation( source, element_header ); break;
+		case nmd_type::BONE_ARRAY     : load_bones( source, element_header ); break;
 		case nmd_type::STRING_TABLE   : load_strings( source ); break;
 		default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
@@ -32,8 +32,8 @@
 }
 
-bool nv::nmd_loader::load_mesh( stream& source, uint32 children )
+bool nv::nmd_loader::load_mesh( stream& source, const nmd_element_header& e )
 {
 	mesh_data* mesh = new mesh_data();
-	for ( uint32 s = 0; s < children; ++s )
+	for ( uint32 s = 0; s < e.children; ++s )
 	{
 		nmd_element_header element_header;
@@ -82,11 +82,11 @@
 }
 
-bool nv::nmd_loader::load_bones( stream& source, uint32 children )
+bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
 {
 	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 );
+	m_bone_data->bones = new nmd_bone[ e.children ];
+	m_bone_data->count = (uint16)e.children;
+	source.read( m_bone_data->bones, sizeof( nmd_bone ), e.children );
 	return true;
 }
@@ -114,5 +114,5 @@
 
 
-bool nv::nmd_loader::load_animation( stream& source, uint32 children )
+bool nv::nmd_loader::load_animation( stream& source, const nmd_element_header& e )
 {
 	NV_ASSERT( m_animation == nullptr, "MULTIPLE ANIMATION ENTRIES!" );
@@ -123,11 +123,12 @@
 	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 )
+	m_animation->node_count = (uint16)e.children;
+	m_animation->nodes      = new nmd_node[ e.children ];
+	for ( uint32 i = 0; i < e.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!" );
+		m_animation->nodes[i].name          = element_header.name;
 
 		uint16 ch_count = element_header.children;
@@ -135,5 +136,4 @@
 		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;
