Index: trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- trunk/nv/gfx/skeletal_mesh.hh	(revision 466)
+++ trunk/nv/gfx/skeletal_mesh.hh	(revision 467)
@@ -17,95 +17,8 @@
 {
 	
-	// TODO: remove or make generic
-	struct md5_vtx_pnt
-	{
-		vec3 position;
-		vec3 normal;
-		vec3 tangent;
-	};
-
-	// TODO: remove or make generic
-	struct md5_key_t
-	{
-		transform tform;
-	};
-
-	// TODO: remove or make generic
-	struct md5_vtx_t
-	{
-		vec2 texcoord;
-	};
-
-	// TODO: remove or make generic
-	struct md5_vtx_pntiw
-	{
-		vec3  position;
-		vec3  normal;
-		vec3  tangent;
-		ivec4 boneindex;
-		vec4  boneweight;
-	};
-
-
-	class skeletal_mesh : public animated_mesh
-	{
-	public:
-		skeletal_mesh( context* a_context ) : animated_mesh(), m_context( a_context ) {}
-		virtual vertex_array get_vertex_array() const { return m_va; }
-		virtual void run_animation( animation_entry* a_anim )
-		{
-			update_animation( a_anim, 0 );
-		}
-		~skeletal_mesh()
-		{
-			m_context->release( m_va );
-		}
-	protected:
-		vertex_array m_va;
-		context*     m_context;
-		data_descriptor m_interpolation_key;
-	};
-
-	class skeletal_animation_entry_cpu : public animation_entry
-	{
-	public:
-		skeletal_animation_entry_cpu( shash64 name, const mesh_nodes_data* a_animation, bool a_looping )
-			: animation_entry( name, a_looping, a_animation->get_frame_rate(), 0.0f, a_animation->get_duration() ), m_node_data( a_animation )
-		{
-			initialize();
-		}
-		skeletal_animation_entry_cpu( shash64 name, mesh_nodes_data* a_animation, float time_start, float time_end, bool a_looping )
-			: animation_entry( name, a_looping, a_animation->get_frame_rate(), time_start, time_end ), m_node_data( a_animation )
-		{
-			initialize();
-		}
-		void initialize();
-		void update_skeleton( transform* skeleton, float time ) const;
-	protected:
-		const mesh_nodes_data* m_node_data;
-		data_descriptor m_interpolation_key;
-	};
-
-	class skeletal_mesh_cpu : public skeletal_mesh
-	{
-	public:
-		skeletal_mesh_cpu( context* a_context, const data_channel_set* a_mesh_data, const mesh_nodes_data* bones );
-		virtual size_t get_index_count() const { return m_indices; }
-		virtual void update_animation( animation_entry* a_anim, uint32 a_anim_time );
-	protected:
-		buffer                       m_pbuffer;
-		uint32                       m_indices;
-		dynamic_array< md5_vtx_pnt > m_pntdata;
-		dynamic_array< transform >   m_pos_offset;
-		dynamic_array< transform >   m_bone_offset;
-
-		const md5_vtx_pntiw*         m_vtx_data;
-		dynamic_array< transform >   m_transform;
-	};
-
- 	class skeletal_animation_entry_gpu : public animation_entry
+ 	class skeletal_animation_entry : public animation_entry
  	{
  	public:
- 		skeletal_animation_entry_gpu( shash64 name, const mesh_nodes_data* anim, bool a_looping )
+ 		skeletal_animation_entry( shash64 name, const mesh_nodes_data* anim, bool a_looping )
 			: animation_entry( name, a_looping, anim->get_frame_rate(), 0.0f, anim->get_duration() )
 			, m_node_data( anim )
@@ -114,5 +27,5 @@
 		}
 
-		skeletal_animation_entry_gpu( shash64 name, const mesh_nodes_data* anim, float time_start, float time_end, bool a_looping )
+		skeletal_animation_entry( shash64 name, const mesh_nodes_data* anim, float time_start, float time_end, bool a_looping )
 			: animation_entry( name, a_looping, anim->get_frame_rate(), time_start, time_end )
 			, m_node_data( anim )
@@ -123,5 +36,5 @@
 		void prepare( const mesh_nodes_data* bones );
 		void update_skeleton( mat4* tr, uint32 time ) const;
-		~skeletal_animation_entry_gpu();
+		~skeletal_animation_entry();
  	protected:
 		void initialize();
@@ -137,9 +50,14 @@
  	};
 
-	class skeletal_mesh_gpu : public skeletal_mesh
+	class skeletal_mesh : public animated_mesh
 	{
 	public:
-		skeletal_mesh_gpu( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data );
+		skeletal_mesh( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data );
+		virtual vertex_array get_vertex_array() const { return m_va; }
 		virtual size_t get_index_count() const { return m_index_count; }
+		virtual void run_animation( animation_entry* a_anim )
+		{
+			update_animation( a_anim, 0 );
+		}
 		virtual void update( program a_program );
 		virtual void update_animation( animation_entry* a_anim, uint32 
@@ -147,6 +65,13 @@
 		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()
+		{
+			m_context->release( m_va );
+			delete[] m_transform;
+		}
 	protected:
+		vertex_array m_va;
+		context*     m_context;
+		data_descriptor m_interpolation_key;
 		const mesh_nodes_data* m_bone_data;
 		uint32 m_index_count;
Index: trunk/nv/stl/string.hh
===================================================================
--- trunk/nv/stl/string.hh	(revision 466)
+++ trunk/nv/stl/string.hh	(revision 467)
@@ -130,5 +130,18 @@
 	typedef string_hash< uint32 > shash32;
 	typedef string_hash< uint64 > shash64;
+
+	constexpr shash32 operator "" _sh32( const char* str, size_t len )
+	{
+		return shash32( detail::fnv_hash< uint32 >::hash( str, len ) );
+	}
+
+	constexpr shash64 operator "" _sh64( const char* str, size_t len )
+	{
+		return shash64( detail::fnv_hash< uint64 >::hash( str, len ) );
+	}
 }
 
+using nv::operator "" _sh32;
+using nv::operator "" _sh64;
+
 #endif // NV_STL_STRING_HH
Index: trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- trunk/src/gfx/skeletal_mesh.cc	(revision 466)
+++ trunk/src/gfx/skeletal_mesh.cc	(revision 467)
@@ -11,111 +11,5 @@
 #include "nv/stl/unordered_map.hh"
 
-nv::skeletal_mesh_cpu::skeletal_mesh_cpu( context* a_context, const data_channel_set* a_mesh_data, const mesh_nodes_data* bones )
-	: skeletal_mesh( a_context )
-{
-	const raw_data_channel* pnt_chan   = a_mesh_data->get_channel<md5_vtx_pnt>();
-	const raw_data_channel* pntiw_chan = a_mesh_data->get_channel<md5_vtx_pntiw>();
-
-	m_pntdata.assign( pnt_chan->data_cast< md5_vtx_pnt >(), pnt_chan->size() );
-	m_bone_offset.resize( bones->size() );
-	m_transform.resize( bones->size() );
-
-	for ( uint32 i = 0; i < bones->size(); ++i )
-	{
-		m_bone_offset[i] = transform( (*bones)[i]->get_transform() );
-	}
-
-	m_vtx_data  = a_mesh_data->get_channel_data<md5_vtx_pntiw>();
-	m_indices   = a_mesh_data->get_channel_size( slot::INDEX );
-	m_va        = a_context->create_vertex_array();
-
-	//array_view< raw_data_channel* > channels = a_mesh_data->get_raw_channels();
-	for ( auto& channel : *a_mesh_data )
-	{
-		//const raw_data_channel* channel = channels[ch];
-		if ( channel.size() > 0 && &channel != pntiw_chan )
-		{
-			const data_descriptor& desc = channel.descriptor();
-			if ( desc[0].vslot == slot::INDEX )
-			{
-				buffer b = a_context->get_device()->create_buffer( INDEX_BUFFER, STREAM_DRAW, channel.raw_size(), channel.raw_data() );
-				a_context->set_index_buffer( m_va, b, desc[0].etype, true );
-			}
-			else
-			{
-				buffer b = a_context->get_device()->create_buffer( VERTEX_BUFFER, STREAM_DRAW, channel.raw_size(), channel.raw_data() );
-				a_context->add_vertex_buffers( m_va, b, desc );
-			}
-		}
-	}
-
-	m_pbuffer   = a_context->find_buffer( m_va, slot::POSITION );
-}
-
-void nv::skeletal_mesh_cpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
-{
-	if ( a_anim )
-	{
-		skeletal_animation_entry_cpu * anim = static_cast<skeletal_animation_entry_cpu*>( a_anim );
-		anim->update_skeleton( m_transform.data(), static_cast<float>( a_anim_time ) );
-		{
-			size_t skeleton_size = m_bone_offset.size();
-			size_t vertex_count  = m_pntdata.size();
-			m_pos_offset.resize( skeleton_size );
-			for ( unsigned int i = 0; i < skeleton_size; ++i )
-			{
-				m_pos_offset[i] = m_transform[i] * m_bone_offset[i];
-			}
-
-			fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
-			for ( unsigned int i = 0; i < vertex_count; ++i )
-			{
-				const md5_vtx_pntiw& vert = m_vtx_data[i];
-
-				for ( int j = 0; j < 4; ++j )
-				{
-					unsigned index = unsigned( vert.boneindex[j] );
-					float weight   = vert.boneweight[j];
-					const quat& orient      = m_transform[index].get_orientation();
-					const transform& offset = m_pos_offset[index];
-					m_pntdata[i].position += offset.transformed( vert.position )        * weight;
-					m_pntdata[i].normal   += ( orient * vert.normal  ) * weight;
-					m_pntdata[i].tangent  += ( orient * vert.tangent ) * weight;
-				}
-			}
-		}
-
-		m_context->update( m_pbuffer, m_pntdata.data(), 0, m_pntdata.raw_size() );
-	}
-}
-
-
-void nv::skeletal_animation_entry_cpu::initialize()
-{
-	for ( auto bone : *m_node_data )
-	{
-		if ( bone->size() > 0 )
-		{
-			m_interpolation_key = bone->get_interpolation_key();
-			break;
-		}
-	}
-}
-
-void nv::skeletal_animation_entry_cpu::update_skeleton( transform* skeleton, float time ) const
-{
-	float frame_duration = 1000.f / static_cast<float>( m_node_data->get_frame_rate() );
-	float anim_duration = frame_duration * m_node_data->get_duration();
-	float new_time = fmodf( time, anim_duration ) * 0.001f;
-
-	float frame_num = new_time * m_node_data->get_frame_rate();
-	for ( size_t i = 0; i < m_node_data->size(); ++i )
-	{
-		raw_channel_interpolator interpolator( (*m_node_data)[i], m_interpolation_key );
-		skeleton[i] = interpolator.get< transform >( frame_num );
-	}
-}
-
-void nv::skeletal_animation_entry_gpu::initialize()
+void nv::skeletal_animation_entry::initialize()
 {
 	m_prepared  = false;
@@ -140,5 +34,5 @@
 }
 
-void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time ) const
+void nv::skeletal_animation_entry::update_skeleton( mat4* data, uint32 time ) const
 {
 	float tick_time = ( time * 0.001f ) * m_frame_rate;
@@ -169,5 +63,5 @@
 }
 
-void nv::skeletal_animation_entry_gpu::prepare( const mesh_nodes_data* bones )
+void nv::skeletal_animation_entry::prepare( const mesh_nodes_data* bones )
 {
 	if ( m_prepared ) return;
@@ -200,5 +94,5 @@
 }
 
-void nv::skeletal_animation_entry_gpu::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
+void nv::skeletal_animation_entry::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
 {
 	// TODO: fix transforms, which are now embedded,
@@ -227,5 +121,5 @@
 }
 
-nv::skeletal_animation_entry_gpu::~skeletal_animation_entry_gpu()
+nv::skeletal_animation_entry::~skeletal_animation_entry()
 {
 	delete[] m_offsets;
@@ -234,6 +128,6 @@
 }
 
-nv::skeletal_mesh_gpu::skeletal_mesh_gpu( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data )
-	: skeletal_mesh( a_context ), m_bone_data( a_bone_data ), m_index_count( 0 ), m_transform( nullptr )
+nv::skeletal_mesh::skeletal_mesh( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data )
+	: m_context( a_context ), m_bone_data( a_bone_data ), m_index_count( 0 ), m_transform( nullptr )
 {
 	if ( a_mesh )
@@ -248,9 +142,9 @@
 }
 
-void nv::skeletal_mesh_gpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
+void nv::skeletal_mesh::update_animation( animation_entry* a_anim, uint32 a_anim_time )
 {
 	if ( m_bone_data && a_anim )
 	{
-		skeletal_animation_entry_gpu * anim = static_cast<skeletal_animation_entry_gpu*>( a_anim );
+		skeletal_animation_entry * anim = static_cast<skeletal_animation_entry*>( a_anim );
 		anim->prepare( m_bone_data );
 		anim->update_skeleton( m_transform, a_anim_time );
@@ -258,5 +152,5 @@
 }
 
-void nv::skeletal_mesh_gpu::update( program a_program )
+void nv::skeletal_mesh::update( program a_program )
 {
 	if ( m_bone_data )
@@ -264,5 +158,5 @@
 }
 
-nv::transform nv::skeletal_mesh_gpu::get_node_transform( uint32 node_id ) const
+nv::transform nv::skeletal_mesh::get_node_transform( uint32 node_id ) const
 {
 	if ( node_id == 0 ) return transform();
@@ -271,5 +165,5 @@
 }
 
-nv::mat4 nv::skeletal_mesh_gpu::get_node_matrix( uint32 node_id ) const
+nv::mat4 nv::skeletal_mesh::get_node_matrix( uint32 node_id ) const
 {
 	return m_transform[ node_id ];
Index: trunk/src/wx/wx_canvas.cc
===================================================================
--- trunk/src/wx/wx_canvas.cc	(revision 466)
+++ trunk/src/wx/wx_canvas.cc	(revision 467)
@@ -63,4 +63,19 @@
 }
 
+static wxColor wx_log_color[] =
+{
+	wxColor( *wxWHITE ),
+	wxColor( *wxRED ),
+	wxColor( *wxRED ),
+	wxColor( *wxRED ),
+	wxColor( *wxYELLOW ),
+	wxColor( *wxGREEN ),
+	wxColor( *wxLIGHT_GREY ),
+	wxColor( *wxLIGHT_GREY ),
+	wxColor( *wxLIGHT_GREY ),
+	wxColor( *wxLIGHT_GREY ),
+	wxColor( *wxLIGHT_GREY ),
+};
+
 void nv::wx_log_text_ctrl_sink::log( nv::log_level level, const nv::string_view& message )
 {
@@ -68,5 +83,6 @@
 	char stamp[16];
 	size_t ssize = timestamp( stamp );
-	str << stamp << " [" << padded_level_name( level ).data() << "] " << message.data() << "\n";
+	m_text_ctrl->SetDefaultStyle( wxTextAttr( wx_log_color[ level / 10 ] ) );
+	str << "[" << padded_level_name( level ).data() << "] " << message.data() << "\n";
 	m_text_ctrl->AppendText( str );
 }
