Index: /trunk/nv.lua
===================================================================
--- /trunk/nv.lua	(revision 409)
+++ /trunk/nv.lua	(revision 410)
@@ -147,5 +147,5 @@
 
 	configuration "vs*"
-		defines { "_SECURE_SCL=0", "_CRT_SECURE_NO_WARNINGS=1", "_ITERATOR_DEBUG_LEVEL=0", "_HAS_ITERATOR_DEBUGGING=0" }
+		defines { "_SECURE_SCL=0", "_CRT_SECURE_NO_WARNINGS=1" } --, "_ITERATOR_DEBUG_LEVEL=0", "_HAS_ITERATOR_DEBUGGING=0" }
 
 if _ACTION == "gmake-clang" then
Index: /trunk/nv/core/arcball.hh
===================================================================
--- /trunk/nv/core/arcball.hh	(revision 409)
+++ /trunk/nv/core/arcball.hh	(revision 410)
@@ -65,5 +65,5 @@
 				p.z = sqrt( 1.0f - sq );
 			else
-				p = math::normalize( p );
+				p = glm::normalize( p );
 			return p;
 		}
Index: /trunk/nv/formats/nmd_loader.hh
===================================================================
--- /trunk/nv/formats/nmd_loader.hh	(revision 409)
+++ /trunk/nv/formats/nmd_loader.hh	(revision 410)
@@ -55,14 +55,8 @@
 	};
 
-	struct nmd_key_channel_header
-	{
-		key_descriptor format;
-		uint32         count;
-	};
-
 	struct nmd_stream_header
 	{
-		vertex_descriptor format;
-		uint32 count;
+		data_descriptor format;
+		uint32          count;
 	};
 
Index: /trunk/nv/formats/obj_loader.hh
===================================================================
--- /trunk/nv/formats/obj_loader.hh	(revision 409)
+++ /trunk/nv/formats/obj_loader.hh	(revision 410)
@@ -32,5 +32,5 @@
 		~obj_loader();
 	private:
-		vertex_descriptor    m_descriptor;
+		data_descriptor      m_descriptor;
 		bool                 m_normals;
 		bool                 m_tangents;
Index: /trunk/nv/gfx/animation.hh
===================================================================
--- /trunk/nv/gfx/animation.hh	(revision 409)
+++ /trunk/nv/gfx/animation.hh	(revision 410)
@@ -12,5 +12,5 @@
 #include <nv/interface/stream.hh>
 #include <nv/stl/math.hh>
-#include <nv/interface/animation_key.hh>
+#include <nv/interface/data_descriptor.hh>
 #include <nv/interface/interpolation_raw.hh>
 #include <nv/interface/interpolation_template.hh>
@@ -22,7 +22,7 @@
 	struct key_raw_channel
 	{
-		key_descriptor desc;
-		uint8*         data;
-		uint32         count;
+		data_descriptor desc;
+		uint8*          data;
+		uint32          count;
 
 		key_raw_channel() : data( nullptr ), count( 0 ) {}
@@ -32,5 +32,5 @@
 		}
 
-		uint32 size() const { return count * desc.size; }
+		uint32 size() const { return count * desc.element_size(); }
 
 		template < typename KEY >
@@ -44,5 +44,5 @@
 		}
 
-		static key_raw_channel* create( const key_descriptor& keydesc, uint32 count = 0 )
+		static key_raw_channel* create( const data_descriptor& keydesc, uint32 count = 0 )
 		{
 			key_raw_channel* result = new key_raw_channel();
@@ -56,8 +56,8 @@
 		{
 			if ( count == 0 ) return 0;
-			uint32 keyfsize   = desc.size / 4;
+			uint32 keyfsize   = desc.element_size() / 4;
 			const float* fdata = reinterpret_cast<const float*>( data ) + keyfsize * index;
 			uint32 mod        = 0;
-			if ( desc.slots[0].vslot == animation_slot::TIME ) mod = 1;
+			if ( desc[0].vslot == slot::TIME ) mod = 1;
 			raw_copy_n( fdata + mod, keyfsize - mod, result );
 			return keyfsize - mod;
@@ -67,5 +67,5 @@
 		{
 			if ( count == 0 ) return 0;
-			uint32 keyfsize   = desc.size / 4;
+			uint32 keyfsize   = desc.element_size() / 4;
 			uint32 keyfresult = keyfsize;
 			const float* fdata = reinterpret_cast<const float*>( data );
@@ -75,7 +75,7 @@
 			int index1  = -1;
 			float factor = 1.0f;
-			if ( desc.slots[0].vslot == animation_slot::TIME )
-			{
-				NV_ASSERT( desc.slots[0].offset == 0, "time offset not zero!" );
+			if ( desc[0].vslot == slot::TIME )
+			{
+				NV_ASSERT( desc[0].offset == 0, "time offset not zero!" );
 				slot++;
 				keyfresult--;
@@ -112,10 +112,10 @@
 			}
 			uint32 ret = 0;
-			for ( ; slot < desc.count; ++slot )
+			for ( ; slot < desc.slot_count(); ++slot )
 			{
 				ret += nv::interpolate_raw( 
-					desc.slots[slot], factor, 
-					fdata + index0 * static_cast<int>( keyfsize ) + desc.slots[slot].offset / 4,
-					fdata + index1 * static_cast<int>( keyfsize ) + desc.slots[slot].offset / 4,
+					desc[slot], factor, 
+					fdata + index0 * static_cast<int>( keyfsize ) + desc[slot].offset / 4,
+					fdata + index1 * static_cast<int>( keyfsize ) + desc[slot].offset / 4,
 					result + ret );
 			}
@@ -133,17 +133,7 @@
 			NV_ASSERT( channel, "nullptr passed to add_channel!" );
 			m_channels.push_back( channel );
-			for ( uint16 i = 0; i < channel->desc.count; ++i )
-			{
-				const key_descriptor_slot& ksi = channel->desc.slots[i];
-				if ( ksi.vslot != animation_slot::TIME )
-				{
-					uint32 index = m_final_key.count;
-					m_final_key.slots[ index ].offset = m_final_key.size;
-					m_final_key.slots[ index ].etype  = ksi.etype;
-					m_final_key.slots[ index ].vslot  = ksi.vslot;
-					m_final_key.size += get_datatype_info( ksi.etype ).size;
-					m_final_key.count++;
-				}
-			}
+			for ( const auto& cslot : channel->desc )
+				if ( cslot.vslot != slot::TIME )
+					m_final_key.push_slot( cslot.etype, cslot.vslot );
 		}
 
@@ -194,5 +184,5 @@
 		size_t get_channel_count() const { return m_channels.size(); }
 		const key_raw_channel* get_channel( size_t index ) const { return m_channels[ index ]; }
-		const key_descriptor& get_final_key() const { return m_final_key; }
+		const data_descriptor& get_final_key() const { return m_final_key; }
 
 		virtual ~key_data()
@@ -201,5 +191,5 @@
 		}
 	private:
-		key_descriptor m_final_key;
+		data_descriptor m_final_key;
 		vector< key_raw_channel* > m_channels;
 	};
@@ -219,5 +209,5 @@
 		{
 			m_data = data;
-			key_descriptor desc;
+			data_descriptor desc;
 			desc.initialize<KEY>();
 			NV_ASSERT( data->desc == desc, "Bad channel passed!" );
@@ -251,5 +241,5 @@
 		{
 			m_data = data;
-			key_descriptor desc;
+			data_descriptor desc;
 			desc.initialize<KEY>();
 			NV_ASSERT( data->desc == desc, "Bad channel passed!" );
@@ -366,5 +356,5 @@
 		explicit transform_vector( const key_raw_channel* channel ) 
 		{
-			key_descriptor kd;
+			data_descriptor kd;
 			kd.initialize<key>();
 			NV_ASSERT( kd == channel->desc, "bad channel!" );
Index: /trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- /trunk/nv/gfx/skeletal_mesh.hh	(revision 409)
+++ /trunk/nv/gfx/skeletal_mesh.hh	(revision 410)
@@ -106,5 +106,5 @@
 		virtual transform get_node_transform( uint32 node_id ) const;
 		virtual mat4 get_node_matrix( uint32 node_id ) const;
-		~skeletal_mesh_gpu() { delete m_transform; }
+		~skeletal_mesh_gpu() { delete[] m_transform; }
 	protected:
 		const mesh_nodes_data* m_bone_data;
Index: unk/nv/interface/animation_key.hh
===================================================================
--- /trunk/nv/interface/animation_key.hh	(revision 409)
+++ 	(revision )
@@ -1,243 +1,0 @@
-// Copyright (C) 2014-2015 ChaosForge Ltd
-// http://chaosforge.org/
-//
-// This file is part of Nova libraries. 
-// For conditions of distribution and use, see copying.txt file in root folder.
-
-// WARNING: this file is explicitly designed to fuck with your brain
-
-#ifndef NV_INTERFACE_ANIMATION_KEY_HH
-#define NV_INTERFACE_ANIMATION_KEY_HH
-
-#include <nv/common.hh>
-#include <nv/core/transform.hh>
-#include <nv/stl/math.hh>
-
-namespace nv
-{
-
-	enum class animation_slot : uint8
-	{
-		TIME     = 0,
-		POSITION = 1,
-		ROTATION = 2,
-		SCALE    = 3,
-		TFORM    = 4,
-
-		SLOT_MAX       = 4,
-		SLOT_MAX_STORE = 8,
-	};
-
-	namespace detail
-	{
-		template < typename KEY, animation_slot SLOT >
-		struct key_has_slot_impl
-		{
-			static bool const value = false;
-		};
-
-		template < typename KEY >
-		struct key_has_slot_impl< KEY, animation_slot::TIME >
-		{
-		private:
-			struct fallback { int time; };
-			struct derived : KEY, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::time>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename KEY >
-		struct key_has_slot_impl< KEY, animation_slot::POSITION >
-		{
-		private:
-			struct fallback { int position; };
-			struct derived : KEY, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::position>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename KEY >
-		struct key_has_slot_impl< KEY, animation_slot::ROTATION >
-		{
-		private:
-			struct fallback { int rotation; };
-			struct derived : KEY, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::rotation>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename KEY >
-		struct key_has_slot_impl< KEY, animation_slot::SCALE >
-		{
-		private:
-			struct fallback { int scale; };
-			struct derived : KEY, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::scale>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename KEY >
-		struct key_has_slot_impl< KEY, animation_slot::TFORM >
-		{
-		private:
-			struct fallback { int tform; };
-			struct derived : KEY, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::tform>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename KEY, animation_slot SLOT, bool HAS_SLOT >
-		struct key_slot_info_impl
-		{
-		};
-
-		template < typename KEY, animation_slot SLOT >
-		struct key_slot_info_impl < KEY, SLOT, false >
-		{
-			typedef empty_type value_type;
-			static const datatype etype  = datatype::NONE;
-			static const int      offset = 0;
-		};
-
-		template < typename KEY >
-		struct key_slot_info_impl< KEY, animation_slot::TIME, true >
-		{
-			typedef decltype( KEY::time ) value_type;
-			static const datatype etype  = type_to_enum< decltype( KEY::time ) >::type;
-			static const int      offset = NV_OFFSET_OF( KEY, time );
-		};
-
-		template < typename KEY >
-		struct key_slot_info_impl< KEY, animation_slot::POSITION, true >
-		{
-			typedef decltype( KEY::position ) value_type;
-			static const datatype etype  = type_to_enum< decltype( KEY::position ) >::type;
-			static const int      offset = NV_OFFSET_OF( KEY, position );
-			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
-			{
-				key.position = nv::interpolate( k1.position, k2.position, factor );
-			}
-		};
-
-		template < typename KEY >
-		struct key_slot_info_impl< KEY, animation_slot::ROTATION, true >
-		{
-			typedef decltype( KEY::rotation ) value_type;
-			static const datatype etype  = type_to_enum< decltype( KEY::rotation ) >::type;
-			static const int      offset = NV_OFFSET_OF( KEY, rotation );
-			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
-			{
-				key.rotation = nv::interpolate( k1.rotation, k2.rotation, factor );
-			}
-		};
-
-		template < typename KEY >
-		struct key_slot_info_impl< KEY, animation_slot::SCALE, true >
-		{
-			typedef decltype( KEY::scale ) value_type;
-			static const datatype etype  = type_to_enum< decltype( KEY::scale ) >::type;
-			static const int      offset = NV_OFFSET_OF( KEY, scale );
-			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
-			{
-				key.scale = nv::interpolate( k1.scale, k2.scale, factor );
-			}
-		};
-
-		template < typename KEY >
-		struct key_slot_info_impl< KEY, animation_slot::TFORM, true >
-		{
-			typedef decltype( KEY::tform ) value_type;
-			static const datatype etype  = type_to_enum< decltype( KEY::tform ) >::type;
-			static const int      offset = NV_OFFSET_OF( KEY, tform );
-			static void interpolate( KEY& key, const KEY& k1, const KEY& k2, float factor )
-			{
-				key.tform = nv::interpolate( k1.tform, k2.tform, factor );
-			}
-		};
-	}
-
-	template < typename KEY, animation_slot SLOT >
-	struct key_has_slot : bool_constant< detail::key_has_slot_impl< KEY, SLOT >::value >
-	{
-	};
-
-
-	template < typename KEY, animation_slot SLOT >
-	struct key_slot_info : public detail::key_slot_info_impl< KEY, SLOT, detail::key_has_slot_impl< KEY, SLOT >::value >
-	{
-	};
-
-	struct key_descriptor_slot
-	{
-		datatype       etype;
-		uint32         offset;
-		animation_slot vslot;
-		key_descriptor_slot() : etype(NONE), offset(0), vslot(animation_slot::TIME) {}
-	};
-
-	struct key_descriptor
-	{
-		key_descriptor_slot slots[ uint16( animation_slot::SLOT_MAX_STORE ) ];
-		uint32              count;
-		uint32              size;
-
-		key_descriptor() : count(0), size(0) {}
-
-		template < typename KEY >
-		void initialize()
-		{
-			count = 0;
-			initialize_slot< KEY, animation_slot::TIME >();
-			initialize_slot< KEY, animation_slot::POSITION >();
-			initialize_slot< KEY, animation_slot::ROTATION >();
-			initialize_slot< KEY, animation_slot::SCALE >();
-			initialize_slot< KEY, animation_slot::TFORM >();
-			size = sizeof( KEY );
-		}
-
-		bool operator==( const key_descriptor& rhs ) const
-		{
-			if ( size  != rhs.size )  return false;
-			if ( count != rhs.count ) return false;
-			for ( uint32 i = 0; i < count; ++i )
-			{
-				if ( slots[i].etype  != rhs.slots[i].etype )  return false;
-				if ( slots[i].offset != rhs.slots[i].offset ) return false;
-				if ( slots[i].vslot  != rhs.slots[i].vslot )  return false;
-			}
-			return true;
-		}
-
-	private:
-		template < typename KEY, animation_slot SLOT >
-		void initialize_slot()
-		{
-			typedef key_slot_info< KEY, SLOT > slot_info;
-			slots[ count ].etype  = slot_info::etype;
-			if ( slots[ count ].etype != datatype::NONE )
-			{
-				slots[ count ].vslot  = SLOT;
-				slots[ count ].offset = slot_info::offset;
-				count++;
-			}
-		}
-	};
-
-}
-
-#endif // NV_INTERFACE_ANIMATION_KEY_HH
Index: /trunk/nv/interface/context.hh
===================================================================
--- /trunk/nv/interface/context.hh	(revision 409)
+++ /trunk/nv/interface/context.hh	(revision 410)
@@ -132,5 +132,5 @@
 		void add_vertex_buffer_impl( vertex_array va, buffer vb, const true_type& )
 		{
-			typedef vertex_slot_info< VTX, SLOT > vinfo;
+			typedef slot_info< VTX, SLOT > vinfo;
 			typedef datatype_traits< typename vinfo::value_type > dt_traits;
 			add_vertex_buffer( va, SLOT, vb, type_to_enum< typename dt_traits::base_type >::type, dt_traits::size, vinfo::offset, sizeof( VTX ), false );
@@ -140,5 +140,5 @@
 		void add_vertex_buffer( vertex_array va, buffer vb )
 		{
-			add_vertex_buffer_impl< VTX, SLOT >( va, vb, bool_constant< vertex_has_slot< VTX, SLOT >::value >() );
+			add_vertex_buffer_impl< VTX, SLOT >( va, vb, has_slot< VTX, SLOT >() );
 		}
 
@@ -157,11 +157,11 @@
 		void add_vertex_buffers( vertex_array va, buffer buf, const mesh_raw_channel* channel )
 		{
-			for ( uint32 s = 0; s < channel->desc.count; ++s )
-			{
-				const vertex_descriptor_slot& slot = channel->desc.slots[s];
-				const datatype_info& info = get_datatype_info(slot.etype);
-				add_vertex_buffer( va, slot.vslot, buf, info.base , info.elements, slot.offset, channel->desc.size, false );
-			}
-		}
+			for ( const auto& cslot : channel->desc )
+			{
+				const datatype_info& info = get_datatype_info( cslot.etype );
+				add_vertex_buffer( va, cslot.vslot, buf, info.base, info.elements, cslot.offset, channel->desc.element_size(), false );
+			}
+		}
+
 
 		template < typename VTXDATA >
@@ -311,5 +311,5 @@
 					if ( type == INDEX_BUFFER )
 					{
-						set_index_buffer( va, b, channel->desc.slots[0].etype, true );
+						set_index_buffer( va, b, channel->desc[0].etype, true );
 					}
 					else
Index: /trunk/nv/interface/data_descriptor.hh
===================================================================
--- /trunk/nv/interface/data_descriptor.hh	(revision 410)
+++ /trunk/nv/interface/data_descriptor.hh	(revision 410)
@@ -0,0 +1,325 @@
+// Copyright (C) 2012-2015 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#ifndef NV_INTERFACE_DATA_DESCRIPTOR_HH
+#define NV_INTERFACE_DATA_DESCRIPTOR_HH
+
+#include <nv/common.hh>
+#include <nv/stl/math.hh>
+#include <nv/stl/type_traits/experimental.hh>
+
+namespace nv
+{
+
+	enum class slot : uint8
+	{
+		POSITION    = 0,
+		TEXCOORD    = 1,
+		NORMAL      = 2,
+		TANGENT     = 3,
+		BONEINDEX   = 4,
+		BONEWEIGHT  = 5,
+		COLOR       = 6,
+
+		INDEX       = 7,
+
+		TIME        = 8,
+		TRANSLATION = 9,
+		ROTATION    = 10,
+		SCALE       = 11,
+		TFORM       = 12,
+
+		MAX_STORE   = 8,
+	};
+
+	struct data_descriptor_slot
+	{
+		datatype etype  = NONE;
+		uint32   offset = 0;
+		slot     vslot  = slot::POSITION;
+	};
+
+	template < typename Struct, slot Slot, typename = void_t<> >
+	struct has_slot : false_type {};
+
+ 	template < typename Struct >
+ 	struct has_slot< Struct, slot::POSITION, void_t< NV_VOID_DECLTYPE( Struct::position ) > > : true_type {};
+	
+	template < typename Struct >
+	struct has_slot < Struct, slot::TEXCOORD, void_t< NV_VOID_DECLTYPE( Struct::texcoord ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::NORMAL, void_t< NV_VOID_DECLTYPE( Struct::normal ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::TANGENT, void_t< NV_VOID_DECLTYPE( Struct::tangent ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::BONEINDEX, void_t< NV_VOID_DECLTYPE( Struct::boneindex ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::BONEWEIGHT, void_t< NV_VOID_DECLTYPE( Struct::boneweight ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::COLOR, void_t< NV_VOID_DECLTYPE( Struct::color ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::INDEX, void_t< NV_VOID_DECLTYPE( Struct::index ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::TIME, void_t< NV_VOID_DECLTYPE( Struct::time ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::TRANSLATION, void_t< NV_VOID_DECLTYPE( Struct::translation ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::ROTATION, void_t< NV_VOID_DECLTYPE( Struct::rotation ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::SCALE, void_t< NV_VOID_DECLTYPE( Struct::scale ) > > : true_type{};
+
+	template < typename Struct >
+	struct has_slot < Struct, slot::TFORM, void_t< NV_VOID_DECLTYPE( Struct::tform ) > > : true_type{};
+
+	namespace detail
+	{
+
+		template < typename Struct, slot Slot, bool IsPresent >
+		struct slot_info_impl
+		{
+		};
+
+		template < typename Struct, slot Slot >
+		struct slot_info_impl < Struct, Slot, false >
+		{
+			typedef empty_type value_type;
+			static const datatype etype  = datatype::NONE;
+			static const int      offset = 0;
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::POSITION, true >
+		{
+			typedef decltype( Struct::position ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::position ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, position );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::TEXCOORD, true >
+		{
+			typedef decltype( Struct::texcoord ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::texcoord ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, texcoord );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::NORMAL, true >
+		{
+			typedef decltype( Struct::normal ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::normal ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, normal );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::TANGENT, true >
+		{
+			typedef decltype( Struct::tangent ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::tangent ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, tangent );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::BONEINDEX, true >
+		{
+			typedef decltype( Struct::boneindex ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::boneindex ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, boneindex );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::BONEWEIGHT, true >
+		{
+			typedef decltype( Struct::boneweight ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::boneweight ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, boneweight );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::COLOR, true >
+		{
+			typedef decltype( Struct::color ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::color ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, color );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::INDEX, true >
+		{
+			typedef decltype( Struct::index ) value_type;
+			static const datatype etype  = type_to_enum< decltype( Struct::index ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, index );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::TIME, true >
+		{
+			typedef decltype( Struct::time ) value_type;
+			static const datatype etype = type_to_enum< decltype( Struct::time ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, time );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::TRANSLATION, true >
+		{
+			typedef decltype( Struct::translation ) value_type;
+			static const datatype etype = type_to_enum< decltype( Struct::translation ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, translation );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::ROTATION, true >
+		{
+			typedef decltype( Struct::rotation ) value_type;
+			static const datatype etype = type_to_enum< decltype( Struct::rotation ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, rotation );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::SCALE, true >
+		{
+			typedef decltype( Struct::scale ) value_type;
+			static const datatype etype = type_to_enum< decltype( Struct::scale ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, scale );
+		};
+
+		template < typename Struct >
+		struct slot_info_impl< Struct, slot::TFORM, true >
+		{
+			typedef decltype( Struct::tform ) value_type;
+			static const datatype etype = type_to_enum< decltype( Struct::tform ) >::type;
+			static const int      offset = NV_OFFSET_OF( Struct, tform );
+		};
+	}
+
+	template < typename Struct, slot Slot >
+	using slot_info = detail::slot_info_impl< Struct, Slot, has_slot< Struct, Slot >::value >;
+
+
+	class data_descriptor
+	{
+	public:
+		typedef const data_descriptor_slot* const_iterator;
+
+		bool operator!=( const data_descriptor& rhs ) const
+		{
+			return !( *this == rhs );
+		}
+
+		bool operator==( const data_descriptor& rhs ) const
+		{
+//			if ( flags != rhs.flags ) return false;
+			if ( size  != rhs.size )  return false;
+			if ( count != rhs.count ) return false;
+			for ( uint32 i = 0; i < count; ++i )
+			{
+				if ( slots[i].etype  != rhs.slots[i].etype )  return false;
+				if ( slots[i].offset != rhs.slots[i].offset ) return false;
+				if ( slots[i].vslot  != rhs.slots[i].vslot )  return false;
+			}
+			return true;
+		}
+
+		template < typename IndexType >
+		void initialize_index()
+		{
+			count = 1;
+			size = sizeof( IndexType );
+			slots[0].etype = type_to_enum< IndexType >::type;
+			slots[0].vslot = slot::INDEX;
+			slots[0].offset = 0;
+		}
+
+		void initialize_index( datatype itype )
+		{
+			count = 1;
+			size  = get_datatype_info( itype ).size;
+			slots[0].etype  = itype;
+			slots[0].vslot  = slot::INDEX;
+			slots[0].offset = 0;
+		}
+
+		template < typename Struct >
+		void initialize()
+		{
+			count = 0;
+			initialize_slot< Struct, slot::POSITION >();
+			initialize_slot< Struct, slot::TEXCOORD >();
+			initialize_slot< Struct, slot::NORMAL >();
+			initialize_slot< Struct, slot::TANGENT >();
+			initialize_slot< Struct, slot::BONEINDEX >();
+			initialize_slot< Struct, slot::BONEWEIGHT >();
+			initialize_slot< Struct, slot::COLOR >();
+			initialize_slot< Struct, slot::TIME >();
+			initialize_slot< Struct, slot::TRANSLATION >();
+			initialize_slot< Struct, slot::ROTATION >();
+			initialize_slot< Struct, slot::SCALE >();
+			initialize_slot< Struct, slot::TFORM >();
+			size = sizeof( Struct );
+		}
+
+		uint32 element_size() const { return size; }
+		uint32 slot_count() const { return count; }
+
+		const_iterator begin() const { return &slots[0]; }
+		const_iterator end() const { return &slots[count]; }
+
+		const data_descriptor_slot& operator []( uint32 i ) const
+		{
+			NV_ASSERT( i < count, "data_descriptor indexing failure!" );
+			return slots[i];
+		}
+
+		void push_slot( datatype etype, slot vslot )
+		{
+			slots[count].etype  = etype;
+			slots[count].offset = size;
+			slots[count].vslot  = vslot;
+			size += get_datatype_info( etype ).size;
+			count++;
+		}
+
+	protected:
+		// 		enum flag_type : uint32
+		// 		{
+		// 			NONE   = 0b0000,
+		// 			VERTEX = 0b0001,
+		// 			INDEX  = 0b0010,
+		// 			KEY    = 0b0100
+		// 		};
+		data_descriptor_slot slots[uint16( slot::MAX_STORE )];
+		uint32               count = 0;
+		uint32               size = 0;
+		//		flag_type            flags = NONE;
+
+		template < typename Struct, slot Slot >
+		void initialize_slot()
+		{
+			typedef slot_info< Struct, Slot > sinfo;
+			slots[count].etype = sinfo::etype;
+			if ( slots[count].etype != datatype::NONE )
+			{
+				slots[count].vslot  = Slot;
+				slots[count].offset = sinfo::offset;
+				count++;
+			}
+		}
+	};
+
+}
+
+#endif // NV_INTERFACE_DATA_DESCRIPTOR_HH
+
Index: /trunk/nv/interface/device.hh
===================================================================
--- /trunk/nv/interface/device.hh	(revision 409)
+++ /trunk/nv/interface/device.hh	(revision 410)
@@ -113,4 +113,11 @@
 			: filter_min( f ), filter_max( f ), wrap_s( w ), wrap_t( w ) {}
 
+	};
+
+	enum buffer_hint
+	{
+		STATIC_DRAW,
+		STREAM_DRAW,
+		DYNAMIC_DRAW
 	};
 
Index: /trunk/nv/interface/interpolation_raw.hh
===================================================================
--- /trunk/nv/interface/interpolation_raw.hh	(revision 409)
+++ /trunk/nv/interface/interpolation_raw.hh	(revision 410)
@@ -13,5 +13,5 @@
 #include <nv/core/transform.hh>
 #include <nv/stl/math.hh>
-#include <nv/interface/animation_key.hh>
+#include <nv/interface/data_descriptor.hh>
 
 namespace nv
@@ -33,14 +33,14 @@
 	}
 
-	inline uint32 interpolate_raw( const key_descriptor_slot& slot, float factor, const float* k1, const float* k2, float* result )
+	inline uint32 interpolate_raw( const data_descriptor_slot& slot, float factor, const float* k1, const float* k2, float* result )
 	{
 		uint32 count = get_datatype_info( slot.etype ).elements;
 		switch ( slot.vslot )
 		{
-		case animation_slot::TIME:     return 0;
-		case animation_slot::POSITION: return interpolate_raw_linear( count, factor, k1, k2, result );
-		case animation_slot::ROTATION: return interpolate_raw_quat( factor, k1, k2, result );
-		case animation_slot::SCALE:    return interpolate_raw_linear( count, factor, k1, k2, result );
-		case animation_slot::TFORM:    return 
+		case slot::TIME:        return 0;
+		case slot::TRANSLATION: return interpolate_raw_linear( count, factor, k1, k2, result );
+		case slot::ROTATION:    return interpolate_raw_quat( factor, k1, k2, result );
+		case slot::SCALE:       return interpolate_raw_linear( count, factor, k1, k2, result );
+		case slot::TFORM:       return 
 										   interpolate_raw_linear( 3, factor, k1, k2, result ) + 
 										   interpolate_raw_quat( factor, k1 + 3, k2 + 3, result + 3 );
@@ -57,15 +57,15 @@
 	}
 
-	inline mat4 extract_matrix_raw( const key_descriptor& desc, const float* data )
+	inline mat4 extract_matrix_raw( const data_descriptor& desc, const float* data )
 	{
-		if ( desc.count == 1 )
+		if ( desc.slot_count() == 1 )
 		{
-			switch ( desc.slots[0].vslot )
+			switch ( desc[0].vslot )
 			{
-			case animation_slot::TIME:     return mat4();
-			case animation_slot::POSITION: return glm::translate( mat4(), make_vec3( data ) );
-			case animation_slot::ROTATION: return mat4_cast( make_quat_fixed( data ) );
-			case animation_slot::SCALE:    return glm::scale( mat4(),make_vec3( data ) );
-			case animation_slot::TFORM:    return transform( make_vec3( data ), make_quat_fixed( data + 3 ) ).extract();
+			case slot::TIME:        return mat4();
+			case slot::TRANSLATION: return glm::translate( mat4(), make_vec3( data ) );
+			case slot::ROTATION:    return mat4_cast( make_quat_fixed( data ) );
+			case slot::SCALE:       return glm::scale( mat4(),make_vec3( data ) );
+			case slot::TFORM:       return transform( make_vec3( data ), make_quat_fixed( data + 3 ) ).extract();
 			default:
 				return mat4();
@@ -74,39 +74,39 @@
 		else
 		{
-			mat4 position;
+			mat4 translation;
 			mat4 rotation;
 			mat4 scale;
-			for ( uint32 i = 0; i < desc.count; ++i )
+			for ( auto& slot : desc )
 			{
-				uint32 offset = desc.slots[i].offset / 4;
-				switch ( desc.slots[i].vslot )
+				uint32 offset = slot.offset / 4;
+				switch ( slot.vslot )
 				{
-				case animation_slot::TIME:     break;
-				case animation_slot::POSITION: 
-					position = glm::translate( position,make_vec3( data + offset ) ); break;
-				case animation_slot::ROTATION: 
+				case slot::TIME:     break;
+				case slot::TRANSLATION:
+					translation = glm::translate( translation,make_vec3( data + offset ) ); break;
+				case slot::ROTATION: 
 					rotation = mat4_cast( make_quat_fixed( data + offset ) ); break;
-				case animation_slot::SCALE:    
+				case slot::SCALE:    
 					scale    = glm::scale( mat4(),make_vec3( data + offset ) ); break;
-				case animation_slot::TFORM:    return transform( make_vec3( data + offset ), make_quat_fixed( data + offset + 3 ) ).extract();
+				case slot::TFORM:    return transform( make_vec3( data + offset ), make_quat_fixed( data + offset + 3 ) ).extract();
 				default:
 					break;
 				}
 			}
-			return position * rotation * scale;
+			return translation * rotation * scale;
 		}
 	}
 
-	inline transform extract_transform_raw( const key_descriptor& desc, const float* data )
+	inline transform extract_transform_raw( const data_descriptor& desc, const float* data )
 	{
-		if ( desc.count == 1 )
+		if ( desc.slot_count() == 1 )
 		{
-			switch ( desc.slots[0].vslot )
+			switch ( desc[0].vslot )
 			{
-			case animation_slot::TIME:     return transform();
-			case animation_slot::POSITION: return transform( make_vec3( data ) );
-			case animation_slot::ROTATION: return transform( make_quat_fixed( data ) );
-			case animation_slot::SCALE:    return transform();
-			case animation_slot::TFORM:    return transform( make_vec3( data ), make_quat_fixed( data + 3 ) );
+			case slot::TIME:        return transform();
+			case slot::TRANSLATION: return transform( make_vec3( data ) );
+			case slot::ROTATION:    return transform( make_quat_fixed( data ) );
+			case slot::SCALE:       return transform();
+			case slot::TFORM:       return transform( make_vec3( data ), make_quat_fixed( data + 3 ) );
 			default:
 				return transform();
@@ -115,27 +115,27 @@
 		else
 		{
-			vec3 position;
+			vec3 translation;
 			quat rotation;
-			for ( uint32 i = 0; i < desc.count; ++i )
+			for ( auto& slot : desc )
 			{
-				uint32 offset = desc.slots[i].offset / 4;
-				switch ( desc.slots[i].vslot )
+				uint32 offset = slot.offset / 4;
+				switch ( slot.vslot )
 				{
-				case animation_slot::TIME:     break;
-				case animation_slot::POSITION: 
-					position = make_vec3( data + offset ); break;
-				case animation_slot::ROTATION: 
+				case slot::TIME:     break;
+				case slot::TRANSLATION:
+					translation = make_vec3( data + offset ); break;
+				case slot::ROTATION: 
 					rotation = make_quat_fixed( data + offset ); break;
-				case animation_slot::SCALE:	   break;
-				case animation_slot::TFORM:    return transform( make_vec3( data + offset ), make_quat_fixed( data + 3 ) );
+				case slot::SCALE:	   break;
+				case slot::TFORM:    return transform( make_vec3( data + offset ), make_quat_fixed( data + 3 ) );
 				default:
 					break;
 				}
 			}
-			return transform( position, rotation );
+			return transform( translation, rotation );
 		}
 	}
 
-	inline void transform_key_position( uint8* data, float scale, const mat3& r33 )
+	inline void transform_key_translation( uint8* data, float scale, const mat3& r33 )
 	{
 		vec3& p = *( reinterpret_cast<vec3*>( data ) );
@@ -161,16 +161,16 @@
 	}
 
-	inline void transform_key_raw( const key_descriptor& desc, uint8* data, float scale, const mat3& r33, const mat3& ri33 )
+	inline void transform_key_raw( const data_descriptor& desc, uint8* data, float scale, const mat3& r33, const mat3& ri33 )
 	{
-		for ( uint32 i = 0; i < desc.count; ++i )
+		for ( auto& slot : desc )
 		{
-			uint32 offset = desc.slots[i].offset;
-			switch ( desc.slots[i].vslot )
+			uint32 offset = slot.offset / 4;
+			switch ( slot.vslot )
 			{
-			case animation_slot::TIME:     break;
-			case animation_slot::POSITION: transform_key_position( data + offset, scale, r33 ); break;
-			case animation_slot::ROTATION: transform_key_rotation( data + offset, r33, ri33 ); break;
-			case animation_slot::SCALE:    transform_key_scale( data + offset, scale ); break;
-			case animation_slot::TFORM:    transform_key_transform( data + offset, scale, r33, ri33 ); break;
+			case slot::TIME:     break;
+			case slot::TRANSLATION: transform_key_translation( data + offset, scale, r33 ); break;
+			case slot::ROTATION:    transform_key_rotation( data + offset, r33, ri33 ); break;
+			case slot::SCALE:       transform_key_scale( data + offset, scale ); break;
+			case slot::TFORM:       transform_key_transform( data + offset, scale, r33, ri33 ); break;
 			default:
 				break;
Index: /trunk/nv/interface/interpolation_template.hh
===================================================================
--- /trunk/nv/interface/interpolation_template.hh	(revision 409)
+++ /trunk/nv/interface/interpolation_template.hh	(revision 410)
@@ -13,27 +13,63 @@
 #include <nv/core/transform.hh>
 #include <nv/stl/math.hh>
-#include <nv/interface/animation_key.hh>
+#include <nv/interface/data_descriptor.hh>
 
 namespace nv
 {
 
-	template < typename KEY, animation_slot SLOT > 
-	void interpolate_slot( KEY& key, const KEY& k1, const KEY& k2, float factor, const true_type& )
-	{
-		key_slot_info< KEY, SLOT >::interpolate( key, k1, k2, factor );
-	}
-
-	template < typename KEY, animation_slot SLOT > 
-	void interpolate_slot( KEY&, const KEY&, const KEY&, float, const false_type& )
-	{
-	}
+	template < typename Key, slot Slot, bool HasKey = has_slot< Key, Slot >::value >
+	struct slot_interpolator;
+
+	template < typename Key, slot Slot >
+	struct slot_interpolator< Key, Slot, false >
+	{
+		static void interpolate( Key&, const Key&, const Key&, float )
+		{
+		}
+	};
+
+	template < typename Key >
+	struct slot_interpolator< Key, slot::TRANSLATION, true >
+	{
+		static void interpolate( Key& key, const Key& k1, const Key& k2, float factor )
+		{
+			key.translation = ::nv::interpolate( k1.translation, k2.translation, factor );
+		}
+	};
+
+	template < typename Key >
+	struct slot_interpolator< Key, slot::ROTATION, true >
+	{
+		static void interpolate( Key& key, const Key& k1, const Key& k2, float factor )
+		{
+			key.rotation = nv::interpolate( k1.rotation, k2.rotation, factor );
+		}
+	};
+
+	template < typename Key >
+	struct slot_interpolator< Key, slot::SCALE, true >
+	{
+		static void interpolate( Key& key, const Key& k1, const Key& k2, float factor )
+		{
+			key.scale = nv::interpolate( k1.scale, k2.scale, factor );
+		}
+	};
+
+	template < typename Key >
+	struct slot_interpolator< Key, slot::TFORM, true >
+	{
+		static void interpolate( Key& key, const Key& k1, const Key& k2, float factor )
+		{
+			key.tform = nv::interpolate( k1.tform, k2.tform, factor );
+		}
+	};
 
 	template < typename KEY >
 	void interpolate_key( KEY& key, const KEY& k1, const KEY& k2, float factor )
 	{
-		interpolate_slot< KEY, animation_slot::POSITION >( key, k1, k2, factor, key_has_slot< KEY, animation_slot::POSITION >() );
-		interpolate_slot< KEY, animation_slot::ROTATION >( key, k1, k2, factor, key_has_slot< KEY, animation_slot::ROTATION >() );
-		interpolate_slot< KEY, animation_slot::SCALE >( key, k1, k2, factor, key_has_slot< KEY, animation_slot::SCALE >() );
-		interpolate_slot< KEY, animation_slot::TFORM >( key, k1, k2, factor, key_has_slot< KEY, animation_slot::TFORM >() );
+		slot_interpolator< KEY, slot::TRANSLATION >::interpolate( key, k1, k2, factor );
+		slot_interpolator< KEY, slot::ROTATION    >::interpolate( key, k1, k2, factor );
+		slot_interpolator< KEY, slot::SCALE       >::interpolate( key, k1, k2, factor );
+		slot_interpolator< KEY, slot::TFORM       >::interpolate( key, k1, k2, factor );
 	}
 
@@ -51,5 +87,5 @@
 
 		template < typename KEY >
-		mat4 extract_matrix_p_impl( const KEY& k ) { return glm::translate(mat4(),k.position); }
+		mat4 extract_matrix_p_impl( const KEY& k ) { return glm::translate(mat4(),k.translation); }
 		template < typename KEY >
 		mat4 extract_matrix_r_impl( const KEY& k ) { return glm::mat4_cast( k.rotation ); }
@@ -61,5 +97,5 @@
 			// TODO: this is obviously unoptimized
 			mat4 result = glm::mat4_cast( k.rotation );
-			result[3] = vec4( k.position, 1.0f );
+			result[3] = vec4( k.translation, 1.0f );
 			return result;
 		}
@@ -75,5 +111,5 @@
 		{
 			// TODO: this is obviously unoptimized
-			return glm::scale(glm::translate(mat4(),k.position),k.scale);
+			return glm::scale(glm::translate(mat4(),k.translation),k.scale);
 		}
 		template < typename KEY >
@@ -81,5 +117,5 @@
 		{
 			// TODO: this is obviously unoptimized
-			return glm::translate(mat4(),k.position) * glm::mat4_cast( k.rotation ) * glm::scale(mat4(),k.scale);
+			return glm::translate(mat4(),k.translation) * glm::mat4_cast( k.rotation ) * glm::scale(mat4(),k.scale);
 		}
 
@@ -113,7 +149,7 @@
 		transform extract_transform_pr_impl( const KEY&, const false_type&, const false_type& ) { return transform(); }
 		template < typename KEY >
-		transform extract_transform_pr_impl( const KEY& k, const true_type&, const true_type& ) { return transform( k.position, k.rotation ); }
-		template < typename KEY >
-		transform extract_transform_pr_impl( const KEY& k, const true_type&, const false_type& ) { return transform( k.position ); }
+		transform extract_transform_pr_impl( const KEY& k, const true_type&, const true_type& ) { return transform( k.translation, k.rotation ); }
+		template < typename KEY >
+		transform extract_transform_pr_impl( const KEY& k, const true_type&, const false_type& ) { return transform( k.translation ); }
 		template < typename KEY >
 		transform extract_transform_pr_impl( const KEY& k, const false_type&, const true_type& ) { return transform( k.rotation ); }
@@ -124,7 +160,7 @@
 		mat4 extract_matrix_impl( const KEY& k, const true_type& )
 		{
-			static_assert( key_has_slot< KEY, animation_slot::POSITION >::value == false, "key!");
-			static_assert( key_has_slot< KEY, animation_slot::ROTATION >::value == false, "key!");
-			static_assert( key_has_slot< KEY, animation_slot::SCALE >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::TRANSLATION >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::ROTATION >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::SCALE >::value == false, "key!");
 			return extract_matrix_slot( k.tform );
 		}
@@ -133,9 +169,9 @@
 		mat4 extract_matrix_impl( const KEY& k, const false_type& )
 		{
-			static_assert( key_has_slot< KEY, animation_slot::TFORM >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::TFORM >::value == false, "key!");
 			return extract_matrix_prs( k,
-				key_has_slot< KEY, animation_slot::POSITION >(),
-				key_has_slot< KEY, animation_slot::ROTATION >(),
-				key_has_slot< KEY, animation_slot::SCALE >()
+				has_slot< KEY, slot::TRANSLATION >(),
+				has_slot< KEY, slot::ROTATION >(),
+				has_slot< KEY, slot::SCALE >()
 				);
 		}
@@ -144,7 +180,7 @@
 		transform extract_transform_impl( const KEY& k, const true_type& )
 		{
-			static_assert( key_has_slot< KEY, animation_slot::POSITION >::value == false, "key!");
-			static_assert( key_has_slot< KEY, animation_slot::ROTATION >::value == false, "key!");
-			static_assert( key_has_slot< KEY, animation_slot::SCALE >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::TRANSLATION >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::ROTATION >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::SCALE >::value == false, "key!");
 			return extract_transfrom_slot( k.tform );
 		}
@@ -153,9 +189,9 @@
 		transform extract_transform_impl( const KEY& k, const false_type& )
 		{
-			static_assert( key_has_slot< KEY, animation_slot::TFORM >::value == false, "key!");
-			static_assert( key_has_slot< KEY, animation_slot::SCALE >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::TFORM >::value == false, "key!");
+			static_assert( has_slot< KEY, slot::SCALE >::value == false, "key!");
 			return extract_transform_pr_impl( k, 
-				key_has_slot< KEY, animation_slot::POSITION >(),
-				key_has_slot< KEY, animation_slot::ROTATION >()
+				has_slot< KEY, slot::TRANSLATION >(),
+				has_slot< KEY, slot::ROTATION >()
 				);
 		}
@@ -165,5 +201,5 @@
 	mat4 extract_matrix( const KEY& k )
 	{
-		return detail::extract_matrix_impl( k, key_has_slot< KEY, animation_slot::TFORM >() );
+		return detail::extract_matrix_impl( k, has_slot< KEY, slot::TFORM >() );
 	}
 
@@ -171,5 +207,5 @@
 	transform extract_transform( const KEY& k )
 	{
-		return detail::extract_transform_impl( k, key_has_slot< KEY, animation_slot::TFORM >() );
+		return detail::extract_transform_impl( k, has_slot< KEY, slot::TFORM >() );
 	}
 
Index: /trunk/nv/interface/mesh_data.hh
===================================================================
--- /trunk/nv/interface/mesh_data.hh	(revision 409)
+++ /trunk/nv/interface/mesh_data.hh	(revision 410)
@@ -11,10 +11,17 @@
 #include <nv/stl/math.hh>
 #include <nv/stl/string.hh>
+#include <nv/interface/data_descriptor.hh>
 #include <nv/gfx/animation.hh>
-#include <nv/interface/vertex.hh>
 
 namespace nv
 {
 
+	enum buffer_type
+	{
+		VERTEX_BUFFER,
+		INDEX_BUFFER,
+	};
+
+
 	// TODO: friend mesh_data_creator class?
 	// TODO: private const etc
@@ -25,7 +32,7 @@
 		friend class mesh_creator;
 
-		vertex_descriptor desc;
-		uint8*            data;
-		uint32            count;
+		data_descriptor desc;
+		uint8*          data;
+		uint32          count;
 
 		mesh_raw_channel() : data( nullptr ), count( 0 ) {}
@@ -38,5 +45,5 @@
 		buffer_type get_buffer_type() const
 		{
-			if ( count > 0 && desc.slots[0].vslot == slot::INDEX )
+			if ( count > 0 && desc[0].vslot == slot::INDEX )
 			{
 				return INDEX_BUFFER;
@@ -45,5 +52,5 @@
 		}
 
-		uint32 size() const { return count * desc.size; }
+		uint32 size() const { return count * desc.element_size(); }
 
 		template < typename VTX >
@@ -56,5 +63,5 @@
 			return result;
 		}
-		static mesh_raw_channel* create( const vertex_descriptor& vtxdesc, uint32 count = 0 )
+		static mesh_raw_channel* create( const data_descriptor& vtxdesc, uint32 count = 0 )
 		{
 			mesh_raw_channel* result = new mesh_raw_channel();
@@ -119,9 +126,7 @@
 			for ( auto ch : m_channels )
 			{
-				for ( uint32 i = 0; i < ch->desc.count; ++i )
-					if ( ch->desc.slots[i].vslot == s )
-					{
+				for ( auto slot : ch->desc )
+					if ( slot.vslot == s )
 						return ch;
-					}
 			}
 			return nullptr;
@@ -132,10 +137,7 @@
 			for ( uint32 c = 0; c < m_channels.size(); ++c )
 			{
-				const mesh_raw_channel* ch = m_channels[c];
-				for ( uint32 i = 0; i < ch->desc.count; ++i )
-					if ( ch->desc.slots[i].vslot == s )
-					{
+				for ( auto slot : m_channels[c]->desc )
+					if ( slot.vslot == s )
 						return int( c );
-					}
 			}
 			return -1;
@@ -159,5 +161,5 @@
 		const mesh_raw_channel* get_channel() const
 		{
-			vertex_descriptor compare;
+			data_descriptor compare;
 			compare.initialize<VTX>();
 			for ( auto ch : m_channels )
@@ -174,5 +176,5 @@
 		const VTX* get_channel_data() const
 		{
-			vertex_descriptor compare;
+			data_descriptor compare;
 			compare.initialize<VTX>();
 			for ( auto ch : m_channels )
Index: unk/nv/interface/vertex.hh
===================================================================
--- /trunk/nv/interface/vertex.hh	(revision 409)
+++ 	(revision )
@@ -1,313 +1,0 @@
-// Copyright (C) 2014-2015 ChaosForge Ltd
-// http://chaosforge.org/
-//
-// This file is part of Nova libraries. 
-// For conditions of distribution and use, see copying.txt file in root folder.
-
-// WARNING: this file is explicitly designed to fuck with your brain
-
-#ifndef NV_INTERFACE_VERTEX_HH
-#define NV_INTERFACE_VERTEX_HH
-
-#include <nv/common.hh>
-#include <nv/core/transform.hh>
-#include <nv/stl/math.hh>
-
-namespace nv
-{
-
-	enum class slot : uint8
-	{
-		POSITION       = 0,
-		TEXCOORD       = 1,
-		NORMAL         = 2,
-		TANGENT        = 3,
-		BONEINDEX      = 4,
-		BONEWEIGHT     = 5,
-		COLOR          = 6,
-
-		INDEX          = 7,
-		SLOT_MAX       = 7,
-		SLOT_MAX_STORE = 8,
-	};
-
-	enum buffer_hint
-	{
-		STATIC_DRAW,
-		STREAM_DRAW,
-		DYNAMIC_DRAW
-	};
-
-	enum buffer_type
-	{
-		VERTEX_BUFFER,
-		INDEX_BUFFER,
-	};
-
-
-	namespace detail
-	{
-
-		template < typename VT, slot SLOT >
-		struct vertex_has_slot_impl
-		{
-			static bool const value = false;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::POSITION >
-		{
-		private:
-			struct fallback { int position; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::position>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::NORMAL >
-		{
-		private:
-			struct fallback { int normal; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::normal>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::TEXCOORD >
-		{
-		private:
-			struct fallback { int texcoord; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::texcoord>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::TANGENT >
-		{
-		private:
-			struct fallback { int tangent; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::tangent>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::BONEINDEX >
-		{
-		private:
-			struct fallback { int boneindex; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::boneindex>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::BONEWEIGHT >
-		{
-		private:
-			struct fallback { int boneweight; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::boneweight>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT >
-		struct vertex_has_slot_impl< VT, slot::COLOR >
-		{
-		private:
-			struct fallback { int color; };
-			struct derived : VT, fallback { };
-			template<typename C, C> struct cht; 
-			template<typename C> static char (&test(cht<int fallback::*, &C::color>*))[1]; 
-			template<typename C> static char (&test(...))[2]; 
-		public:
-			static bool const value = sizeof(test<derived>(0)) == 2;
-		};
-
-		template < typename VT, slot SLOT, bool HAS_SLOT >
-		struct vertex_slot_info_impl
-		{
-		};
-
-		template < typename VT, slot SLOT >
-		struct vertex_slot_info_impl < VT, SLOT, false >
-		{
-			typedef empty_type value_type;
-			static const datatype etype  = datatype::NONE;
-			static const int      offset = 0;
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::POSITION, true >
-		{
-			typedef decltype( VT::position ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::position ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, position );
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::TEXCOORD, true >
-		{
-			typedef decltype( VT::texcoord ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::texcoord ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, texcoord );
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::NORMAL, true >
-		{
-			typedef decltype( VT::normal ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::normal ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, normal );
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::TANGENT, true >
-		{
-			typedef decltype( VT::tangent ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::tangent ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, tangent );
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::BONEINDEX, true >
-		{
-			typedef decltype( VT::boneindex ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::boneindex ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, boneindex );
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::BONEWEIGHT, true >
-		{
-			typedef decltype( VT::boneweight ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::boneweight ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, boneweight );
-		};
-
-		template < typename VT >
-		struct vertex_slot_info_impl< VT, slot::COLOR, true >
-		{
-			typedef decltype( VT::color ) value_type;
-			static const datatype etype  = type_to_enum< decltype( VT::color ) >::type;
-			static const int      offset = NV_OFFSET_OF( VT, color );
-		};
-
-	}
-
-
-	template < typename VT, slot SLOT >
-	struct vertex_has_slot : public detail::vertex_has_slot_impl< VT, SLOT >
-	{
-	};
-
-
-	template < typename VT, slot SLOT >
-	struct vertex_slot_info : public detail::vertex_slot_info_impl< VT, SLOT, detail::vertex_has_slot_impl< VT, SLOT >::value >
-	{
-	};
-
-	struct vertex_descriptor_slot
-	{
-		datatype etype;
-		uint32   offset;
-		slot     vslot;
-	};
-
-	struct vertex_descriptor
-	{
-		vertex_descriptor_slot slots[ uint16( slot::SLOT_MAX_STORE ) ];
-		uint32                 count;
-		uint32                 size;
-
-		template < typename IDX >
-		void initialize_index()
-		{
-			count = 1;
-			size  = sizeof( IDX );
-			slots[0].etype  = type_to_enum< IDX >::type;
-			slots[0].vslot  = slot::INDEX;
-			slots[0].offset = 0;
-		}
-
-		void initialize_index( datatype itype )
-		{
-			count = 1;
-			size  = get_datatype_info( itype ).size;
-			slots[0].etype  = itype;
-			slots[0].vslot  = slot::INDEX;
-			slots[0].offset = 0;
-		}
-
-		template < typename VTX >
-		void initialize()
-		{
-			count = 0;
-			initialize_slot< VTX, slot::POSITION >();
-			initialize_slot< VTX, slot::TEXCOORD >();
-			initialize_slot< VTX, slot::NORMAL >();
-			initialize_slot< VTX, slot::TANGENT >();
-			initialize_slot< VTX, slot::BONEINDEX >();
-			initialize_slot< VTX, slot::BONEWEIGHT >();
-			initialize_slot< VTX, slot::COLOR >();
-			size = sizeof( VTX );
-		}
-
-		bool operator!=( const vertex_descriptor& rhs ) const
-		{
-			return !( *this == rhs );
-		}
-
-		bool operator==( const vertex_descriptor& rhs ) const
-		{
-			if ( size  != rhs.size )  return false;
-			if ( count != rhs.count ) return false;
-			for ( uint32 i = 0; i < count; ++i )
-			{
-				if ( slots[i].etype  != rhs.slots[i].etype )  return false;
-				if ( slots[i].offset != rhs.slots[i].offset ) return false;
-				if ( slots[i].vslot  != rhs.slots[i].vslot )  return false;
-			}
-			return true;
-		}
-
-	private:
-		template < typename VTX, slot SLOT >
-		void initialize_slot()
-		{
-			typedef vertex_slot_info< VTX, SLOT > slot_info;
-			slots[ count ].etype  = slot_info::etype;
-			if ( slots[ count ].etype != datatype::NONE )
-			{
-				slots[ count ].vslot  = SLOT;
-				slots[ count ].offset = slot_info::offset;
-				count++;
-			}
-		}
-
-	};
-
-}
-
-#endif // NV_INTERFACE_VERTEX_HH
Index: /trunk/nv/stl/container/contiguous_storage_policy.hh
===================================================================
--- /trunk/nv/stl/container/contiguous_storage_policy.hh	(revision 409)
+++ /trunk/nv/stl/container/contiguous_storage_policy.hh	(revision 410)
@@ -50,4 +50,5 @@
 		fixed_storage( fixed_storage&& ) = default;
 		fixed_storage& operator=( fixed_storage&& ) = default;
+
 	};
 
@@ -73,7 +74,7 @@
 		operator array_ref< value_type >() { return array_ref< value_type >( Storage::data(), size() ); }
 		operator array_view< value_type >() const { return array_view< value_type >( Storage::data(), size() ); }
-	protected:
+
 		constexpr resizable_storage() : m_size( 0 ) {}
-
+	protected:
 		// allow move
 		inline resizable_storage( resizable_storage&& other )
@@ -84,4 +85,5 @@
 		inline resizable_storage& operator=( resizable_storage&& other )
 		{
+			if ( m_size > 0 ) Storage::reallocate( 0, false );
 			m_size = other.m_size;
 			Storage::operator=( nv::move( other ) );
@@ -113,5 +115,5 @@
 		static SizeType get( SizeType requested, SizeType capacity, SizeType max_size )
 		{
-			SizeType minimum = nv::min<SizeType>( capacity, 4 );
+			SizeType minimum = nv::max<SizeType>( capacity, 4 );
 			SizeType remaining = max_size - capacity;
 			if ( remaining < requested ) return 0;
@@ -142,7 +144,7 @@
 		operator array_ref< value_type >() { return array_ref< value_type >( Storage::data(), size() ); }
 		operator array_view< value_type >() const { return array_view< value_type >( Storage::data(), size() ); }
-	protected:
+
 		constexpr growable_storage() : m_size( 0 ), m_capacity( 0 ) {}
-
+	protected:
 		// allow move
 		inline growable_storage( growable_storage&& other )
@@ -154,4 +156,5 @@
 		inline growable_storage& operator=( growable_storage&& other )
 		{
+			if ( m_capacity > 0 ) Storage::reallocate( 0, false );
 			m_size = other.m_size;
 			m_capacity = other.m_capacity;
@@ -172,5 +175,4 @@
 				{
 					m_capacity = new_capacity;
-					m_size = new_size;
 				}
 				else return false;
@@ -184,5 +186,5 @@
 			if ( new_capacity > m_capacity )
 			{
-				if ( Storage::reallocate( new_capacity, copy_needed ) )
+				if ( new_capacity > 0 && Storage::reallocate( new_capacity, copy_needed ) )
 				{
 					m_capacity = new_capacity;
Index: /trunk/nv/stl/container/growing_container_handler.hh
===================================================================
--- /trunk/nv/stl/container/growing_container_handler.hh	(revision 409)
+++ /trunk/nv/stl/container/growing_container_handler.hh	(revision 410)
@@ -28,6 +28,9 @@
 	{
 	public:
+		typedef typename growing_container_handler< Storage, InitializePolicy > this_type;
 		typedef typename Storage::value_type value_type;
 		typedef typename Storage::size_type  size_type;
+		typedef value_type*                  iterator;
+		typedef const value_type*            const_iterator;
 
 		using sized_container_handler< Storage, InitializePolicy >::sized_container_handler;
@@ -50,9 +53,65 @@
 			if ( Storage::try_grow( 1 ) ) construct_object( Storage::data() + Storage::size() - 1, forward<Args>( args )... );
 		}
+
 		void pop_back()
 		{
+			InitializePolicy::destroy( Storage::data() + Storage::size() - 1 );
 			Storage::try_resize( Storage::size() - 1, true );
 		}
 
+		// TODO: implement swap_erase
+		iterator erase( iterator position )
+		{
+			iterator iend = Storage::data() + Storage::size();
+			InitializePolicy::destroy( position );
+			if ( ( position + 1 ) < iend )
+				raw_alias_copy( position + 1, iend, position );
+			Storage::try_resize( Storage::size() - 1, true );
+			return position;
+		}
+
+		iterator erase( iterator first, iterator last )
+		{
+			iterator iend = Storage::data() + Storage::size();
+			InitializePolicy::destroy( first, last );
+			iterator position = raw_alias_copy( last, iend, first );
+			Storage::try_resize( Storage::size() - ( last - first ), true );
+			return position;
+		}
+
+		void append( const_iterator first, const_iterator last )
+		{
+			// TODO: distance can't be called on destructive iterators - check 
+			//   and use pushback if needed?
+			size_type d        = distance( first, last );
+			size_type old_size = Storage::size();
+			if ( Storage::try_grow( d ) )
+				InitializePolicy::copy( first, last, Storage::data() + old_size );
+		}
+
+		// TODO: This can be much optimized in the grow case by copying in three parts
+		void insert( iterator position, const value_type& value )
+		{
+			iterator iend = Storage::data() + Storage::size();
+			if ( Storage::try_grow( 1 ) )
+			{
+				raw_alias_copy( position, iend, position + 1 );
+				copy_construct_object( position, value );
+			}
+		}
+
+		// TODO: This can be much optimized in the grow case by copying in three parts
+		void insert( iterator position, const_iterator first, const_iterator last )
+		{
+			// TODO: distance can't be called on destructive iterators - check 
+			//   and use pushback if needed?
+			iterator iend = Storage::data() + Storage::size();
+			size_type d = distance( first, last );
+			if ( Storage::try_grow( d ) )
+			{
+				raw_alias_copy( position, iend, position + d );
+				InitializePolicy::copy( first, last, position );
+			}
+		}
 	};
 
Index: /trunk/nv/stl/string.hh
===================================================================
--- /trunk/nv/stl/string.hh	(revision 409)
+++ /trunk/nv/stl/string.hh	(revision 410)
@@ -346,5 +346,5 @@
 	{
 		if ( p > this->size() ) return string_view(); // NV_THROW( out_of_range( "substr" ) );
-		if ( n == p || p + n > this->size() ) n = this->size() - p;
+		if ( n == npos || n == p || p + n > this->size() ) n = this->size() - p;
 		return string_view( this->data() + p, n );
 	}
Index: /trunk/nv/stl/type_traits/experimental.hh
===================================================================
--- /trunk/nv/stl/type_traits/experimental.hh	(revision 409)
+++ /trunk/nv/stl/type_traits/experimental.hh	(revision 410)
@@ -43,11 +43,13 @@
 
 #if NV_COMPILER == NV_MSVC
+#define NV_VOID_DECLTYPE( EXPR ) ::nv::match_ptr_t< &EXPR >
 #define NV_GENERATE_HAS_MEMBER( MEMBER ) \
 template< typename, typename = nv::void_t<> > struct has_##MEMBER##_member : nv::false_type {}; \
-template< typename T > struct has_##MEMBER##_member <T, nv::void_t< nv::match_ptr_t< &T::MEMBER > > > : true_type{};
+template< typename T > struct has_##MEMBER##_member <T, nv::void_t< NV_VOID_DECLTYPE( T::MEMBER ) > > : true_type{};
 #else
+#define NV_VOID_DECLTYPE( EXPR ) decltype( EXPR )
 #define NV_GENERATE_HAS_MEMBER( MEMBER ) \
 template< typename, typename = nv::void_t<> > struct has_##MEMBER##_member : nv::false_type {}; \
-template< typename T > struct has_##MEMBER##_member <T, nv::void_t< decltype( T::MEMBER ) > > : true_type{};
+template< typename T > struct has_##MEMBER##_member <T, nv::void_t< NV_VOID_DECLTYPE( T::MEMBER ) > > : true_type{};
 #endif
 
Index: /trunk/nv/stl/type_traits/transforms.hh
===================================================================
--- /trunk/nv/stl/type_traits/transforms.hh	(revision 409)
+++ /trunk/nv/stl/type_traits/transforms.hh	(revision 410)
@@ -40,9 +40,9 @@
 
 		template< typename SOURCE, typename TARGET,
-			bool CONST = is_const<SOURCE>::value,
-			bool VOLATILE = is_volatile<SOURCE>::value >
+			bool IsConst = is_const<SOURCE>::value,
+			bool IsVolatile = is_volatile<SOURCE>::value >
 		struct match_cv
 		{
-			typedef typename cv_selector< TARGET, CONST, VOLATILE >::type type;
+			typedef typename cv_selector< TARGET, IsConst, IsVolatile >::type type;
 		};
 
@@ -73,5 +73,5 @@
 		template<> struct unsigned_type < signed long long > { typedef unsigned long long type; };
 
-		template < typename T, bool IS_ENUM = is_enum< T >::value >
+		template < typename T, bool IsEnum = is_enum< T >::value >
 		struct make_signed_impl;
 
Index: /trunk/nv/wx/wx_canvas.hh
===================================================================
--- /trunk/nv/wx/wx_canvas.hh	(revision 409)
+++ /trunk/nv/wx/wx_canvas.hh	(revision 410)
@@ -31,4 +31,6 @@
 		virtual int OnExit();
 		virtual bool initialize() = 0;
+	protected:
+		nv::logger m_logger;
 	};
 
@@ -88,5 +90,5 @@
 	public:
 		wx_log_text_ctrl_sink( wxTextCtrl* a_text_ctrl );
-		virtual void log( nv::log_level level, const std::string& message );
+		virtual void log( nv::log_level level, const nv::string_view& message );
 	private:
 		wxTextCtrl* m_text_ctrl;
Index: /trunk/nv_wx.lua
===================================================================
--- /trunk/nv_wx.lua	(revision 409)
+++ /trunk/nv_wx.lua	(revision 410)
@@ -10,7 +10,7 @@
 solution "*"
 	includedirs { 
-		"C:/wxwidgets/include/msvc/",
-		"C:/wxwidgets/include/",
+		"D:/Libraries/wxwidgets/include/msvc/",
+		"D:/Libraries/wxwidgets/include/",
 	}
-	libdirs { "C:/wxwidgets/lib/vc120_dll" }
+	libdirs { "D:/Libraries/wxwidgets/lib/vc140_dll" }
 	defines { "__WXMSW__", "_UNICODE", "WXUSINGDLL", "wxMSVC_VERSION_AUTO" }
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" );
 }
