Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 292)
+++ trunk/src/formats/assimp_loader.cc	(revision 293)
@@ -9,4 +9,5 @@
 #include <glm/gtx/transform.hpp>
 #include "nv/io/std_stream.hh"
+#include "nv/gfx/mesh_creator.hh"
 #include "nv/lib/assimp.hh"
 
@@ -14,4 +15,40 @@
 
 const int MAX_BONES = 64;
+
+struct assimp_plain_vtx 
+{
+	vec3 position;
+	vec3 normal;
+	vec2 texcoord;
+	vec4 tangent;
+
+	assimp_plain_vtx() {}
+	assimp_plain_vtx( const vec3& v, const vec2& t, const vec3& n, const vec4& g )
+	{
+		position = v;
+		texcoord = t;
+		normal   = n;
+		tangent  = g;
+	}
+};
+
+struct assimp_skinned_vtx 
+{
+	vec3  position;
+	vec3  normal;
+	vec2  texcoord;
+	vec4  tangent;
+	ivec4 boneindex;
+	vec4  boneweight;
+
+	assimp_skinned_vtx() {}
+	assimp_skinned_vtx( const vec3& v, const vec2& t, const vec3& n, const vec4& g )
+	{
+		position = v;
+		texcoord = t;
+		normal   = n;
+		tangent  = g;
+	}
+};
 
 struct assimp_key_p  { float time; vec3 position; };
@@ -21,23 +58,8 @@
 
 
-nv::assimp_loader::assimp_loader( const string& a_ext, const mat3& a_rotate_transform, float a_scale, bool pre_transform, uint32 a_assimp_flags /*= 0 */ ) 
-	: m_scene( nullptr ), m_flat( pre_transform ), m_mesh_count(0)
-{
-	initialize( a_ext, a_rotate_transform, a_scale, a_assimp_flags );
-}
-
-nv::assimp_loader::assimp_loader( const string& a_ext, bool pre_transform, uint32 a_assimp_flags /*= 0 */ ) 
-	: m_scene( nullptr ), m_flat( pre_transform ), m_mesh_count(0)
-{
-	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 )
+nv::assimp_loader::assimp_loader( const string& a_ext, uint32 a_assimp_flags /*= 0 */ ) 
+	: m_scene( nullptr ), m_mesh_count(0)
 {
 	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 )
@@ -97,8 +119,4 @@
 	data->set_name( mesh->mName.data );
 
-	vec3 vertex_offset     = glm::vec3(); 
-	mat3 vertex_transform  = m_scale * m_r33;
-	mat3 normal_transform  = m_r33;
-
 	bool skinned = mesh->mNumBones > 0;
 	mesh_raw_channel* channel = nullptr;
@@ -111,8 +129,8 @@
 	for (unsigned int i=0; i<mesh->mNumVertices; i++)
 	{
-		vec3 v = vertex_transform * assimp_vec3_cast( mesh->mVertices[ i ] ) + vertex_offset;
-		vec3 n = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mNormals[ i ] ) );
-		vec3 t = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mTangents[ i ] ) );
-		vec3 b = glm::normalize( normal_transform * assimp_vec3_cast( mesh->mBitangents[ i ] ) );
+		vec3 v = assimp_vec3_cast( mesh->mVertices[ i ] );
+		vec3 n = glm::normalize( assimp_vec3_cast( mesh->mNormals[ i ] ) );
+		vec3 t = glm::normalize( assimp_vec3_cast( mesh->mTangents[ i ] ) );
+		vec3 b = glm::normalize( assimp_vec3_cast( mesh->mBitangents[ i ] ) );
 		vec2 s = assimp_st_cast( mesh->mTextureCoords[ 0 ][ i ] );
 
@@ -176,11 +194,8 @@
 	const aiMesh*  mesh  = scene->mMeshes[ index ];
 
-	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++)
 	{
 		aiBone* bone   = mesh->mBones[m];
-		mat4    offset = bone_pre_transform * assimp_mat4_cast( bone->mOffsetMatrix ) * bone_transform;
+		mat4    offset = assimp_mat4_cast( bone->mOffsetMatrix );
 		bones[m].name = bone->mName.data;
 		bones[m].data = nullptr;
@@ -338,17 +353,9 @@
 	uint16 frame_rate     = (uint16)anim->mTicksPerSecond;
 	float  duration       = (float)anim->mDuration;
-	bool   flat           = m_flat;
-	uint32 max_frames     = 0;
-
-	load_node( index, data, root, 0, -1, max_frames );
-
-	// DAE pre_transform hack
-	if ( m_flat && frame_rate == 1 )
-	{
-		frame_rate = 32;
-		duration   = (float)max_frames;
-	}
-
-	return new mesh_nodes_data( anim->mName.data, count, data, frame_rate, duration, flat ) ;
+	bool   flat           = false;
+
+	load_node( index, data, root, 0, -1 );
+
+	return new mesh_nodes_data( anim->mName.data, count, data, frame_rate, duration, flat );
 }
 
@@ -364,5 +371,5 @@
 }
 
-nv::sint16 nv::assimp_loader::load_node( uint32 anim_id, mesh_node_data* nodes, const void* vnode, sint16 this_id, sint16 parent_id, uint32& max_frames )
+nv::sint16 nv::assimp_loader::load_node( uint32 anim_id, mesh_node_data* nodes, const void* vnode, sint16 this_id, sint16 parent_id )
 {
 	const aiScene* scene = (const aiScene*)m_scene;
@@ -393,21 +400,10 @@
 	a_data.data = nullptr;
 
-	if (anode) 
-	{
-		if ( m_flat )
-		{
-			create_transformed_keys( &a_data, anode, parent_id >= 0 ? &(nodes[ parent_id ]) : nullptr );
-		}
-		else
-		{
-			create_direct_keys( &a_data, anode );
-		}
-		max_frames = glm::max<uint32>( a_data.data->get_channel(0)->count, max_frames );
-	}
+	if (anode) create_keys( &a_data, anode );
 
 	nv::sint16 next = this_id + 1;
 	for ( unsigned i = 0; i < node->mNumChildren; ++i )
 	{
-		next = load_node( anim_id, nodes, node->mChildren[i], next, this_id, max_frames );
+		next = load_node( anim_id, nodes, node->mChildren[i], next, this_id );
 	}
 
@@ -415,37 +411,5 @@
 }
 
-void nv::assimp_loader::create_transformed_keys( mesh_node_data* data, const void* vnode, const mesh_node_data* parent )
-{
-	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
-	size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
-
-	key_raw_channel* raw_channel = key_raw_channel::create<assimp_key_tr>( max_keys );
-	data->data = new key_data;
-	data->data->add_channel( raw_channel );
-	assimp_key_tr* channel = ((assimp_key_tr*)(raw_channel->data));
-
-	for ( unsigned n = 0; n < max_keys; ++n )
-	{
-		size_t pn = glm::min( node->mNumPositionKeys - 1, n );
-		size_t rn = glm::min( node->mNumRotationKeys - 1, n );
-		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;
-		if ( parent && parent->data )
-		{
-			const key_raw_channel* pchannel = parent->data->get_channel(0);
-			if ( pchannel && pchannel->count > 0 )
-			{
-				ptr = ((assimp_key_tr*)pchannel->data)[ glm::min( n, pchannel->count-1 ) ].tform;
-			}
-		}
-
-		nv::transform key( ptr * nv::transform( pos, rot ) );
-		channel[n].tform = key;
-	}
-}
-
-void nv::assimp_loader::create_direct_keys( mesh_node_data* data, const void* vnode )
+void nv::assimp_loader::create_keys( mesh_node_data* data, const void* vnode )
 {
 	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
@@ -469,10 +433,10 @@
 	{
 		pchannel[np].time     = (float)node->mPositionKeys[np].mTime;
-		pchannel[np].position = m_r33 * assimp_vec3_cast(node->mPositionKeys[np].mValue) * m_scale;
+		pchannel[np].position = assimp_vec3_cast(node->mPositionKeys[np].mValue);
 	}
 	for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
 	{
 		rchannel[np].time     = (float)node->mRotationKeys[np].mTime;
-		rchannel[np].rotation = glm::quat_cast( m_r33 * glm::mat3_cast( assimp_quat_cast(node->mRotationKeys[np].mValue ) ) * m_ri33 );
+		rchannel[np].rotation = assimp_quat_cast(node->mRotationKeys[np].mValue );
 	}
 // 	if ( node->mNumScalingKeys > 0 )
Index: trunk/src/gfx/mesh_creator.cc
===================================================================
--- trunk/src/gfx/mesh_creator.cc	(revision 293)
+++ trunk/src/gfx/mesh_creator.cc	(revision 293)
@@ -0,0 +1,163 @@
+// Copyright (C) 2012-2014 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/gfx/mesh_creator.hh"
+
+struct nv_key_transform { nv::transform tform; };
+
+void nv::mesh_nodes_creator::pre_transform_keys()
+{
+	if ( m_data->m_flat ) return;
+	merge_keys();
+	uint32 max_frames = 0;
+	for ( size_t i = 0; i < m_data->get_count(); ++i )
+	{
+		sint16 parent_id = m_data->m_nodes[i].parent_id;
+		key_data* keys   = m_data->m_nodes[i].data;
+		key_data* pkeys  = ( parent_id != -1 ? m_data->m_nodes[parent_id].data : nullptr );
+		size_t count     = ( keys ? keys->get_channel(0)->count : 0 );
+		size_t pcount    = ( pkeys ? pkeys->get_channel(0)->count : 0 );
+		max_frames = glm::max<uint32>( count, max_frames );
+		if ( pkeys && pkeys->get_channel_count() > 0 && keys && keys->get_channel_count() > 0 )
+		{
+			nv_key_transform*  channel = ((nv_key_transform*)(keys->get_channel(0)->data));
+			nv_key_transform* pchannel = ((nv_key_transform*)(pkeys->get_channel(0)->data));
+			for ( unsigned n = 0; n < count; ++n )
+			{
+				channel[n].tform = pchannel[ glm::min( n, pcount-1 ) ].tform * channel[n].tform;
+			}
+		}
+	}
+
+	// DAE pre_transform hack
+	if ( m_data->m_frame_rate == 1 )
+	{
+		m_data->m_frame_rate = 32;
+		m_data->m_duration   = (float)max_frames;
+	}
+
+	m_data->m_flat = true;
+}
+
+// TODO: DELETE
+struct assimp_key_p  { float time; nv::vec3 position; };
+struct assimp_key_r  { float time; nv::quat rotation; };
+
+
+void nv::mesh_nodes_creator::merge_keys()
+{
+	for ( size_t i = 0; i < m_data->get_count(); ++i )
+	{
+		key_data* old_keys = m_data->m_nodes[i].data;
+		if ( old_keys && old_keys->get_channel_count() > 0 )
+		{
+			size_t chan_count = old_keys->get_channel_count();
+			if ( chan_count == 1 
+				&& old_keys->get_channel(0)->desc.count == 1 
+				&& old_keys->get_channel(0)->desc.slots[0].etype == TRANSFORM ) continue;
+
+			size_t max_keys = 0;
+			for ( size_t c = 0; c < chan_count; ++c )
+			{
+				max_keys = glm::max( max_keys, old_keys->get_channel(c)->count );
+			}
+
+			key_raw_channel* raw_channel = key_raw_channel::create<nv_key_transform>( max_keys );
+			key_data* new_keys = new key_data;
+			new_keys->add_channel( raw_channel );
+			nv_key_transform* channel = ((nv_key_transform*)(raw_channel->data));
+			key_descriptor final_key = old_keys->get_final_key();
+
+			for ( unsigned n = 0; n < max_keys; ++n )
+			{
+				float key[ 16 ];
+				float* pkey = key;
+
+				for ( uint16 c = 0; c < chan_count; ++c )
+				{
+					size_t idx = glm::min( old_keys->get_channel(c)->count - 1, n );
+					pkey += old_keys->get_channel(c)->get_raw( idx, pkey );
+				}
+				channel[n].tform = extract_transform_raw( final_key, key );
+			}
+
+			delete old_keys;
+			m_data->m_nodes[i].data = new_keys;
+		}
+	}
+}
+
+void nv::mesh_nodes_creator::transform( float scale, const mat3& r33 )
+{
+	mat3 ri33 = glm::inverse( r33 );
+	mat4 pre_transform ( scale * r33 );
+	mat4 post_transform( 1.f/scale * ri33 ); 
+
+	for ( size_t i = 0; i < m_data->get_count(); ++i )
+	{
+		mesh_node_data& node = m_data->m_nodes[i];
+		node.transform = pre_transform * node.transform * post_transform;
+		if ( node.data )
+		{
+			key_data* kdata  = node.data;
+			for ( size_t c = 0; c < kdata->get_channel_count(); ++c )
+			{
+				const key_raw_channel* channel = kdata->get_channel(c);
+				size_t key_size = channel->desc.size;
+				for ( size_t n = 0; n < channel->count; ++n )
+				{
+					transform_key_raw( channel->desc, (uint8*)(channel->data + n * key_size), scale, r33, ri33 );
+				}
+			}
+		}
+	}
+}
+
+void nv::mesh_data_creator::transform( float scale, const mat3& r33 )
+{
+	vec3 vertex_offset     = glm::vec3(); 
+	mat3 vertex_transform  = scale * r33;
+	mat3 normal_transform  = r33;
+
+	for ( uint32 c = 0; c < m_data->get_channel_count(); ++c )
+	{
+		const mesh_raw_channel* channel = m_data->get_channel(c);
+		const vertex_descriptor& desc   = channel->desc;
+		uint8* raw_data = channel->data;
+		int vtx_size = desc.size;
+		int p_offset = -1;
+		int n_offset = -1;
+		int t_offset = -1;
+		for ( uint32 i = 0; i < desc.count; ++i )
+			switch ( desc.slots[i].vslot )
+			{
+				case slot::POSITION : if ( desc.slots[i].etype == FLOAT_VECTOR_3 ) p_offset = desc.slots[i].offset; break;
+				case slot::NORMAL   : if ( desc.slots[i].etype == FLOAT_VECTOR_3 ) n_offset = desc.slots[i].offset; break;
+				case slot::TANGENT  : if ( desc.slots[i].etype == FLOAT_VECTOR_4 ) t_offset = desc.slots[i].offset; break;
+				default             : break;
+			}
+
+		if ( p_offset != -1 )
+			for ( uint32 i = 0; i < channel->count; i++)
+			{
+				vec3& p = *((vec3*)(raw_data + vtx_size*i + p_offset ));
+				p = vertex_transform * p + vertex_offset;
+			}
+
+		if ( n_offset != -1 )
+			for ( uint32 i = 0; i < channel->count; i++)
+			{
+				vec3& n = *((vec3*)(raw_data + vtx_size*i + n_offset ));
+				n = glm::normalize( normal_transform * n );
+			}
+		if ( t_offset != -1 )
+			for ( uint32 i = 0; i < channel->count; i++)
+			{
+				vec4& t = *((vec4*)(raw_data + vtx_size*i + t_offset ));
+				t = vec4( glm::normalize( normal_transform * vec3(t) ), t[3] );
+			}
+	}
+}
