Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 248)
+++ trunk/src/formats/assimp_loader.cc	(revision 249)
@@ -270,2 +270,158 @@
 }
 
+assimp_animation* nv::assimp_loader::release_animation( size_t, bool pre_transform, const std::vector< assimp_bone >* bone_data )
+{
+	if ( m_scene == nullptr ) return nullptr;
+	const aiScene* scene = (const aiScene*)m_scene;
+	if ( scene->mRootNode == nullptr || scene->mAnimations[0] == nullptr ) return nullptr;
+	assimp_animation* result = new assimp_animation;
+
+	// need resize not to copy!
+	result->nodes.resize( count_nodes( scene->mRootNode ) );
+
+	const aiNode*      root = scene->mRootNode;
+	const aiAnimation* anim = scene->mAnimations[0]; // if implemented, change in load_node also
+
+	result->fps            = (float)anim->mTicksPerSecond;
+	result->duration       = (float)anim->mDuration;
+	result->pretransformed = pre_transform;
+
+	load_node( result, root, 0, -1 );
+	result->nodes[0].transform = glm::scale( m_scale, m_scale, m_scale ) * result->nodes[0].transform;
+
+	if ( bone_data )
+	{
+		std::unordered_map< std::string, uint16 > names;
+		for ( uint16 bi = 0; bi < bone_data->size(); ++bi )
+		{
+			names[ (*bone_data)[bi].name ] = bi;
+		}
+
+		for ( unsigned i = 0; i < result->nodes.size(); ++i )
+		{
+			assimp_animated_node_data& node = result->nodes[i];
+			node.bone_id = -1;
+			auto bi = names.find( node.name );
+			if ( bi != names.end() )
+			{
+				node.bone_id = bi->second;
+			}
+			if ( node.parent_id != -1 ) 
+			{
+				result->nodes[ node.parent_id ].children.push_back( &node );
+			}
+		}
+	}
+
+	return result;
+}
+
+nv::uint32 nv::assimp_loader::count_nodes( const void* node ) const
+{
+	const aiNode* ainode = (const aiNode*)node;
+	nv::uint32 count = 1;
+	for ( unsigned i = 0; i < ainode->mNumChildren; ++i )
+	{
+		count += count_nodes( ainode->mChildren[i] );
+	}
+	return count;
+}
+
+nv::uint32 nv::assimp_loader::load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id )
+{
+	const aiScene* scene = (const aiScene*)m_scene;
+	const aiNode*  node  = (const aiNode*)vnode;
+	string name( node->mName.data );
+	const aiAnimation* anim  = scene->mAnimations[0];
+	const aiNodeAnim*  anode = nullptr;
+
+	for ( unsigned i = 0 ; i < anim->mNumChannels ; i++ )
+	{
+		anode = anim->mChannels[i];
+		if ( std::string( anode->mNodeName.data ) == name ) break;
+		anode = nullptr;
+	}
+
+	assimp_animated_node_data& a_data = data->nodes[ this_id ];
+
+	a_data.name      = name;
+	a_data.parent_id = parent_id;
+	a_data.bone_id = -1;
+	a_data.transform = nv::assimp_mat4_cast( node->mTransformation );
+
+	if (anode) 
+	{
+		if ( data->pretransformed )
+		{
+			a_data.keys = create_transformed_keys( anode, parent_id >= 0 ? data->nodes[ parent_id ].keys : nullptr );
+		}
+		else
+		{
+			a_data.keys = create_direct_keys( anode );
+		}
+	}
+
+	nv::uint32 next = this_id + 1;
+	for ( unsigned i = 0; i < node->mNumChildren; ++i )
+	{
+		next = load_node( data, node->mChildren[i], next, this_id );
+	}
+	return next;
+}
+
+key_animation_data* nv::assimp_loader::create_transformed_keys( const void* vnode, const key_animation_data* parent_keys )
+{
+	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
+	size_t max_keys = glm::max( node->mNumPositionKeys, node->mNumRotationKeys );
+	nv::transform_vector* keys = new nv::transform_vector;
+	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 = nv::assimp_vec3_cast(node->mPositionKeys[pn].mValue);
+		nv::quat rot = nv::assimp_quat_cast(node->mRotationKeys[rn].mValue);
+		nv::transform ptr;
+		if ( parent_keys )
+		{
+			const nv::transform_vector* pv = (const nv::transform_vector*)parent_keys;
+			if ( pv && pv->size() > 0 ) ptr = pv->get( glm::min( n, pv->size()-1 ) );
+		}
+
+		nv::vec3 rot_pos = ptr.get_orientation() * pos;
+		pos = ptr.get_position() + rot_pos;
+		rot = glm::normalize( ptr.get_orientation() * rot );
+		keys->insert( nv::transform( pos, rot ) );
+	}
+	return keys;
+}
+
+key_animation_data* nv::assimp_loader::create_direct_keys( const void* vnode )
+{
+	const aiNodeAnim* node = (const aiNodeAnim*)vnode;
+	if ( node->mNumPositionKeys == 0 && node->mNumRotationKeys == 0 && node->mNumScalingKeys == 0 ) return nullptr;
+	key_vectors_prs* keys = new key_vectors_prs;
+
+	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
+	{
+		keys->insert_position( (float)node->mPositionKeys[np].mTime, assimp_vec3_cast(node->mPositionKeys[np].mValue) );
+	}
+	for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
+	{
+		keys->insert_rotation( (float)node->mRotationKeys[np].mTime, assimp_quat_cast(node->mRotationKeys[np].mValue) );
+	}
+	if ( node->mNumScalingKeys > 0 )
+	{
+		nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
+		float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
+		if ( node->mNumScalingKeys > 1 || scale_value > 0.001 ) 
+		{
+			NV_LOG( nv::LOG_WARNING, "scale key significant!" );
+			for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
+			{
+				keys->insert_scale( (float)node->mScalingKeys[np].mTime, assimp_vec3_cast(node->mScalingKeys[np].mValue) );
+			}
+		}
+	}
+	return keys;
+}
+
