Index: trunk/src/gfx/mesh_creator.cc
===================================================================
--- trunk/src/gfx/mesh_creator.cc	(revision 294)
+++ trunk/src/gfx/mesh_creator.cc	(revision 295)
@@ -168,4 +168,24 @@
 };
 
+void nv::mesh_data_creator::flip_normals()
+{
+	int ch_n  = m_data->get_channel_index( slot::NORMAL );
+	size_t n_offset = 0;
+	if ( ch_n == -1 ) return;
+	mesh_raw_channel* channel = m_data->m_channels[ch_n];
+	for ( uint32 i = 0; i < channel->desc.count; ++i )
+		if ( channel->desc.slots[i].vslot == slot::NORMAL )
+		{
+			n_offset  = channel->desc.slots[i].offset; 
+		}
+
+	for ( uint32 i = 0; i < channel->count; ++i )
+	{
+		vec3& normal = *(vec3*)(channel->data + channel->desc.size * i + n_offset);
+		normal = -normal;
+	}
+}
+
+
 void nv::mesh_data_creator::generate_tangents()
 {
@@ -339,2 +359,103 @@
 	return result;
 }
+
+nv::mesh_raw_channel* nv::mesh_data_creator::append_channels( mesh_raw_channel* a, mesh_raw_channel* b, uint32 frame_count )
+{
+	if ( a->desc != b->desc ) return nullptr;
+	if ( a->count % frame_count != 0 ) return nullptr;
+	if ( b->count % frame_count != 0 ) return nullptr;
+	size_t vtx_size = a->desc.size;
+
+	uint8* data = new uint8[ ( a->count + b->count ) * vtx_size ];
+	
+
+	if ( frame_count == 1 )
+	{
+		size_t a_size = vtx_size * a->count;
+		std::copy_n( a->data, a_size, data );
+		std::copy_n( b->data, vtx_size * b->count, data + a_size );		
+	}
+	else
+	{
+		size_t frame_size_a = ( a->count / frame_count ) * vtx_size;
+		size_t frame_size_b = ( b->count / frame_count ) * vtx_size;
+		size_t pos_a = 0;
+		size_t pos_b = 0;
+		size_t pos   = 0;
+		for ( size_t i = 0; i < frame_count; ++i )
+		{
+			std::copy_n( a->data + pos_a, frame_size_a, data + pos );
+			std::copy_n( b->data + pos_b, frame_size_b, data + pos + frame_size_a );				pos_a += frame_size_a; 
+			pos_b += frame_size_b; 
+			pos   += frame_size_a + frame_size_b;
+		}
+	}
+
+	mesh_raw_channel* result = new mesh_raw_channel;
+	result->count = a->count + b->count;
+	result->desc  = a->desc;
+	result->data  = data;
+	return result;
+}
+
+
+
+bool nv::mesh_data_creator::is_same_format( mesh_data* other )
+{
+	if ( m_data->get_channel_count() != other->get_channel_count() ) return false;
+	for ( uint32 c = 0; c < m_data->get_channel_count(); ++c )
+	{
+		if ( m_data->get_channel(c)->desc != other->get_channel(c)->desc )
+			return false;
+	}
+	return true;
+}
+
+void nv::mesh_data_creator::merge( mesh_data* other )
+{
+	if ( !is_same_format( other ) ) return;
+	int ch_pi  = m_data->get_channel_index( slot::POSITION );
+	int ch_ti  = m_data->get_channel_index( slot::TEXCOORD );
+	int och_pi = other->get_channel_index( slot::POSITION );
+	int och_ti = other->get_channel_index( slot::TEXCOORD );
+	if ( ch_pi == -1 || ch_ti == -1 ) return;
+	size_t size   = m_data->m_channels[ ch_ti ]->count;
+	size_t osize  =  other->m_channels[ och_ti ]->count;
+	size_t count  = m_data->m_channels[ ch_pi ]->count;
+	size_t ocount =  other->m_channels[ och_pi ]->count;
+	if ( count % size != 0 || ocount % osize != 0 ) return;
+	if ( count / size != ocount / osize ) return;
+	
+	for ( uint32 c = 0; c < m_data->get_channel_count(); ++c )
+	{
+		mesh_raw_channel* old = m_data->m_channels[c];
+		size_t frame_count = ( old->is_index() ? 1 : old->count / size );
+		m_data->m_channels[c] = append_channels( old, other->m_channels[c], frame_count );
+		NV_ASSERT( m_data->m_channels[c], "Merge problem!" );
+		if ( old->is_index() )
+		{
+			switch ( old->desc.slots[0].etype )
+			{
+			case USHORT : 
+				{
+					NV_ASSERT( size + osize < uint16(-1), "Index out of range!" );
+					uint16* indexes = (uint16*)m_data->m_channels[c]->data;
+					for ( uint16 i = (uint16)old->count; i < m_data->m_channels[c]->count; ++i )
+						indexes[i] += (uint16)size;
+
+				}
+				break;
+			case UINT   : 
+				{
+					uint32* indexes = (uint32*)m_data->m_channels[c]->data;
+					for ( uint32 i = old->count; i < m_data->m_channels[c]->count; ++i )
+						indexes[i] += size;
+				}
+				break;
+			default : NV_ASSERT( false, "Unsupported index type!" ); break;
+			}
+			m_data->m_index_channel = m_data->m_channels[c];
+		}
+		delete old;
+	}
+}
