Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 409)
+++ trunk/src/formats/assimp_loader.cc	(revision 410)
@@ -50,5 +50,5 @@
 };
 
-struct assimp_key_p  { float time; vec3 position; };
+struct assimp_key_p  { float time; vec3 translation; };
 struct assimp_key_r  { float time; quat rotation; };
 struct assimp_key_s  { float time; vec3 scale; };
@@ -430,6 +430,6 @@
 	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
 	{
-		pchannel[np].time     = static_cast<float>( node->mPositionKeys[np].mTime );
-		pchannel[np].position = assimp_vec3_cast(node->mPositionKeys[np].mValue);
+		pchannel[np].time        = static_cast<float>( node->mPositionKeys[np].mTime );
+		pchannel[np].translation = assimp_vec3_cast(node->mPositionKeys[np].mValue);
 	}
 	for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 409)
+++ trunk/src/formats/nmd_loader.cc	(revision 410)
@@ -44,5 +44,5 @@
 		source.read( &stream_header, sizeof( stream_header ), 1 );
 		mesh_raw_channel* channel = mesh_raw_channel::create( stream_header.format, stream_header.count );
-		source.read( channel->data, stream_header.format.size, stream_header.count );
+		source.read( channel->data, stream_header.format.element_size(), stream_header.count );
 		mesh->add_channel( channel );
 	}
@@ -126,8 +126,8 @@
 				source.read( &element_header, sizeof( element_header ), 1 );
 				NV_ASSERT( element_header.type == nmd_type::KEY_CHANNEL, "CHANNEL expected!" );
-				nv::nmd_key_channel_header cheader;
+				nv::nmd_stream_header cheader;
 				source.read( &cheader, sizeof( cheader ), 1 );
 				key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count );
-				source.read( channel->data, channel->desc.size, channel->count );
+				source.read( channel->data, channel->desc.element_size(), channel->count );
 				kdata->add_channel( channel );
 			}
@@ -191,5 +191,5 @@
 		sheader.count  = chan->count;
 		stream_out.write( &sheader, sizeof( sheader ), 1 );
-		stream_out.write( chan->data, chan->desc.size, chan->count );
+		stream_out.write( chan->data, chan->desc.element_size(), chan->count );
 	}
 }
@@ -205,5 +205,5 @@
 			for ( uint32 c = 0; c < node->data->get_channel_count(); ++c )
 			{
-				total += sizeof( nmd_element_header ) + sizeof( nmd_key_channel_header );
+				total += sizeof( nmd_element_header ) + sizeof( nmd_stream_header );
 				total += node->data->get_channel(c)->size();
 			}
@@ -230,5 +230,5 @@
 		for ( uint32 c = 0; c < chan_count; ++c )
 		{
-			chan_size += sizeof( nmd_element_header ) + sizeof( nv::nmd_key_channel_header );
+			chan_size += sizeof( nmd_element_header ) + sizeof( nv::nmd_stream_header );
 			chan_size += node->data->get_channel(c)->size();
 		}
@@ -252,12 +252,12 @@
 			eheader.type     = nmd_type::KEY_CHANNEL;
 			eheader.children = 0;
-			eheader.size     = sizeof( nmd_key_channel_header ) + channel->size();
+			eheader.size     = sizeof( nmd_stream_header ) + channel->size();
 			stream_out.write( &eheader, sizeof( eheader ), 1 );
 
-			nmd_key_channel_header cheader;
+			nmd_stream_header cheader;
 			cheader.format    = channel->desc;
 			cheader.count     = channel->count;
 			stream_out.write( &cheader, sizeof( cheader ), 1 );
-			stream_out.write( channel->data, channel->desc.size, channel->count );
+			stream_out.write( channel->data, channel->desc.element_size(), channel->count );
 		}
 	}
Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 409)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 410)
@@ -192,5 +192,5 @@
 	buffer  ib = m_context->get_device()->create_buffer( INDEX_BUFFER, STATIC_DRAW, m_mesh_data->get_index_channel()->size(), m_mesh_data->get_index_channel()->data );
 
-	m_context->set_index_buffer( m_va, ib, m_mesh_data->get_index_channel()->desc.slots[0].etype, true );
+	m_context->set_index_buffer( m_va, ib, m_mesh_data->get_index_channel()->desc[0].etype, true );
 
 	m_data = new uint8[ m_vertex_count * m_vsize ];
Index: trunk/src/gfx/mesh_creator.cc
===================================================================
--- trunk/src/gfx/mesh_creator.cc	(revision 409)
+++ trunk/src/gfx/mesh_creator.cc	(revision 410)
@@ -57,6 +57,6 @@
 			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;
+				&& old_keys->get_channel(0)->desc.slot_count() == 1 
+				&& old_keys->get_channel(0)->desc[0].etype == TRANSFORM ) continue;
 
 			size_t max_keys = 0;
@@ -70,5 +70,5 @@
 			new_keys->add_channel( raw_channel );
 			nv_key_transform* channel = reinterpret_cast<nv_key_transform*>(raw_channel->data);
-			key_descriptor final_key = old_keys->get_final_key();
+			data_descriptor final_key = old_keys->get_final_key();
 
 			for ( unsigned n = 0; n < max_keys; ++n )
@@ -107,5 +107,5 @@
 			{
 				const key_raw_channel* channel = kdata->get_channel(c);
-				size_t key_size = channel->desc.size;
+				size_t key_size = channel->desc.element_size();
 				for ( size_t n = 0; n < channel->count; ++n )
 				{
@@ -126,16 +126,16 @@
 	{
 		const mesh_raw_channel* channel = m_data->get_channel(c);
-		const vertex_descriptor& desc   = channel->desc;
+		const data_descriptor&  desc    = channel->desc;
 		uint8* raw_data = channel->data;
-		uint32 vtx_size = desc.size;
+		uint32 vtx_size = desc.element_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 = int(desc.slots[i].offset); break;
-				case slot::NORMAL   : if ( desc.slots[i].etype == FLOAT_VECTOR_3 ) n_offset = int(desc.slots[i].offset); break;
-				case slot::TANGENT  : if ( desc.slots[i].etype == FLOAT_VECTOR_4 ) t_offset = int(desc.slots[i].offset); break;
+		for ( const auto& cslot : desc  )
+			switch ( cslot.vslot )
+			{
+				case slot::POSITION : if ( cslot.etype == FLOAT_VECTOR_3 ) p_offset = int( cslot.offset ); break;
+				case slot::NORMAL   : if ( cslot.etype == FLOAT_VECTOR_3 ) n_offset = int( cslot.offset ); break;
+				case slot::TANGENT  : if ( cslot.etype == FLOAT_VECTOR_4 ) t_offset = int( cslot.offset ); break;
 				default             : break;
 			}
@@ -174,13 +174,13 @@
 	if ( ch_n == -1 ) return;
 	mesh_raw_channel* channel = m_data->m_channels[ unsigned( 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 ( const auto& cslot : channel->desc )
+		if ( cslot.vslot == slot::NORMAL )
+		{
+			n_offset  = cslot.offset;
 		}
 
 	for ( uint32 i = 0; i < channel->count; ++i )
 	{
-		vec3& normal = *reinterpret_cast<vec3*>( channel->data + channel->desc.size * i + n_offset );
+		vec3& normal = *reinterpret_cast<vec3*>( channel->data + channel->desc.element_size() * i + n_offset );
 		normal = -normal;
 	}
@@ -204,25 +204,27 @@
 	{
 		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 )
+
+		for ( const auto& cslot : channel->desc )
+		switch ( cslot.vslot )
 		{
 			case slot::POSITION : 
-				if ( desc.slots[i].etype == FLOAT_VECTOR_3 ) 
-				{
-					p_offset  = int( desc.slots[i].offset );
+				if ( cslot.etype == FLOAT_VECTOR_3 )
+				{
+					p_offset  = int( cslot.offset );
 					p_channel = channel;
 				}
 				break;
-			case slot::NORMAL   : if ( desc.slots[i].etype == FLOAT_VECTOR_3 ) 
-				{
-					n_offset  = int( desc.slots[i].offset );
+			case slot::NORMAL   : 
+				if ( cslot.etype == FLOAT_VECTOR_3 )
+				{
+					n_offset  = int( cslot.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  = int( desc.slots[i].offset );
+			case slot::TEXCOORD : 
+				if ( cslot.etype == FLOAT_VECTOR_2 )
+				{
+					t_offset  = int( cslot.offset );
 					t_channel = channel;
 				}
@@ -230,5 +232,5 @@
 			case slot::INDEX    : 
 				{
-					i_type    = desc.slots[i].etype;
+					i_type    = cslot.etype;
 					i_channel = channel;
 				}
@@ -278,7 +280,7 @@
 		}
 
-		const vec2& w1 = *reinterpret_cast<vec2*>(t_channel->data + t_channel->desc.size*ti0 + t_offset );
-		const vec2& w2 = *reinterpret_cast<vec2*>(t_channel->data + t_channel->desc.size*ti1 + t_offset );
-		const vec2& w3 = *reinterpret_cast<vec2*>(t_channel->data + t_channel->desc.size*ti2 + t_offset );
+		const vec2& w1 = *reinterpret_cast<vec2*>(t_channel->data + t_channel->desc.element_size()*ti0 + t_offset );
+		const vec2& w2 = *reinterpret_cast<vec2*>(t_channel->data + t_channel->desc.element_size()*ti1 + t_offset );
+		const vec2& w3 = *reinterpret_cast<vec2*>(t_channel->data + t_channel->desc.element_size()*ti2 + t_offset );
 		vec2 st1 = w3 - w1;
 		vec2 st2 = w2 - w1;
@@ -291,7 +293,7 @@
 			uint32 nti1 = t_channel->count * set + ti1;
 			uint32 nti2 = t_channel->count * set + ti2;
-			vec3 v1 = *reinterpret_cast<vec3*>(p_channel->data + p_channel->desc.size*nti0 + p_offset );
-			vec3 v2 = *reinterpret_cast<vec3*>(p_channel->data + p_channel->desc.size*nti1 + p_offset );
-			vec3 v3 = *reinterpret_cast<vec3*>(p_channel->data + p_channel->desc.size*nti2 + p_offset );
+			vec3 v1 = *reinterpret_cast<vec3*>(p_channel->data + p_channel->desc.element_size()*nti0 + p_offset );
+			vec3 v2 = *reinterpret_cast<vec3*>(p_channel->data + p_channel->desc.element_size()*nti1 + p_offset );
+			vec3 v3 = *reinterpret_cast<vec3*>(p_channel->data + p_channel->desc.element_size()*nti2 + p_offset );
 			vec3 xyz1 = v3 - v1;
 			vec3 xyz2 = v2 - v1;
@@ -317,5 +319,5 @@
 	for ( unsigned int i = 0; i < vtx_count; ++i )
 	{
-		const vec3 n = *reinterpret_cast<vec3*>( n_channel->data + n_channel->desc.size*i + n_offset );
+		const vec3 n = *reinterpret_cast<vec3*>( n_channel->data + n_channel->desc.element_size()*i + n_offset );
 		const vec3 t = vec3(tangents[i]);
 		if ( ! ( t.x == 0.0f && t.y == 0.0f && t.z == 0.0f ) )
@@ -335,21 +337,18 @@
 {
 	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 ];
+	data_descriptor adesc = a->desc;
+	data_descriptor bdesc = b->desc;
+	uint32          count = a->count;
+
+	data_descriptor desc  = a->desc;
+	for ( auto bslot : bdesc )
+	{
+		desc.push_slot( bslot.etype, bslot.vslot );
+	}
+	uint8* data = new uint8[ count * desc.element_size() ];
 	for ( uint32 i = 0; i < count; ++i )
 	{
-		raw_copy_n( a->data + i * adesc.size, adesc.size, data + i*desc.size );
-		raw_copy_n( b->data + i * bdesc.size, bdesc.size, data + i*desc.size + adesc.size );
+		raw_copy_n( a->data + i * adesc.element_size(), adesc.element_size(), data + i*desc.element_size() );
+		raw_copy_n( b->data + i * bdesc.element_size(), bdesc.element_size(), data + i*desc.element_size() + adesc.element_size() );
 	}
 	mesh_raw_channel* result = new mesh_raw_channel;
@@ -365,5 +364,5 @@
 	if ( a->count % frame_count != 0 ) return nullptr;
 	if ( b->count % frame_count != 0 ) return nullptr;
-	size_t vtx_size = a->desc.size;
+	size_t vtx_size = a->desc.element_size();
 
 	uint8* data = new uint8[ ( a->count + b->count ) * vtx_size ];
@@ -435,5 +434,5 @@
 		if ( old->get_buffer_type() == INDEX_BUFFER )
 		{
-			switch ( old->desc.slots[0].etype )
+			switch ( old->desc[0].etype )
 			{
 			case USHORT : 
Index: trunk/src/gl/gl_window.cc
===================================================================
--- trunk/src/gl/gl_window.cc	(revision 409)
+++ trunk/src/gl/gl_window.cc	(revision 410)
@@ -104,5 +104,5 @@
 	m_handle  = wm->adopt_window( handle );
 	m_hwnd    = ::GetDC( reinterpret_cast<HWND>( handle ) );
-	m_context->set_viewport( nv::ivec4( 0, 0, m_width, m_height ) );
+	m_context->set_viewport( nv::ivec4( 0, 0, 1, 1 ) );
 #else
 	NV_ASSERT( false, "Native GL context adoption not implemented for this platform!" );
Index: trunk/src/stl/assert.cc
===================================================================
--- trunk/src/stl/assert.cc	(revision 409)
+++ trunk/src/stl/assert.cc	(revision 410)
@@ -5,13 +5,10 @@
 // For conditions of distribution and use, see copying.txt file in root folder.
 
-#define NV_BASE_COMMON_HH
-#define NV_INTERNAL_INCLUDE
-#include "nv/base/assert.hh"
-#undef NV_BASE_COMMON_HH
+#include "nv/base/common.hh"
 #include "nv/core/logging.hh"
 
 extern "C" {
 #if NV_COMPILER == NV_MSVC
-	NV_NORETURN void __cdecl exit( _In_ int _Code );
+	_ACRTIMP NV_NORETURN void __cdecl exit( _In_ int _Code );
 #else
 	void exit( int status_code ) NV_NORETURN;
Index: trunk/src/wx/wx_canvas.cc
===================================================================
--- trunk/src/wx/wx_canvas.cc	(revision 409)
+++ trunk/src/wx/wx_canvas.cc	(revision 410)
@@ -63,15 +63,18 @@
 }
 
-void nv::wx_log_text_ctrl_sink::log( nv::log_level level, const std::string& message )
+void nv::wx_log_text_ctrl_sink::log( nv::log_level level, const nv::string_view& message )
 {
 	wxString str;
-	str << timestamp() << " [" << padded_level_name( level ) << "] " << message << "\n";
+	char stamp[16];
+	size_t ssize = timestamp( stamp );
+	str << stamp << " [" << padded_level_name( level ).data() << "] " << message.data() << "\n";
 	m_text_ctrl->AppendText( str );
 }
 
 nv::wx_app_base::wx_app_base()
+	: m_logger( nv::LOG_TRACE )
 {
-	static nv::logger log( nv::LOG_TRACE );
-	log.add_sink( new nv::log_file_sink( "log.txt" ), nv::LOG_TRACE );
+	nv::log_sink* sink = new nv::log_file_sink( "log.txt" );
+	m_logger.add_sink( sink, nv::LOG_TRACE );
 	NV_LOG( nv::LOG_NOTICE, "Logging started" );
 }
