Index: trunk/nv/gfx/mesh_creator.hh
===================================================================
--- trunk/nv/gfx/mesh_creator.hh	(revision 455)
+++ trunk/nv/gfx/mesh_creator.hh	(revision 456)
@@ -18,15 +18,39 @@
 	{
 	public:
-		mesh_data_creator( data_channel_set* data ) : m_data( data ) {}
+		mesh_data_creator( data_channel_set* data );
 		// assumes that position and normal is vec3, tangent is vec4
 		void transform( float scale, const mat3& r33 );
 		// TODO: this could generate normals too
 		void generate_tangents();
+		// assumes that position and normal is vec3, tangent is vec4
+		void rotate_quadrant( uint8 rotation );
+		void translate( vec3 offset );
 		void flip_normals();
+		void scale_texture( vec2 min, vec2 max );
 		bool is_same_format( data_channel_set* other );
 		void merge( data_channel_set* other );
 	private:
+		void initialize();
+
 		raw_data_channel merge_channels( const raw_data_channel& a, const raw_data_channel& b );
 		raw_data_channel append_channels( const raw_data_channel& a, const raw_data_channel& b, uint32 frame_count = 1 );
+
+		raw_data_channel* m_pos_channel;
+		raw_data_channel* m_nrm_channel;
+		raw_data_channel* m_tan_channel;
+		raw_data_channel* m_tex_channel;
+		raw_data_channel* m_idx_channel;
+
+		int m_pos_offset;
+		int m_nrm_offset;
+		int m_tan_offset;
+		int m_tex_offset;
+		int m_idx_offset;
+
+		nv::datatype m_pos_type;
+		nv::datatype m_nrm_type;
+		nv::datatype m_tan_type;
+		nv::datatype m_tex_type;
+		nv::datatype m_idx_type;
 
 		data_channel_set* m_data;
Index: trunk/nv/interface/data_channel.hh
===================================================================
--- trunk/nv/interface/data_channel.hh	(revision 455)
+++ trunk/nv/interface/data_channel.hh	(revision 456)
@@ -40,5 +40,4 @@
 		}
 
-
 		const data_descriptor& descriptor() const { return m_desc; }
 		uint32 element_size() const { return m_desc.element_size(); }
@@ -52,4 +51,12 @@
 		{
 			return reinterpret_cast<const Struct*>( m_data );
+		}
+
+		// TODO: constexpr compile-time cast check?
+		template < typename T >
+		const T& extract( uint16 slot_index, uint32 index ) const
+		{
+			NV_ASSERT( slot < m_desc.size(), "Bas slot passed to extract!" )
+			return *reinterpret_cast<const T*>( m_data + m_desc.element_size()*index + m_desc[slot_index].offset );
 		}
 
Index: trunk/nv/interface/data_channel_access.hh
===================================================================
--- trunk/nv/interface/data_channel_access.hh	(revision 455)
+++ trunk/nv/interface/data_channel_access.hh	(revision 456)
@@ -93,4 +93,18 @@
 			return channel;
 		}
+
+		static raw_data_channel clone( const raw_data_channel& other )
+		{
+			raw_data_channel channel;
+			channel.m_desc = other.m_desc;
+			channel.m_size = other.m_size;
+			channel.m_data = nullptr;
+			if ( channel.m_size > 0 )
+			{
+				channel.m_data = new uint8[channel.raw_size()];
+				raw_copy_n( other.raw_data(), other.raw_size(), channel.m_data );
+			}
+			return channel;
+		}
 	};
 
@@ -104,4 +118,15 @@
 		{
 			return new data_channel_set;
+		}
+
+		static data_channel_set* clone( const data_channel_set& other )
+		{
+			data_channel_set* result = new data_channel_set;
+			for ( auto& channel : other )
+			{
+				result->m_channels[result->m_size] = data_channel_creator::clone( channel );
+				result->m_size++;
+			}
+			return result;
 		}
 
Index: trunk/src/gfx/mesh_creator.cc
===================================================================
--- trunk/src/gfx/mesh_creator.cc	(revision 455)
+++ trunk/src/gfx/mesh_creator.cc	(revision 456)
@@ -113,4 +113,9 @@
 		}
 	}
+}
+
+nv::mesh_data_creator::mesh_data_creator( data_channel_set* data ) : m_data( data )
+{
+	initialize();
 }
 
@@ -168,17 +173,10 @@
 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;
-	raw_data_channel_access channel( m_data, unsigned( ch_n ) );
-	for ( const auto& cslot : channel.descriptor() )
-		if ( cslot.vslot == slot::NORMAL )
-		{
-			n_offset  = cslot.offset;
-		}
-
+	if ( m_nrm_channel == nullptr ) return;
+	NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unknown normal vector type!" );
+	raw_data_channel_access channel( m_nrm_channel );
 	for ( uint32 i = 0; i < channel.size(); ++i )
 	{
-		vec3& normal = *reinterpret_cast<vec3*>( channel.raw_data() + channel.element_size() * i + n_offset );
+		vec3& normal = *reinterpret_cast<vec3*>( channel.raw_data() + channel.element_size() * i + m_nrm_offset );
 		normal = -normal;
 	}
@@ -186,71 +184,39 @@
 
 
+void nv::mesh_data_creator::scale_texture( vec2 min, vec2 max )
+{
+	if ( m_tex_channel == nullptr ) return;
+	NV_ASSERT( m_tex_type == FLOAT_VECTOR_2, "Unknown texcoord vector type!" );
+	raw_data_channel_access channel( m_tex_channel );
+	vec2 scale = max - min;
+	for ( uint32 i = 0; i < channel.size(); ++i )
+	{
+		vec2& tc = *reinterpret_cast<vec2*>( channel.raw_data() + channel.element_size() * i + m_tex_offset );
+		tc = min + tc * scale;
+	}
+}
+
 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 raw_data_channel* p_channel = nullptr;
-	      raw_data_channel* n_channel = nullptr;
-	const raw_data_channel* t_channel = nullptr;
-	const raw_data_channel* i_channel = nullptr;
-
-	for ( uint32 c = 0; c < m_data->size(); ++c )
-	{
-		const raw_data_channel* channel = m_data->get_channel(c);
-
-		for ( const auto& cslot : channel->descriptor() )
-		switch ( cslot.vslot )
-		{
-			case slot::POSITION : 
-				if ( cslot.etype == FLOAT_VECTOR_3 )
-				{
-					p_offset  = int( cslot.offset );
-					p_channel = channel;
-				}
-				break;
-			case slot::NORMAL   : 
-				if ( cslot.etype == FLOAT_VECTOR_3 )
-				{
-					n_offset  = int( cslot.offset );
-					n_channel = data_channel_set_creator( m_data )[ c ];
-					n_channel_index = c;
-				}
-				break;
-			case slot::TEXCOORD : 
-				if ( cslot.etype == FLOAT_VECTOR_2 )
-				{
-					t_offset  = int( cslot.offset );
-					t_channel = channel;
-				}
-				break;
-			case slot::INDEX    : 
-				{
-					i_type    = cslot.etype;
-					i_channel = channel;
-				}
-				break;
-			case slot::TANGENT  : return;
-			default             : break;
-		}
-	}
-	if ( !p_channel || !n_channel || !t_channel ) return;
-
-	if ( p_channel->size() != n_channel->size()
-		|| p_channel->size() % t_channel->size() != 0
-		|| ( i_type != UINT && i_type != USHORT && i_type != NONE ) )
+	if ( m_tan_channel != nullptr ) return;
+	if ( !m_pos_channel || !m_nrm_channel || !m_tex_channel ) return;
+
+	if ( m_pos_channel->size() != m_nrm_channel->size()
+		|| m_pos_channel->size() % m_tex_channel->size() != 0
+		|| ( m_idx_type != UINT && m_idx_type != USHORT && m_idx_type != NONE ) )
 	{
 		return;
 	}
 
-	raw_data_channel g_channel  = data_channel_creator::create< vertex_g >( p_channel->size() );
+	NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported position vector type!" );
+	NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unsupported normal vector type!" );
+	NV_ASSERT( m_tex_type == FLOAT_VECTOR_2, "Unknown texcoord vector type!" );
+
+	raw_data_channel g_channel  = data_channel_creator::create< vertex_g >( m_pos_channel->size() );
 	vec4* tangents              = &( data_channel_access< vertex_g >( &g_channel ).data()[0].tangent );
-	vec3* tangents2             = new vec3[ p_channel->size() ];
-	uint32 tri_count = i_channel ? i_channel->size() / 3 : t_channel->size() / 3;
-	uint32 vtx_count = p_channel->size();
-	uint32 sets      = p_channel->size() / t_channel->size();
+	vec3* tangents2             = new vec3[ m_pos_channel->size() ];
+	uint32 tri_count = m_idx_channel ? m_idx_channel->size() / 3 : m_tex_channel->size() / 3;
+	uint32 vtx_count = m_pos_channel->size();
+	uint32 sets      = m_pos_channel->size() / m_tex_channel->size();
 
 	for ( unsigned int i = 0; i < tri_count; ++i )
@@ -259,19 +225,19 @@
 		uint32 ti1 = 0;
 		uint32 ti2 = 0;
-		if ( i_type == UINT )
-		{
-			const uint32* idata = reinterpret_cast<const uint32*>( i_channel->raw_data() );
+		if ( m_idx_type == UINT )
+		{
+			const uint32* idata = reinterpret_cast<const uint32*>( m_idx_channel->raw_data() );
 			ti0 = idata[ i * 3 ];
 			ti1 = idata[ i * 3 + 1 ];
 			ti2 = idata[ i * 3 + 2 ];
 		}
-		else if ( i_type == USHORT )
-		{
-			const uint16* idata = reinterpret_cast<const uint16*>( i_channel->raw_data() );
+		else if ( m_idx_type == USHORT )
+		{
+			const uint16* idata = reinterpret_cast<const uint16*>( m_idx_channel->raw_data() );
 			ti0 = idata[ i * 3 ];
 			ti1 = idata[ i * 3 + 1 ];
 			ti2 = idata[ i * 3 + 2 ];
 		}
-		else // if ( i_type == NONE )
+		else // if ( m_idx_type == NONE )
 		{
 			ti0 = i * 3;
@@ -280,7 +246,7 @@
 		}
 
-		const vec2& w1 = *reinterpret_cast<const vec2*>(t_channel->raw_data() + t_channel->element_size()*ti0 + t_offset );
-		const vec2& w2 = *reinterpret_cast<const vec2*>(t_channel->raw_data() + t_channel->element_size()*ti1 + t_offset );
-		const vec2& w3 = *reinterpret_cast<const vec2*>(t_channel->raw_data() + t_channel->element_size()*ti2 + t_offset );
+		const vec2& w1 = *reinterpret_cast<const vec2*>( m_tex_channel->raw_data() + m_tex_channel->element_size()*ti0 + m_tex_offset );
+		const vec2& w2 = *reinterpret_cast<const vec2*>( m_tex_channel->raw_data() + m_tex_channel->element_size()*ti1 + m_tex_offset );
+		const vec2& w3 = *reinterpret_cast<const vec2*>( m_tex_channel->raw_data() + m_tex_channel->element_size()*ti2 + m_tex_offset );
 		vec2 st1 = w3 - w1;
 		vec2 st2 = w2 - w1;
@@ -290,10 +256,10 @@
 		for ( uint32 set = 0; set < sets; ++set )
 		{
-			uint32 nti0 = t_channel->size() * set + ti0;
-			uint32 nti1 = t_channel->size() * set + ti1;
-			uint32 nti2 = t_channel->size() * set + ti2;
-			const vec3& v1 = *reinterpret_cast<const vec3*>(p_channel->raw_data() + p_channel->element_size()*nti0 + p_offset );
-			const vec3& v2 = *reinterpret_cast<const vec3*>(p_channel->raw_data() + p_channel->element_size()*nti1 + p_offset );
-			const vec3& v3 = *reinterpret_cast<const vec3*>(p_channel->raw_data() + p_channel->element_size()*nti2 + p_offset );
+			uint32 nti0 = m_tex_channel->size() * set + ti0;
+			uint32 nti1 = m_tex_channel->size() * set + ti1;
+			uint32 nti2 = m_tex_channel->size() * set + ti2;
+			const vec3& v1 = *reinterpret_cast<const vec3*>( m_pos_channel->raw_data() + m_pos_channel->element_size()*nti0 + m_pos_offset );
+			const vec3& v2 = *reinterpret_cast<const vec3*>( m_pos_channel->raw_data() + m_pos_channel->element_size()*nti1 + m_pos_offset );
+			const vec3& v3 = *reinterpret_cast<const vec3*>( m_pos_channel->raw_data() + m_pos_channel->element_size()*nti2 + m_pos_offset );
 			vec3 xyz1 = v3 - v1;
 			vec3 xyz2 = v2 - v1;
@@ -319,5 +285,5 @@
 	for ( unsigned int i = 0; i < vtx_count; ++i )
 	{
-		const vec3 n = *reinterpret_cast<const vec3*>( n_channel->raw_data() + n_channel->element_size()*i + n_offset );
+		const vec3 n = *reinterpret_cast<const vec3*>( m_nrm_channel->raw_data() + m_nrm_channel->element_size()*i + m_nrm_offset );
 		const vec3 t = vec3(tangents[i]);
 		if ( ! ( t.x == 0.0f && t.y == 0.0f && t.z == 0.0f ) )
@@ -329,5 +295,130 @@
 	delete[] tangents2;
 
-	data_channel_set_creator( m_data ).set_channel( n_channel_index, merge_channels( *n_channel, g_channel ) );
+	uint32 n_channel_index = m_data->get_channel_index( slot::NORMAL );
+	data_channel_set_creator( m_data ).set_channel( n_channel_index, merge_channels( *m_nrm_channel, g_channel ) );
+	initialize();
+}
+
+void nv::mesh_data_creator::rotate_quadrant( uint8 rotation )
+{
+	if ( rotation % 4 == 0 ) return;
+	NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported position vector type!" );
+	NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unsupported normal vector type!" );
+	NV_ASSERT( m_tan_type == FLOAT_VECTOR_4, "Unsupported tangent vector type!" );
+
+	float r11 = 0.f;
+	float r12 = 0.f;
+	float r21 = 0.f;
+	float r22 = 0.f;
+
+	switch ( rotation % 4 )
+	{
+	case 1: r12 = -1.f; r21 =  1.f; break;
+	case 2: r11 = -1.f; r22 = -1.f; break;
+	case 3: r12 =  1.f; r21 = -1.f; break;
+	default:
+		break;
+	}
+
+	unsigned vtx_count = m_pos_channel->size();
+	uint8* pos_data = raw_data_channel_access( m_pos_channel ).raw_data();
+	uint8* nrm_data = raw_data_channel_access( m_nrm_channel ).raw_data();
+	uint8* tan_data = raw_data_channel_access( m_tan_channel ).raw_data();
+	for ( unsigned int i = 0; i < vtx_count; ++i )
+	{
+		vec3& pos = *reinterpret_cast<vec3*>( pos_data + m_pos_channel->element_size() * i + m_pos_offset );
+		vec3& nrm = *reinterpret_cast<vec3*>( nrm_data + m_nrm_channel->element_size() * i + m_nrm_offset );
+		vec4& tan = *reinterpret_cast<vec4*>( tan_data + m_tan_channel->element_size() * i + m_tan_offset );
+
+		pos = vec3(
+			pos.x * r11 + pos.z * r12,
+			pos.y,
+			pos.x * r21 + pos.z * r22
+			);
+		nrm = vec3(
+			nrm.x * r11 + nrm.z * r12,
+			nrm.y,
+			nrm.x * r21 + nrm.z * r22
+			);
+		tan = vec4(
+			tan.x * r11 + tan.z * r12,
+			tan.y,
+			tan.x * r21 + tan.z * r22,
+			1.0f // make sure this is proper
+			);
+	}
+
+
+}
+
+void nv::mesh_data_creator::translate( vec3 offset )
+{
+	if ( m_pos_channel == nullptr ) return;
+	NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported poosition vector type!" );
+	raw_data_channel_access channel( m_pos_channel );
+	for ( uint32 i = 0; i < channel.size(); ++i )
+	{
+		vec3& p = *reinterpret_cast<vec3*>( channel.raw_data() + channel.element_size() * i + m_pos_offset );
+		p = p + offset;
+	}
+
+}
+
+void nv::mesh_data_creator::initialize()
+{
+	NV_ASSERT( m_data, "bad parameter!" );
+	m_pos_channel = nullptr;
+	m_nrm_channel = nullptr;
+	m_tan_channel = nullptr;
+	m_tex_channel = nullptr;
+	m_idx_channel = nullptr;
+
+	m_pos_offset = -1;
+	m_nrm_offset = -1;
+	m_tan_offset = -1;
+	m_tex_offset = -1;
+	m_idx_offset = -1;
+
+	m_pos_type = NONE;
+	m_nrm_type = NONE;
+	m_tan_type = NONE;
+	m_tex_type = NONE;
+	m_idx_type = NONE;
+
+	for ( uint32 c = 0; c < m_data->size(); ++c )
+	{
+		raw_data_channel* channel = data_channel_set_creator( m_data )[c];
+
+		for ( const auto& cslot : channel->descriptor() )
+			switch ( cslot.vslot )
+			{
+			case slot::POSITION:
+				m_pos_type = cslot.etype;
+				m_pos_offset = int( cslot.offset );
+				m_pos_channel = channel;
+				break;
+			case slot::NORMAL:
+				m_nrm_type = cslot.etype;
+				m_nrm_offset = int( cslot.offset );
+				m_nrm_channel = channel;
+				break;
+			case slot::TANGENT:
+				m_tan_type = cslot.etype;
+				m_tan_offset = int( cslot.offset );
+				m_tan_channel = channel;
+				break;
+			case slot::TEXCOORD:
+				m_tex_type = cslot.etype;
+				m_tex_offset = int( cslot.offset );
+				m_tex_channel = channel;
+				break;
+			case slot::INDEX:
+				m_idx_type = cslot.etype;
+				m_idx_offset = int( cslot.offset );
+				m_idx_channel = channel;
+				break;
+			default: break;
+			}
+	}
 }
 
@@ -344,5 +435,5 @@
 		raw_copy_n( b.raw_data() + i * b.element_size(), b.element_size(), raw_data_channel_access( &result ).raw_data() + i*desc.element_size() + a.element_size() );
 	}
-
+	initialize();
 	return result;
 }
@@ -380,4 +471,5 @@
 	}
 
+	initialize();
 	return result;
 }
@@ -447,4 +539,5 @@
 		}
 	}
+	initialize();
 }
 
Index: trunk/src/gui/gui_gfx_renderer.cc
===================================================================
--- trunk/src/gui/gui_gfx_renderer.cc	(revision 455)
+++ trunk/src/gui/gui_gfx_renderer.cc	(revision 456)
@@ -248,5 +248,4 @@
 		if ( e->m_flags[HOVER] )    selector = stext[2];
 		if ( e->m_flags[SELECTED] ) selector = stext[1];
-		NV_LOG_INFO( "redraw" );
 
 		if ( m_style.get( e, "skin", selector, path ) )
