Index: trunk/src/gfx/mesh_creator.cc
===================================================================
--- trunk/src/gfx/mesh_creator.cc	(revision 293)
+++ trunk/src/gfx/mesh_creator.cc	(revision 294)
@@ -162,2 +162,179 @@
 	}
 }
+
+struct vertex_g
+{
+	nv::vec4 tangent;
+};
+
+void nv::mesh_data_creator::generate_tangents()
+{
+	int p_offset = -1;
+	int n_offset = -1;
+	int t_offset = -1;
+	datatype i_type = NONE;
+	uint32 n_channel_index = 0;
+
+	const mesh_raw_channel* p_channel = nullptr;
+	      mesh_raw_channel* n_channel = nullptr;
+	const mesh_raw_channel* t_channel = nullptr;
+	const mesh_raw_channel* i_channel = nullptr;
+
+	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;
+		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; 
+					p_channel = channel;
+				}
+				break;
+			case slot::NORMAL   : if ( desc.slots[i].etype == FLOAT_VECTOR_3 ) 
+				{
+					n_offset  = desc.slots[i].offset; 
+					n_channel = m_data->m_channels[ c ];
+					n_channel_index = c;
+				}
+				break;
+			case slot::TEXCOORD : if ( desc.slots[i].etype == FLOAT_VECTOR_2 ) 
+				{
+					t_offset  = desc.slots[i].offset; 
+					t_channel = channel;
+				}
+				break;
+			case slot::INDEX    : 
+				{
+					i_type    = desc.slots[i].etype;
+					i_channel = channel;
+				}
+				break;
+			case slot::TANGENT  : return;
+			default             : break;
+		}
+	}
+	if ( !p_channel || !n_channel || !t_channel ) return;
+
+	if ( p_channel->count != n_channel->count || p_channel->count % t_channel->count != 0 || ( i_type != UINT && i_type != USHORT && i_type != NONE ) )
+	{
+		return;
+	}
+
+	mesh_raw_channel* g_channel = mesh_raw_channel::create<vertex_g>( p_channel->count );
+	vec4* tangents              = (vec4*)g_channel->data;
+	vec3* tangents2             = new vec3[ p_channel->count ];
+	uint32 tri_count = i_channel ? i_channel->count / 3 : t_channel->count / 3;
+	uint32 vtx_count = p_channel->count;
+	uint32 sets      = p_channel->count / t_channel->count;
+
+	for ( unsigned int i = 0; i < tri_count; ++i )
+	{
+		uint32 ti0 = 0;
+		uint32 ti1 = 0;
+		uint32 ti2 = 0;
+		if ( i_type == UINT )
+		{
+			const uint32* idata = (const uint32*)i_channel->data;
+			ti0 = idata[ i * 3 ];
+			ti1 = idata[ i * 3 + 1 ];
+			ti2 = idata[ i * 3 + 2 ];
+		}
+		else if ( i_type == USHORT )
+		{
+			const uint16* idata = (const uint16*)i_channel->data;
+			ti0 = idata[ i * 3 ];
+			ti1 = idata[ i * 3 + 1 ];
+			ti2 = idata[ i * 3 + 2 ];
+		}
+		else // if ( i_type == NONE )
+		{
+			ti0 = i * 3;
+			ti1 = i * 3 + 1;
+			ti2 = i * 3 + 2;
+		}
+
+		const vec2& w1 = *((vec2*)(t_channel->data + t_channel->desc.size*ti0 + t_offset ));
+		const vec2& w2 = *((vec2*)(t_channel->data + t_channel->desc.size*ti1 + t_offset ));
+		const vec2& w3 = *((vec2*)(t_channel->data + t_channel->desc.size*ti2 + t_offset ));
+		vec2 st1 = w3 - w1;
+		vec2 st2 = w2 - w1;
+		float stst = (st1.x * st2.y - st2.x * st1.y);
+		float coef = ( stst != 0.0f ? 1.0f / stst : 0.0f );
+
+		for ( uint32 set = 0; set < sets; ++set )
+		{
+			uint32 nti0 = t_channel->count * set + ti0;
+			uint32 nti1 = t_channel->count * set + ti1;
+			uint32 nti2 = t_channel->count * set + ti2;
+			vec3 v1 = *((vec3*)(p_channel->data + p_channel->desc.size*nti0 + p_offset ));
+			vec3 v2 = *((vec3*)(p_channel->data + p_channel->desc.size*nti1 + p_offset ));
+			vec3 v3 = *((vec3*)(p_channel->data + p_channel->desc.size*nti2 + p_offset ));
+			vec3 xyz1 = v3 - v1;
+			vec3 xyz2 = v2 - v1;
+
+			//glm::vec3 normal = glm::cross( xyz1, xyz2 );
+			//
+			//vtcs[ ti0 ].normal += normal;
+			//vtcs[ ti1 ].normal += normal;
+			//vtcs[ ti2 ].normal += normal;
+			vec3 tangent  = (( xyz1 * st2.y ) - ( xyz2 * st1.y )) * coef;
+			vec3 tangent2 = (( xyz2 * st1.x ) - ( xyz1 * st2.x )) * coef;
+
+			tangents[nti0] = vec4( vec3( tangents[nti0] ) + tangent, 0 );
+			tangents[nti1] = vec4( vec3( tangents[nti1] ) + tangent, 0 );
+			tangents[nti2] = vec4( vec3( tangents[nti2] ) + tangent, 0 );
+
+			tangents2[nti0] += tangent2;
+			tangents2[nti1] += tangent2;
+			tangents2[nti2] += tangent2;
+		}
+	}
+
+	for ( unsigned int i = 0; i < vtx_count; ++i )
+	{
+		const vec3 n = *((vec3*)(n_channel->data + n_channel->desc.size*i + n_offset ));
+		const vec3 t = vec3(tangents[i]);
+		if ( ! ( t.x == 0.0f && t.y == 0.0f && t.z == 0.0f ) )
+		{
+			tangents[i]    = vec4( glm::normalize(t - n * glm::dot( n, t )), 0.0f ); 
+			tangents[i][3] = (glm::dot(glm::cross(n, t), tangents2[i]) < 0.0f) ? -1.0f : 1.0f;
+		}
+	}
+	delete tangents2;
+
+	m_data->m_channels[ n_channel_index ] = merge_channels( n_channel, g_channel );
+	delete n_channel;
+	delete g_channel;
+}
+
+nv::mesh_raw_channel* nv::mesh_data_creator::merge_channels( mesh_raw_channel* a, mesh_raw_channel* b )
+{
+	NV_ASSERT( a->count == b->count, "merge_channel - bad channels!" );
+	vertex_descriptor adesc = a->desc;
+	vertex_descriptor bdesc = b->desc;
+	uint32            count = a->count;
+
+	vertex_descriptor desc  = a->desc;
+	for ( uint32 i = 0; i < bdesc.count; i++ )
+	{
+		desc.slots[desc.count+i] = bdesc.slots[i];
+		desc.slots[desc.count+i].offset += desc.size;
+	}
+	desc.size  += bdesc.size;
+	desc.count += bdesc.count;
+	uint8* data = new uint8[ count * desc.size ];
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		std::copy_n( a->data + i * adesc.size, adesc.size, data + i*desc.size );
+		std::copy_n( b->data + i * bdesc.size, bdesc.size, data + i*desc.size + adesc.size );
+	}
+	mesh_raw_channel* result = new mesh_raw_channel;
+	result->count = count;
+	result->desc  = desc;
+	result->data  = data;
+	return result;
+}
