Index: trunk/nv/formats/md3_loader.hh
===================================================================
--- trunk/nv/formats/md3_loader.hh	(revision 152)
+++ trunk/nv/formats/md3_loader.hh	(revision 153)
@@ -21,4 +21,5 @@
 namespace nv 
 {
+
 	struct md3_tag
 	{
@@ -39,4 +40,5 @@
 		virtual mat4 get_tag( sint32 frame, const std::string& name ) const;
 		sint32 get_max_frames() const;
+		void load_tag_names( std::vector< std::string >& tags );
 		void load_tags( std::vector<mat4>& t, const std::string& tag );
 		void load_positions( std::vector<vec3>& p, sint32 frame =-1 );
@@ -50,4 +52,32 @@
 	};
 
+	class keyframed_mesh
+	{
+	public:
+		typedef std::vector< mat4 > transforms;
+		typedef std::unordered_map< std::string, transforms > tag_map;
+
+		keyframed_mesh( md3_loader* loader );
+		size_t get_frame_count() const { return m_frames; };
+		size_t get_index_count() const { return m_indices.size(); };
+		size_t get_vertex_count() const { return m_texcoords.size(); };
+		const std::vector< vec3 >& get_positions() { return m_positions; }
+		const std::vector< vec3 >& get_normals() { return m_normals; }
+		const std::vector< vec3 >& get_positions() { return m_positions; }
+		const std::vector< vec2 >& get_texcoords() { return m_texcoords; }
+		const std::vector< uint16 >& get_indices() { return m_indices; }
+		const tag_map& get_tag_map() { return m_tags; }
+		const vec3* get_position_data( uint32 frame = 0 ) { return m_positions.data() + m_texcoords.size() * frame; }
+		const vec3* get_normal_data( uint32 frame = 0 ) { return m_normals.data() + m_texcoords.size() * frame; }
+	protected:
+
+		std::size_t           m_frames;
+		tag_map               m_tags;
+		std::vector< vec3 >   m_normals;
+		std::vector< vec3 >   m_positions;
+		std::vector< vec2 >   m_texcoords;
+		std::vector< uint16 > m_indices;
+	};
+
 }
 
Index: trunk/nv/gl/gl_device.hh
===================================================================
--- trunk/nv/gl/gl_device.hh	(revision 152)
+++ trunk/nv/gl/gl_device.hh	(revision 153)
@@ -23,6 +23,6 @@
 		virtual window* create_window( uint16 width, uint16 height );
 		virtual program* create_program( const string& vs_source, const string& fs_source );
-		virtual vertex_buffer* create_vertex_buffer( buffer_hint hint, size_t size, void* source = nullptr );
-		virtual index_buffer* create_index_buffer( buffer_hint hint, size_t size, void* source = nullptr );
+		virtual vertex_buffer* create_vertex_buffer( buffer_hint hint, size_t size, const void* source = nullptr );
+		virtual index_buffer* create_index_buffer( buffer_hint hint, size_t size, const void* source = nullptr );
 		virtual vertex_array* create_vertex_array();
 		virtual image_data* create_image_data( const std::string& filename ); // temporary
Index: trunk/nv/gl/gl_vertex_buffer.hh
===================================================================
--- trunk/nv/gl/gl_vertex_buffer.hh	(revision 152)
+++ trunk/nv/gl/gl_vertex_buffer.hh	(revision 153)
@@ -22,6 +22,6 @@
 	{
 	public:
-		gl_vertex_buffer( buffer_hint hint, size_t size, void* data = nullptr );
-		virtual void update( void* data, size_t offset, size_t size );
+		gl_vertex_buffer( buffer_hint hint, size_t size, const void* data = nullptr );
+		virtual void update( const void* data, size_t offset, size_t size );
 		virtual void bind();
 		virtual void unbind();
@@ -34,6 +34,6 @@
 	{
 	public:
-		gl_index_buffer( buffer_hint hint, size_t size, void* data = nullptr );
-		virtual void update( void* data, size_t offset, size_t size );
+		gl_index_buffer( buffer_hint hint, size_t size, const void* data = nullptr );
+		virtual void update( const void* data, size_t offset, size_t size );
 		virtual void bind();
 		virtual void unbind();
Index: trunk/nv/interface/device.hh
===================================================================
--- trunk/nv/interface/device.hh	(revision 152)
+++ trunk/nv/interface/device.hh	(revision 153)
@@ -30,6 +30,6 @@
 		virtual window* create_window( uint16 width, uint16 height ) = 0;
 		virtual program* create_program( const string& vs_source, const string& fs_source ) = 0;
-		virtual vertex_buffer* create_vertex_buffer( buffer_hint hint, size_t size, void* source = nullptr ) = 0;
-		virtual index_buffer* create_index_buffer( buffer_hint hint, size_t size, void* source = nullptr ) = 0;
+		virtual vertex_buffer* create_vertex_buffer( buffer_hint hint, size_t size, const void* source = nullptr ) = 0;
+		virtual index_buffer* create_index_buffer( buffer_hint hint, size_t size, const void* source = nullptr ) = 0;
 		virtual vertex_array* create_vertex_array() = 0;
 		virtual image_data* create_image_data( const std::string& filename ) = 0; // temporary
Index: trunk/nv/interface/vertex_buffer.hh
===================================================================
--- trunk/nv/interface/vertex_buffer.hh	(revision 152)
+++ trunk/nv/interface/vertex_buffer.hh	(revision 153)
@@ -33,5 +33,5 @@
 	public:
 		buffer( buffer_hint hint, size_t size ) { m_size = size; m_hint = hint; }
-		virtual void update( void* data, size_t offset, size_t size ) = 0;
+		virtual void update( const void* data, size_t offset, size_t size ) = 0;
 		virtual void bind() = 0;
 		virtual void unbind() = 0;
Index: trunk/src/formats/md3_loader.cc
===================================================================
--- trunk/src/formats/md3_loader.cc	(revision 152)
+++ trunk/src/formats/md3_loader.cc	(revision 153)
@@ -351,7 +351,18 @@
 }
 
-sint32 nv::md3_loader::get_max_frames() const
+sint32 md3_loader::get_max_frames() const
 {
 	return ((md3_t*)m_md3)->header.num_frames;
+}
+
+void md3_loader::load_tag_names( std::vector< std::string >& tags )
+{
+	tags.clear();
+	md3_t* md3 = (md3_t*)m_md3;
+	for ( sint32 i = 0; i < md3->header.num_tags; ++i )
+	{
+		const md3_tag_t& rtag = md3->tags[i + md3->header.num_tags];
+		tags.push_back( (char*)(rtag.name) );
+	}
 }
 
@@ -480,2 +491,20 @@
 
 }
+
+keyframed_mesh::keyframed_mesh( md3_loader* loader )
+{
+	loader->load_positions( m_positions );
+	loader->load_normals( m_normals );
+	loader->load_texcoords( m_texcoords );
+	loader->load_indicies( m_indices );
+
+	std::vector< std::string > names;
+	loader->load_tag_names( names );
+	for ( auto& name : names )
+	{
+		loader->load_tags( m_tags[ name ], name );
+	}
+
+	m_frames = loader->get_max_frames();
+}
+
Index: trunk/src/gl/gl_device.cc
===================================================================
--- trunk/src/gl/gl_device.cc	(revision 152)
+++ trunk/src/gl/gl_device.cc	(revision 153)
@@ -53,10 +53,10 @@
 }
 
-vertex_buffer* gl_device::create_vertex_buffer( buffer_hint hint, size_t size, void* source /*= nullptr */ )
+vertex_buffer* gl_device::create_vertex_buffer( buffer_hint hint, size_t size, const void* source /*= nullptr */ )
 {
 	return new gl_vertex_buffer( hint, size, source );
 }
 
-index_buffer* gl_device::create_index_buffer( buffer_hint hint, size_t size, void* source /*= nullptr */ )
+index_buffer* gl_device::create_index_buffer( buffer_hint hint, size_t size, const void* source /*= nullptr */ )
 {
 	return new gl_index_buffer( hint, size, source );
Index: trunk/src/gl/gl_vertex_buffer.cc
===================================================================
--- trunk/src/gl/gl_vertex_buffer.cc	(revision 152)
+++ trunk/src/gl/gl_vertex_buffer.cc	(revision 153)
@@ -10,5 +10,5 @@
 using namespace nv;
 
-gl_vertex_buffer::gl_vertex_buffer( buffer_hint hint, size_t size, void* data ) 
+gl_vertex_buffer::gl_vertex_buffer( buffer_hint hint, size_t size, const void* data ) 
 	: vertex_buffer( hint, size ), m_name()
 {
@@ -18,5 +18,5 @@
 }
 
-void gl_vertex_buffer::update( void* data, size_t offset, size_t size )
+void gl_vertex_buffer::update( const void* data, size_t offset, size_t size )
 {
 	glBufferSubData( GL_ARRAY_BUFFER, (GLintptr)offset, (GLsizeiptr)size, data );
@@ -39,5 +39,5 @@
 }
 
-gl_index_buffer::gl_index_buffer( buffer_hint hint, size_t size, void* data ) 
+gl_index_buffer::gl_index_buffer( buffer_hint hint, size_t size, const void* data ) 
 	: index_buffer( hint, size ), m_name()
 {
@@ -47,5 +47,5 @@
 }
 
-void gl_index_buffer::update( void* data, size_t offset, size_t size )
+void gl_index_buffer::update( const void* data, size_t offset, size_t size )
 {
 	glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, (GLintptr)offset, (GLsizeiptr)size, data );
