Index: /trunk/nv/formats/md3_loader.hh
===================================================================
--- /trunk/nv/formats/md3_loader.hh	(revision 229)
+++ /trunk/nv/formats/md3_loader.hh	(revision 230)
@@ -17,4 +17,5 @@
 #include <unordered_map>
 #include <vector>
+#include <nv/transform.hh>
 #include <nv/gfx/mesh_data.hh>
 #include <nv/interface/mesh_loader.hh>
@@ -26,5 +27,5 @@
 	{
 		std::string name;
-		mat4 transform;
+		transform   trans;
 	};
 
@@ -40,8 +41,8 @@
 
 		virtual const md3_tag* get_tag( const std::string& name ) const;
-		virtual mat4 get_tag( sint32 frame, const std::string& name ) const;
+		virtual transform get_tag( sint32 frame, const std::string& name ) const;
 		size_t 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_tags( std::vector<transform>& t, const std::string& tag );
 		void load_positions( std::vector<vec3>& p, sint32 frame =-1 );
 		void load_normals( std::vector<vec3>& n, sint32 frame =-1 );
Index: /trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- /trunk/nv/gfx/keyframed_mesh.hh	(revision 229)
+++ /trunk/nv/gfx/keyframed_mesh.hh	(revision 230)
@@ -10,4 +10,5 @@
 #include <nv/common.hh>
 #include <nv/interface/context.hh>
+#include <nv/interface/animated_mesh.hh>
 #include <nv/formats/md3_loader.hh>
 
@@ -15,21 +16,37 @@
 {
 
-	class keyframed_mesh
+	class keyframed_animation_entry : public animation_entry
 	{
 	public:
-		keyframed_mesh( context* a_context, mesh_data* a_data, program* a_program );
+		friend class keyframed_mesh;
+
+		keyframed_animation_entry( const std::string& name, uint32 a_start, uint32 a_frames, uint32 a_fps, bool a_loop )
+			: animation_entry( name ), m_start( a_start ), m_frames( a_frames ), m_fps( a_fps ), m_looping( a_loop ) {}
+		virtual uint32 get_frame_rate() const { return m_fps; }
+		virtual uint32 get_frame_count() const { return m_frames; }
+		virtual bool is_looping() const { return m_looping; }
+	protected:
+		uint32 m_start;
+		uint32 m_frames;
+		uint32 m_fps;
+		bool   m_looping;
+	};
+
+
+	class keyframed_mesh : public animated_mesh
+	{
+	public:
+		keyframed_mesh( context* a_context, mesh_data* a_data );
+		virtual size_t get_index_count() const { return m_data->get_index_count(); }
+		virtual void run_animation( animation_entry* a_anim );
 		size_t get_max_frames() const;
-		mat4 get_tag( const std::string& tag ) const;
+		transform get_tag( const std::string& tag ) const;
 		virtual void setup_animation( uint32 start, uint32 count, uint32 fps, bool loop );
 		virtual void set_frame( uint32 frame );
 		virtual void update( uint32 ms );
-		virtual void draw( render_state& rstate );
-		program* get_program() { return m_program; }
+		virtual void update( program* a_program );
 		virtual ~keyframed_mesh();
 	protected:
-		context*      m_context;
 		mesh_data*    m_data;
-		program*      m_program;
-		vertex_array* m_va;
 
 		uint32 m_start_frame;
@@ -49,5 +66,5 @@
 	public:
 		keyframed_mesh_gpu( context* a_context, mesh_data* a_data, program* a_program );
-		void draw( render_state& rstate );
+		void update( uint32 ms );
 	private:
 		int m_loc_next_position;
@@ -61,5 +78,5 @@
 	{
 	public:
-		keyframed_mesh_cpu( context* a_context, mesh_data* a_data, program* a_program );
+		keyframed_mesh_cpu( context* a_context, mesh_data* a_data );
 		void update( uint32 ms );
 	private:
Index: /trunk/nv/gfx/mesh_data.hh
===================================================================
--- /trunk/nv/gfx/mesh_data.hh	(revision 229)
+++ /trunk/nv/gfx/mesh_data.hh	(revision 230)
@@ -11,6 +11,7 @@
 #include <unordered_map>
 #include <vector>
+#include <nv/math.hh>
+#include <nv/transform.hh>
 #include <nv/interface/device.hh>
-#include <nv/math.hh>
 
 namespace nv
@@ -20,5 +21,5 @@
 	public:
 		friend class mesh_data_creator;
-		typedef std::vector< mat4 > transforms;
+		typedef std::vector< transform > transforms;
 		typedef std::unordered_map< std::string, transforms > tag_map;
 
Index: /trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- /trunk/nv/gfx/skeletal_mesh.hh	(revision 229)
+++ /trunk/nv/gfx/skeletal_mesh.hh	(revision 230)
@@ -10,4 +10,5 @@
 #include <nv/common.hh>
 #include <nv/interface/context.hh>
+#include <nv/interface/animated_mesh.hh>
 #include <nv/formats/md5_loader.hh>
 
@@ -15,18 +16,29 @@
 {
 
-	class skeletal_mesh
+	class skeletal_animation_entry : public animation_entry
 	{
 	public:
-		skeletal_mesh( context* a_context, program* a_program, md5_loader* a_loader );
+		friend class skeletal_mesh;
+
+		skeletal_animation_entry( const std::string& name, md5_animation* a_animation, bool a_looping ) 
+			: animation_entry( name ), m_animation( a_animation ), m_looping( a_looping ) {}
+		virtual uint32 get_frame_rate() const { return m_animation->get_frame_rate(); }
+		virtual uint32 get_frame_count() const { return m_animation->get_frame_count(); }
+		virtual bool is_looping() const { return m_looping; }
+	protected:
+		md5_animation* m_animation;
+		bool           m_looping;
+	};
+
+	class skeletal_mesh : public animated_mesh
+	{
+	public:
+		skeletal_mesh( context* a_context, md5_loader* a_loader );
+		virtual size_t get_index_count() const { return m_data->get_index_count(0); }
+		virtual void run_animation( animation_entry* a_anim );
 		virtual void setup_animation( md5_animation* a_anim );
 		virtual void update( uint32 ms );
-		virtual void draw( render_state& rstate );
-		program* get_program() { return m_program; }
 		virtual ~skeletal_mesh();
 	protected:
-		context*      m_context;
-		program*      m_program;
-		vertex_array* m_va;
-
 		vertex_buffer* m_vb_position;
 		vertex_buffer* m_vb_normal;
@@ -35,14 +47,4 @@
 		md5_loader*    m_data;
 		md5_animation* m_animation;
-
-		uint32 m_start_frame;
-		uint32 m_stop_frame;
-		uint32 m_last_frame;
-		uint32 m_next_frame;
-		uint32 m_time;
-		uint32 m_fps;
-		f32    m_interpolation;
-		bool   m_looping;
-		bool   m_active;
 	};
 
Index: /trunk/nv/interface/animated_mesh.hh
===================================================================
--- /trunk/nv/interface/animated_mesh.hh	(revision 230)
+++ /trunk/nv/interface/animated_mesh.hh	(revision 230)
@@ -0,0 +1,57 @@
+// Copyright (C) 2014 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+/**
+ * @file animated_mesh.hh
+ * @author Kornel Kisielewicz
+ * @brief animated_mesh
+ */
+
+#ifndef NV_ANIMATED_MESH_HH
+#define NV_ANIMATED_MESH_HH
+
+#include <nv/common.hh>
+#include <nv/math.hh>
+#include <nv/transform.hh>
+#include <nv/interface/context.hh>
+
+namespace nv
+{
+
+	class animation_entry
+	{
+	public:
+		animation_entry( const std::string& name ) : m_name( name ) {}
+		virtual const std::string& get_name() const { return m_name; }
+		virtual uint32 get_frame_rate() const = 0;
+		virtual uint32 get_frame_count() const = 0;
+		virtual bool is_looping() const = 0;
+		virtual ~animation_entry() {}
+	protected:
+		std::string m_name;
+	};
+
+	class animated_mesh
+	{
+	public:
+		animated_mesh( context* a_context ) : m_context( a_context ), m_va( nullptr ) {}
+		virtual void run_animation( animation_entry* ) {}
+		virtual void update( uint32 ) {}
+		virtual void update( program* ) {}
+		virtual transform get_tag( const std::string& ) const { return transform(); }
+		virtual nv::vertex_array* get_vertex_array() const { return m_va; }
+		virtual size_t get_index_count() const { return 0; }
+		virtual nv::primitive get_primitive_type() { return nv::TRIANGLES; }
+		virtual ~animated_mesh() {}
+	protected:
+		context*      m_context;
+		vertex_array* m_va;
+	};
+
+
+}
+
+#endif // NV_ANIMATED_MESH_HH
Index: /trunk/nv/interface/camera.hh
===================================================================
--- /trunk/nv/interface/camera.hh	(revision 230)
+++ /trunk/nv/interface/camera.hh	(revision 230)
@@ -0,0 +1,48 @@
+// Copyright (C) 2014 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+/**
+ * @file camera.hh
+ * @author Kornel Kisielewicz epyon@chaosforge.org
+ * @brief Camera interface class
+ */
+
+#ifndef NV_CAMERA_HH
+#define NV_CAMERA_HH
+
+#include <nv/common.hh>
+#include <nv/math.hh>
+
+namespace nv
+{
+	class camera
+	{
+	public:
+		camera() {}
+
+		void set_lookat( const vec3& eye, const vec3& focus, const vec3& up )
+		{
+			m_view  = glm::lookAt( eye, focus, up );
+		}
+
+		void set_perspective( f32 fov, f32 aspect, f32 near, f32 far )
+		{
+			m_projection = glm::perspective( fov, aspect, near, far );
+		}
+		const mat4& get_projection() const 
+		{
+			return m_projection;
+		}
+		const mat4& get_view() const 
+		{
+			return m_view;
+		}
+	private:
+		mat4 m_projection;
+		mat4 m_view;
+	};
+}
+
+#endif // NV_CAMERA_HH
Index: /trunk/nv/interface/font.hh
===================================================================
--- /trunk/nv/interface/font.hh	(revision 230)
+++ /trunk/nv/interface/font.hh	(revision 230)
@@ -0,0 +1,41 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#ifndef NV_GL_FONT_HH
+#define NV_GL_FONT_HH
+
+#include <nv/common.hh>
+#include <nv/math.hh>
+#include <unordered_map>
+
+namespace nv
+{
+	struct gylph
+	{
+	    uint16 charcode;
+		ivec2 size;
+	    ivec2 offset;
+	    vec2 advance;
+	    vec2 tl;
+	    vec2 br;
+		std::unordered_map< uint16, float > kerning;
+
+		gylph();
+		float get_kerning( const uint16 cc );
+	};
+
+	class font
+	{
+	public:
+		const gylph* get_gylph( uint16 charcode ) const;
+	private:
+		std::unordered_map< uint16, gylph > m_gylphs;
+	}
+
+
+}
+
+#endif // NV_GL_FONT_HH
Index: /trunk/nv/interface/scene_node.hh
===================================================================
--- /trunk/nv/interface/scene_node.hh	(revision 230)
+++ /trunk/nv/interface/scene_node.hh	(revision 230)
@@ -0,0 +1,85 @@
+// Copyright (C) 2014 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+/**
+ * @file scene_node.hh
+ * @author Kornel Kisielewicz epyon@chaosforge.org
+ * @brief Scene node class
+ */
+
+#ifndef NV_SCENE_NODE_HH
+#define NV_SCENE_NODE_HH
+
+#include <nv/common.hh>
+#include <vector>
+#include <nv/transform.hh>
+#include <nv/math.hh>
+#include <nv/interface/render_state.hh>
+
+namespace nv
+{
+
+	class scene_node
+	{
+	public:
+		typedef std::vector< scene_node* > list;
+		
+		scene_node() {}
+
+		virtual void update( uint32 ms )
+		{
+			for ( auto& child : m_children )
+			{
+				child->update( ms );
+			}
+		}
+
+		virtual void attach( scene_node* child )
+		{
+			m_children.push_back( child );
+		}
+
+		virtual void set_transform( const transform& t )
+		{
+			m_transform = t;
+		}
+
+		virtual const transform& get_transform() const
+		{
+			return m_transform;
+		}
+
+		virtual mat4 extract_transform_matrix() const
+		{
+			return get_transform().extract();
+		}
+
+		const vec3& get_position() const 
+		{ 
+			return m_transform.get_position(); 
+		}
+
+		const quat& get_orientation() const 
+		{ 
+			return m_transform.get_orientation(); 
+		}
+
+
+		list::const_iterator begin() { return m_children.cbegin(); }
+		list::const_iterator end()   { return m_children.cend(); }
+
+		virtual ~scene_node()
+		{
+			for ( auto& child : m_children ) delete child;
+		}
+
+	protected:
+		transform m_transform;
+		list      m_children;
+	};
+
+}
+
+#endif // NV_SCENE_NODE_HH
Index: /trunk/nv/transform.hh
===================================================================
--- /trunk/nv/transform.hh	(revision 230)
+++ /trunk/nv/transform.hh	(revision 230)
@@ -0,0 +1,73 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#ifndef NV_TRANSFORM_HH
+#define NV_TRANSFORM_HH
+
+#include <nv/common.hh>
+#include <nv/math.hh>
+
+namespace nv
+{
+
+	class transform
+	{
+	public:
+		transform() {}
+		explicit transform( const vec3& a_position ) : m_position( a_position ) {}
+		explicit transform( const quat& a_orientation ) : m_orientation( a_orientation ) {}
+		transform( const vec3& a_position, const quat& a_orientation ) : m_position( a_position ), m_orientation( a_orientation ) {}
+
+		void set_position( const vec3& a_position ) { m_position = a_position; }
+		void set_orientation( const quat& a_orientation ) { m_orientation = a_orientation; }
+		void set_orientation( const vec3& axis, float angle )
+		{
+			m_orientation = glm::angleAxis( angle, axis );
+		}
+
+		const vec3& get_position() const { return m_position; }
+		const quat& get_orientation() const { return m_orientation; }
+
+	public:
+		void move( const vec3& heading, float distance )
+		{
+			m_position += glm::normalize( heading ) * distance;
+		}
+		void translate( const vec3& absolute )
+		{
+			m_position += absolute;
+		}
+		void rotate( const vec3& axis, f32 angle )
+		{
+			quat temp( angle, axis );
+			m_orientation = temp * m_orientation;
+		}
+		void set( const mat4& from )
+		{
+			m_orientation = quat( from );
+			m_position    = vec3( from[3] );
+		}
+		mat4 extract() const
+		{
+			mat4 result = glm::mat4_cast( m_orientation );
+			result[3] = vec4( m_position, 1.0f );
+			return result;
+		}
+	private:
+		vec3 m_position;
+		quat m_orientation;
+	};
+
+	inline transform interpolate( const transform& a, const transform& b, f32 value )
+	{
+		return transform( 
+			glm::mix  ( a.get_position(), b.get_position(), value ), 
+			glm::slerp( a.get_orientation(), b.get_orientation(), value ) 
+		);
+	}
+}
+
+#endif // NV_TRANSFORM_HH
Index: /trunk/src/formats/md3_loader.cc
===================================================================
--- /trunk/src/formats/md3_loader.cc	(revision 229)
+++ /trunk/src/formats/md3_loader.cc	(revision 230)
@@ -328,5 +328,5 @@
 */
 
-void nv::md3_loader::load_tags( std::vector<mat4>& t, const std::string& tag )
+void nv::md3_loader::load_tags( std::vector<transform>& t, const std::string& tag )
 {
 	md3_t* md3 = (md3_t*)m_md3;
@@ -340,9 +340,9 @@
 			if (rname == tag)
 			{
-				vec4 axisx     = vec4( md3_vec3( rtag.axis[0] ), 0.0 );
-				vec4 axisz     = vec4( md3_vec3( rtag.axis[1] ), 0.0 );
-				vec4 axisy     = vec4( md3_vec3( rtag.axis[2] ), 0.0 );
-				vec4 origin    = vec4( md3_vec3( rtag.origin ),  1.0 );
-				t.emplace_back( axisx, axisy, axisz, origin );
+				vec3 axisx  ( md3_vec3( rtag.axis[0] ) );
+				vec3 axisz  ( md3_vec3( rtag.axis[1] ) );
+				vec3 axisy  ( md3_vec3( rtag.axis[2] ) );
+				vec3 origin ( md3_vec3( rtag.origin )  );
+				t.emplace_back( origin, quat( mat3( axisx, axisy, axisz ) ) );
 			}
 		}
@@ -406,5 +406,5 @@
 }
 
-mat4 md3_loader::get_tag( sint32 frame, const std::string& name ) const
+transform md3_loader::get_tag( sint32 frame, const std::string& name ) const
 {
 	md3_t* md3 = (md3_t*)m_md3;
@@ -415,12 +415,12 @@
 		if (rname == name)
 		{
-			vec4 axisx     = vec4( md3_vec3( rtag.axis[0] ), 0.0 );
-			vec4 axisz     = vec4( md3_vec3( rtag.axis[1] ), 0.0 );
-			vec4 axisy     = vec4( md3_vec3( rtag.axis[2] ), 0.0 );
-			vec4 origin    = vec4( md3_vec3( rtag.origin ),  1.0 );
-			return glm::mat4( axisx, axisy, axisz, origin );
-		}
-	}
-	return glm::mat4();
+			vec3 axisx ( md3_vec3( rtag.axis[0] ) );
+			vec3 axisz ( md3_vec3( rtag.axis[1] ) );
+			vec3 axisy ( md3_vec3( rtag.axis[2] ) );
+			vec3 origin( md3_vec3( rtag.origin ) );
+			return transform( origin, quat( mat3( axisx, axisy, axisz ) ) );
+		}
+	}
+	return transform();
 }
 
Index: /trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- /trunk/src/gfx/keyframed_mesh.cc	(revision 229)
+++ /trunk/src/gfx/keyframed_mesh.cc	(revision 230)
@@ -15,9 +15,7 @@
 using namespace nv;
 
-keyframed_mesh::keyframed_mesh( context* a_context, mesh_data* a_data, program* a_program )
-	: m_context( a_context )
+keyframed_mesh::keyframed_mesh( context* a_context, mesh_data* a_data )
+	: animated_mesh( a_context )
 	, m_data( a_data )
-	, m_program( a_program )
-	, m_va( nullptr )
 	, m_start_frame( false )
 	, m_stop_frame( false )
@@ -38,8 +36,8 @@
 }
 
-mat4 keyframed_mesh::get_tag( const std::string& tag ) const
-{
-	const std::vector< nv::mat4 >& transforms = m_data->get_tag_map().at( tag );
-	return glm::interpolate( transforms[ m_last_frame ], transforms[ m_next_frame ], m_interpolation );
+transform keyframed_mesh::get_tag( const std::string& tag ) const
+{
+	const std::vector< transform >& transforms = m_data->get_tag_map().at( tag );
+	return interpolate( transforms[ m_last_frame ], transforms[ m_next_frame ], m_interpolation );
 }
 
@@ -101,8 +99,7 @@
 }
 
-void nv::keyframed_mesh::draw( render_state& rstate )
-{
-	m_program->set_opt_uniform( "nv_interpolate", m_interpolation );
-	m_context->draw( nv::TRIANGLES, rstate, m_program, m_va, m_data->get_index_count() );
+void nv::keyframed_mesh::update( program* a_program )
+{
+	a_program->set_opt_uniform( "nv_interpolate", m_interpolation );
 }
 
@@ -112,6 +109,12 @@
 }
 
+void nv::keyframed_mesh::run_animation( animation_entry* a_anim )
+{
+	keyframed_animation_entry * anim = down_cast<keyframed_animation_entry>(a_anim);
+	setup_animation( anim->m_start, anim->m_frames, anim->m_fps, anim->m_looping );
+}
+
 keyframed_mesh_gpu::keyframed_mesh_gpu( context* a_context, mesh_data* a_data, program* a_program )
-	: keyframed_mesh( a_context, a_data, a_program )
+	: keyframed_mesh( a_context, a_data )
 	, m_loc_next_position( 0 )
 	, m_loc_next_normal( 0 )
@@ -120,6 +123,6 @@
 {
 	nv::vertex_buffer* vb;
-	m_loc_next_position = m_program->get_attribute( "nv_next_position" )->get_location();
-	m_loc_next_normal   = m_program->get_attribute( "nv_next_normal" )->get_location();
+	m_loc_next_position = a_program->get_attribute( "nv_next_position" )->get_location();
+	m_loc_next_normal   = a_program->get_attribute( "nv_next_normal" )->get_location();
 
 	vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec3 ) * m_data->get_frame_count(), (void*)m_data->get_positions().data() );
@@ -137,6 +140,8 @@
 }
 
-void nv::keyframed_mesh_gpu::draw( render_state& rstate )
-{
+void nv::keyframed_mesh_gpu::update( uint32 ms )
+{
+	keyframed_mesh::update( ms );
+
 	size_t vtx_count = m_data->get_vertex_count();
 	if ( m_gpu_last_frame != m_last_frame )
@@ -152,10 +157,9 @@
 		m_gpu_next_frame = m_next_frame;
 	}
-	keyframed_mesh::draw( rstate );
-}
-
-
-nv::keyframed_mesh_cpu::keyframed_mesh_cpu( context* a_context, mesh_data* a_data, program* a_program )
-	: keyframed_mesh( a_context, a_data, a_program )
+}
+
+
+nv::keyframed_mesh_cpu::keyframed_mesh_cpu( context* a_context, mesh_data* a_data )
+	: keyframed_mesh( a_context, a_data )
 {
 	m_vb_position = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec3 ), (void*)m_data->get_position_frame(0) );
Index: /trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- /trunk/src/gfx/skeletal_mesh.cc	(revision 229)
+++ /trunk/src/gfx/skeletal_mesh.cc	(revision 230)
@@ -11,8 +11,6 @@
 
 
-nv::skeletal_mesh::skeletal_mesh( context* a_context, program* a_program, md5_loader* a_loader )
-	: m_context( a_context )
-	, m_program( a_program )
-	, m_va( nullptr )
+nv::skeletal_mesh::skeletal_mesh( context* a_context, md5_loader* a_loader )
+	: animated_mesh( a_context )
 	, m_data( a_loader )
 	, m_animation( nullptr )
@@ -38,4 +36,5 @@
 	if ( m_animation ) m_animation->reset_animation();
 	m_animation = a_anim;
+	// TODO : INSTANCE!
 	m_animation->reset_animation();
 }
@@ -64,11 +63,13 @@
 }
 
-void nv::skeletal_mesh::draw( render_state& rstate )
-{
-	m_context->draw( nv::TRIANGLES, rstate, m_program, m_va, m_data->get_index_count(0) );
-}
-
 nv::skeletal_mesh::~skeletal_mesh()
 {
 	delete m_va;
 }
+
+void nv::skeletal_mesh::run_animation( animation_entry* a_anim )
+{
+	skeletal_animation_entry * anim = down_cast<skeletal_animation_entry>(a_anim);
+	// TODO : INSTANCE!
+	setup_animation( anim->m_animation );
+}
