Index: /trunk/nv/formats/obj_loader.hh
===================================================================
--- /trunk/nv/formats/obj_loader.hh	(revision 237)
+++ /trunk/nv/formats/obj_loader.hh	(revision 238)
@@ -16,4 +16,5 @@
 #include <nv/common.hh>
 #include <nv/interface/mesh_loader.hh>
+#include <nv/interface/mesh_data.hh>
 
 namespace nv 
@@ -34,4 +35,19 @@
 	};
 
+	class wavefront_loader 
+	{
+	public:
+		wavefront_loader( bool normals = true, bool tangents = false );
+		virtual bool load( stream& source );
+		mesh_data* release_mesh_data();
+		~wavefront_loader();
+	private:
+		vertex_descriptor m_descriptor;
+		bool              m_normals;
+		bool              m_tangents;
+		mesh_data*        m_mesh;
+	};
+
+
 }
 
Index: /trunk/nv/gfx/mesh_data.hh
===================================================================
--- /trunk/nv/gfx/mesh_data.hh	(revision 237)
+++ /trunk/nv/gfx/mesh_data.hh	(revision 238)
@@ -5,6 +5,6 @@
 // For conditions of distribution and use, see copyright notice in nv.hh
 
-#ifndef NV_MESH_DATA_HH
-#define NV_MESH_DATA_HH
+#ifndef NV_MESH_DATA_OLD_HH
+#define NV_MESH_DATA_OLD_HH
 
 #include <nv/common.hh>
@@ -95,3 +95,3 @@
 }
 
-#endif // NV_MESH_DATA_HH
+#endif // NV_MESH_DATA_OLD_HH
Index: /trunk/nv/interface/device.hh
===================================================================
--- /trunk/nv/interface/device.hh	(revision 237)
+++ /trunk/nv/interface/device.hh	(revision 238)
@@ -16,4 +16,5 @@
 #include <nv/string.hh>
 #include <nv/interface/mesh.hh>
+#include <nv/interface/mesh_data.hh>
 #include <nv/interface/vertex_buffer.hh>
 #include <nv/interface/texture2d.hh>
@@ -112,4 +113,29 @@
 		}
 
+		// TODO: HINTS ARE DIFFERENT!
+		vertex_array* create_vertex_array( const mesh_data* data, buffer_hint hint )
+		{
+			vertex_array*  va = create_vertex_array();
+			for ( uint32 ch = 0; ch < data->get_channel_data().size(); ++ch )
+			{
+				const mesh_raw_channel* channel = data->get_channel_data()[ch];
+				vertex_buffer* vb = create_vertex_buffer( hint, channel->size, channel->data );
+				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);
+					va->add_vertex_buffer( slot.vslot, vb, info.base , info.elements, slot.offset, channel->desc.size, false );
+				}
+			}
+			if ( data->get_index_channel() != nullptr )
+			{
+				const mesh_raw_index_channel* index = data->get_index_channel();
+				index_buffer* ib = create_index_buffer( hint, index->size, index->data );
+				va->set_index_buffer( ib, index->etype, true );
+			}
+			return va;
+		}
+
+
 		virtual ~device() {}
 	};
Index: /trunk/nv/interface/mesh_data.hh
===================================================================
--- /trunk/nv/interface/mesh_data.hh	(revision 238)
+++ /trunk/nv/interface/mesh_data.hh	(revision 238)
@@ -0,0 +1,90 @@
+// 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_MESH_DATA_HH
+#define NV_MESH_DATA_HH
+
+#include <nv/common.hh>
+#include <nv/math.hh>
+#include <nv/interface/context.hh>
+#include <nv/interface/vertex.hh>
+
+namespace nv
+{
+
+	struct mesh_raw_channel
+	{
+		vertex_descriptor desc;
+		uint8*            data;
+		uint32            size;
+		uint32            count;
+
+		mesh_raw_channel() : data( nullptr ), size( 0 ) {}
+		~mesh_raw_channel() 
+		{
+			if ( data != nullptr ) delete[] data;
+		}
+	};
+
+	struct mesh_raw_index_channel
+	{
+		datatype          etype;
+		uint8*            data;
+		uint32            size;
+		uint32            count;
+
+		mesh_raw_index_channel() : etype( NONE ), data( nullptr ), size( 0 ) {}
+		~mesh_raw_index_channel() 
+		{
+			if ( data != nullptr ) delete[] data;
+		}
+	};
+
+	struct mesh_raw_data
+	{
+	};
+
+	class mesh_data
+	{
+	public:
+		mesh_data() : m_index_channel( nullptr ) {}
+		void add_channel( mesh_raw_channel* channel ) { m_channels.push_back( channel ); }
+		void set_index_channel( mesh_raw_index_channel* channel )
+		{
+			if ( m_index_channel ) delete m_index_channel;
+			m_index_channel = channel;
+		}
+
+		const std::vector< mesh_raw_channel* >& get_channel_data() const { return m_channels; }
+		const mesh_raw_index_channel*           get_index_channel() const { return m_index_channel; }
+
+		size_t get_count() const 
+		{
+			if ( m_index_channel ) return m_index_channel->count;
+			if ( m_channels.size() > 0 ) return m_channels[0]->count;
+			return 0;
+		}
+
+		size_t get_count( size_t channel ) const 
+		{
+			if ( m_channels.size() > channel ) return m_channels[channel]->count;
+			return 0;
+		}
+
+		~mesh_data()
+		{
+			for ( auto channel : m_channels ) delete channel;
+			if ( m_index_channel ) delete m_index_channel;
+		}
+	private:
+		std::vector< mesh_raw_channel* > m_channels;
+		mesh_raw_index_channel*          m_index_channel;
+	};
+
+
+}
+
+#endif // NV_MESH_DATA_HH
Index: /trunk/nv/interface/vertex.hh
===================================================================
--- /trunk/nv/interface/vertex.hh	(revision 237)
+++ /trunk/nv/interface/vertex.hh	(revision 238)
@@ -273,5 +273,5 @@
 				slots[ count ].vslot  = SLOT;
 				slots[ count ].offset = slot_info::offset;
-				count++
+				count++;
 			}
 		}
Index: /trunk/nv/math.hh
===================================================================
--- /trunk/nv/math.hh	(revision 237)
+++ /trunk/nv/math.hh	(revision 238)
@@ -104,5 +104,41 @@
 		BYTE_VECTOR_3,
 		BYTE_VECTOR_4,
-	};
+		DATATYPE_COUNT,
+	};
+
+	struct datatype_info
+	{
+		size_t   size;
+		datatype base;
+		size_t   elements;
+	};
+
+	inline const datatype_info& get_datatype_info( datatype dt )
+	{
+		static datatype_info info[ DATATYPE_COUNT ] = {
+			{ 0, NONE, 0 },   // NONE  
+			{ 4, INT, 1 },    // INT,
+			{ 1, BYTE, 1 },   // BYTE,
+			{ 2, SHORT, 1 },  // SHORT,
+			{ 4, UINT, 1 },   // UINT,
+			{ 1, UBYTE, 1 },  // UBYTE,
+			{ 2, USHORT, 1 }, // USHORT,
+			{ 4, FLOAT, 1 },  // FLOAT
+			{ 4 * 2,  FLOAT, 2 },  // FLOAT_VECTOR_2,
+			{ 4 * 3,  FLOAT, 3 },  // FLOAT_VECTOR_3,
+			{ 4 * 4,  FLOAT, 4 },  // FLOAT_VECTOR_4,
+			{ 4 * 4,  FLOAT, 4 },  // FLOAT_MATRIX_2,
+			{ 4 * 9,  FLOAT, 9 },  // FLOAT_MATRIX_3,
+			{ 4 * 16, FLOAT, 16 }, // FLOAT_MATRIX_4,
+			{ 4 * 2,  INT, 2 },  // INT_VECTOR_2,
+			{ 4 * 3,  INT, 3 },  // INT_VECTOR_3,
+			{ 4 * 4,  INT, 4 },  // INT_VECTOR_4,
+			// unsupported gl conversion, remove?
+			{ 1 * 2,  BYTE, 2 },  // BYTE_VECTOR_2,
+			{ 1 * 3,  BYTE, 3 },  // BYTE_VECTOR_3,
+			{ 1 * 4,  BYTE, 4 },  // BYTE_VECTOR_4,
+		};
+		return info[dt];
+	}
 
 	template < datatype EnumType > struct enum_to_type {};
@@ -181,4 +217,5 @@
 
 
+
 } // namespace nv
 
Index: /trunk/src/formats/obj_loader.cc
===================================================================
--- /trunk/src/formats/obj_loader.cc	(revision 237)
+++ /trunk/src/formats/obj_loader.cc	(revision 238)
@@ -11,4 +11,35 @@
 using namespace nv;
 
+struct obj_vertex_vt
+{
+	vec3 position;
+	vec2 texcoord;
+
+	obj_vertex_vt( vec3 a_position, vec2 a_texcoord ) 
+		: position( a_position ), texcoord( a_texcoord ) {}
+};
+
+struct obj_vertex_vtn
+{
+	vec3 position;
+	vec2 texcoord;
+	vec3 normal;
+
+	obj_vertex_vtn( vec3 a_position, vec2 a_texcoord, vec3 a_normal ) 
+		: position( a_position ), texcoord( a_texcoord ), normal( a_normal ) {}
+};
+
+
+struct obj_vertex_vtnt
+{
+	vec3 position;
+	vec2 texcoord;
+	vec3 normal;
+	vec4 tangent;
+
+	obj_vertex_vtnt( vec3 a_position, vec2 a_texcoord, vec3 a_normal ) 
+		: position( a_position ), texcoord( a_texcoord ), normal( a_normal ) {}
+};
+
 struct obj_reader
 {
@@ -24,6 +55,9 @@
 	obj_reader();
 	bool read_stream( std::istream& stream );
-
-	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count ) = 0;
+	virtual size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count ) = 0;
+	virtual size_t raw_size() { return 0; }
+	virtual const uint8* raw_pointer() { return nullptr; }
+	virtual void calculate_tangents() {}
+
 	virtual ~obj_reader(){}
 };
@@ -120,5 +154,5 @@
 {
 	mesh_obj_reader( mesh_data_creator* m ) : m_mesh( m ) {}
-	virtual std::size_t add_face( uint32* v, uint32* t, uint32* n, size_t count );
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count );
 	virtual void calculate_tangents();
 
@@ -238,8 +272,5 @@
 bool nv::obj_loader::load( stream& source )
 {
-	if ( m_mesh != nullptr )
-	{
-		delete m_mesh;
-	}
+	if ( m_mesh != nullptr ) delete m_mesh;
 	mesh_data_creator creator;
 	mesh_obj_reader reader( &creator );
@@ -254,2 +285,207 @@
 	return true;
 }
+
+struct mesh_data_reader_vt : public obj_reader
+{
+	mesh_data_reader_vt()  {}
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32*, size_t count )
+	{
+		if ( count < 3 ) return 0; // TODO : report error?
+		// TODO : support if normals not present;
+		std::size_t result = 0;
+		// Simple triangulation - obj's shouldn't have more than quads anyway
+		for ( size_t i = 2; i < count; ++i )
+		{
+			result++;
+			m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ] );
+			m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ] );
+			m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ] );
+		}
+		return result;
+	}
+	std::vector< obj_vertex_vt > m_data;
+	virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vt ); }
+	virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
+};
+
+struct mesh_data_reader_vtn : public obj_reader
+{
+	mesh_data_reader_vtn()  {}
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
+	{
+		if ( count < 3 ) return 0; // TODO : report error?
+		// TODO : support if normals not present;
+		std::size_t result = 0;
+		// Simple triangulation - obj's shouldn't have more than quads anyway
+		for ( size_t i = 2; i < count; ++i )
+		{
+			result++;
+			m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
+			m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
+			m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
+		}
+		return result;
+	}
+	std::vector< obj_vertex_vtn > m_data;
+	virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vtn ); }
+	virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
+};
+
+struct mesh_data_reader_vtnt : public obj_reader
+{
+	mesh_data_reader_vtnt()  {}
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
+	{
+		if ( count < 3 ) return 0; // TODO : report error?
+		// TODO : support if normals not present;
+		std::size_t result = 0;
+		// Simple triangulation - obj's shouldn't have more than quads anyway
+		for ( size_t i = 2; i < count; ++i )
+		{
+			result++;
+			m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
+			m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
+			m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
+		}
+		return result;
+	}
+	std::vector< obj_vertex_vtnt > m_data;
+	virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vtnt ); }
+	virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
+
+	// based on http://www.terathon.com/code/tangent.html
+	void calculate_tangents()
+	{
+		//		const std::vector< vec3 >& vp = m_mesh->get_positions();
+		//		const std::vector< vec2 >& vt = m_mesh->get_texcoords();
+		//		const std::vector< vec3 >& vn = m_mesh->get_normals();
+		//		std::vector< vec3 >& tg = m_mesh->get_tangents();
+
+		size_t count  = m_data.size();
+		size_t tcount = count / 3;
+
+		std::vector< vec3 > tan1( count );
+		std::vector< vec3 > tan2( count );
+
+		for (size_t a = 0; a < tcount; ++a )
+		{
+			size_t i1 = a * 3;
+			size_t i2 = a * 3 + 1;
+			size_t i3 = a * 3 + 2;
+			obj_vertex_vtnt& vtx1 = m_data[ i1 ];
+			obj_vertex_vtnt& vtx2 = m_data[ i2 ];
+			obj_vertex_vtnt& vtx3 = m_data[ i3 ];
+
+			// TODO: simplify
+			vec3 xyz1 = vtx2.position - vtx1.position;
+			vec3 xyz2 = vtx3.position - vtx1.position;
+			//vec2 st1  = w2 - w1;
+			//vec2 st2  = w3 - w1;
+
+			float s1 = vtx2.texcoord.x - vtx1.texcoord.x;
+			float t1 = vtx2.texcoord.y - vtx1.texcoord.y;
+			float s2 = vtx3.texcoord.x - vtx1.texcoord.x;
+			float t2 = vtx3.texcoord.y - vtx1.texcoord.y;
+
+			float stst = s1 * t2 - s2 * t1;
+			float r = 0.0f;
+			if (stst > 0.0f || stst < 0.0f) r = 1.0f / stst;
+
+			vec3 sdir = ( t2 * xyz1 - t1 * xyz2 ) * r;
+			vec3 tdir = ( s1 * xyz2 - s2 * xyz1 ) * r;
+
+			// the += below obviously doesn't make sense in this case, but I'll
+			// leave it here for when I move to indices
+			tan1[i1] += sdir;
+			tan1[i2] += sdir;
+			tan1[i3] += sdir;
+
+			// tan2 not needed anymore??
+			tan2[i1] += tdir;
+			tan2[i2] += tdir;
+			tan2[i3] += tdir;
+		}
+
+		for (std::size_t a = 0; a < count; ++a )
+		{
+			const vec3& n = m_data[a].normal;
+			const vec3& t = tan1[a];
+			if ( ! (t.x == 0.0f && t.y == 0.0f && t.z == 0.0f) )
+			{
+				m_data[a].tangent    = vec4( glm::normalize(t - n * glm::dot( n, t )), 0.0f ); 
+				m_data[a].tangent[3] = (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
+			}
+		}
+
+	}
+
+
+};
+
+nv::wavefront_loader::wavefront_loader( bool normals /*= true*/, bool tangents /*= false */ )
+	: m_normals( normals ), m_tangents( tangents ), m_mesh( nullptr )
+{
+	if ( normals )
+	{
+		if ( tangents )
+			m_descriptor.initialize<obj_vertex_vtnt>();
+		else
+			m_descriptor.initialize<obj_vertex_vtn>();
+	}
+	else
+		m_descriptor.initialize<obj_vertex_vt>();
+}
+
+bool nv::wavefront_loader::load( stream& source )
+{
+	if ( m_mesh ) delete m_mesh;
+	
+	obj_reader* reader = nullptr;
+	if ( m_normals )
+	{
+		if ( m_tangents )
+			reader = new mesh_data_reader_vtnt();
+		else
+			reader = new mesh_data_reader_vtn();
+	}
+	else
+		reader = new mesh_data_reader_vt();
+	std_stream sstream( &source );
+	reader->read_stream( sstream );
+
+	if ( m_tangents )
+	{
+		reader->calculate_tangents();
+	}
+	
+
+	mesh_raw_channel* channel = new mesh_raw_channel();
+	nv::uint8* data = nullptr;
+	if ( reader->raw_size() > 0 ) 
+	{
+		data = new uint8[ reader->raw_size() ];
+		std::copy_n( reader->raw_pointer(), reader->raw_size(), data );
+	}
+	channel->data  = data;
+	channel->desc  = m_descriptor;
+	channel->count = reader->size * 3;
+	channel->size  = reader->raw_size();
+	delete reader;
+
+	m_mesh = new mesh_data();
+	m_mesh->add_channel( channel );
+	return true;
+
+}
+
+mesh_data* nv::wavefront_loader::release_mesh_data()
+{
+	mesh_data* result = m_mesh;
+	m_mesh = nullptr;
+	return result;
+}
+
+nv::wavefront_loader::~wavefront_loader()
+{
+	if ( m_mesh ) delete m_mesh;
+}
Index: /trunk/tests/objload_test/objload_test.cc
===================================================================
--- /trunk/tests/objload_test/objload_test.cc	(revision 237)
+++ /trunk/tests/objload_test/objload_test.cc	(revision 238)
@@ -32,6 +32,5 @@
 	nv::vertex_array* m_va;
 	nv::program*      m_program;
-	nv::mesh_data_old* m_mesh;
-	nv::uint32        m_count;
+	nv::mesh_data*    m_mesh;
 };
 
@@ -64,8 +63,15 @@
 	nv::c_file_system fs;
 	nv::stream* mesh_file = fs.open( "mesh.obj" );
-	nv::mesh_loader* loader = new nv::obj_loader();
+
+// 		nv::mesh_loader* loader = new nv::obj_loader();
+// 		loader->load( *mesh_file );
+// 		nv::mesh_data_old* mesh = loader->release_mesh_data();
+// 		m_count = loader->get_size();
+// 		delete mesh_file;
+// 		delete loader;
+
+	nv::wavefront_loader* loader = new nv::wavefront_loader();
 	loader->load( *mesh_file );
 	m_mesh = loader->release_mesh_data();
-	m_count = loader->get_size();
 	delete mesh_file;
 	delete loader;
@@ -73,12 +79,7 @@
 	m_program = m_device->create_program( nv::slurp( "obj.vert" ), nv::slurp( "obj.frag" ) );
 
-	nv::vertex_buffer* vb = nullptr;
-	m_va = m_device->create_vertex_array();
-	vb = m_device->create_vertex_buffer( nv::STATIC_DRAW, m_mesh->get_vertex_count() * sizeof( nv::vec3 ), m_mesh->get_positions().data() );
-	m_va->add_vertex_buffer( nv::slot::POSITION, vb, nv::FLOAT, 3 );
-	vb = m_device->create_vertex_buffer( nv::STATIC_DRAW, m_mesh->get_vertex_count() * sizeof( nv::vec3 ), m_mesh->get_normals().data() );
-	m_va->add_vertex_buffer( nv::slot::NORMAL, vb, nv::FLOAT, 3 );
-	vb = m_device->create_vertex_buffer( nv::STATIC_DRAW, m_mesh->get_vertex_count() * sizeof( nv::vec2 ), m_mesh->get_texcoords().data() );
-	m_va->add_vertex_buffer( nv::slot::TEXCOORD, vb, nv::FLOAT, 2 );
+//	nv::vertex_buffer* vb = nullptr;
+	m_va = m_device->create_vertex_array( m_mesh, nv::STATIC_DRAW );
+
 	return true;
 }
@@ -113,5 +114,5 @@
 		m_program->set_uniform( "diffuse", 0 );
 		m_program->set_uniform( "specular", 1 );
-		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_program, m_va, m_count * 3 );
+		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_program, m_va, m_mesh->get_count() );
 		m_window->swap_buffers();
 
@@ -147,6 +148,6 @@
 application::~application()
 {
+	delete m_mesh;
 	delete m_program;
-	delete m_mesh;
 	delete m_diffuse;
 	delete m_specular;
Index: /trunk/tests/render_test/box.frag
===================================================================
--- /trunk/tests/render_test/box.frag	(revision 237)
+++ /trunk/tests/render_test/box.frag	(revision 238)
@@ -4,5 +4,5 @@
 varying vec3 f_material;
 varying float f_diffuse_value;
-uniform sampler2D tex;
+uniform sampler2D nv_t_diffuse;
 uniform vec3 light;
  
@@ -16,7 +16,7 @@
 
 	if (secondary)
-		gl_FragColor = texture2D(tex, vec2((fract(f_coord.z - f_coord.x) + modulus ), ( fract(-f_coord.y) + remmod) )  / 16.0);
+		gl_FragColor = texture2D(nv_t_diffuse, vec2((fract(f_coord.z - f_coord.x) + modulus ), ( fract(-f_coord.y) + remmod) )  / 16.0);
 	else
-		gl_FragColor = texture2D(tex, vec2((fract(f_coord.z) + modulus ), ( fract(f_coord.x) + remmod) )  / 16.0);
+		gl_FragColor = texture2D(nv_t_diffuse, vec2((fract(f_coord.z) + modulus ), ( fract(f_coord.x) + remmod) )  / 16.0);
 	gl_FragColor = vec4( gl_FragColor.xyz * (f_diffuse_value + 0.4), 1.0 );
 }
Index: /trunk/tests/render_test/box.vert
===================================================================
--- /trunk/tests/render_test/box.vert	(revision 237)
+++ /trunk/tests/render_test/box.vert	(revision 238)
@@ -1,21 +1,22 @@
 #version 120
-attribute vec3 coords;
-attribute vec3 material;
+attribute vec3 nv_position;
+attribute vec3 nv_color;
+uniform mat4 nv_m_mvp;
+
 varying vec3 f_coord;
 varying vec3 f_normal;
 varying vec3 f_material;
 varying float f_diffuse_value;
-uniform mat4 matrix_mvp;
 uniform vec3 light;
 
 void main(void) {
 	f_normal = vec3(0,0,0);
-	f_normal[int(abs(material.y)-1)] = sign( material.y );
+	f_normal[int(abs(nv_color.y)-1)] = sign( nv_color.y );
 	vec3 vnormal  = normalize(f_normal);
-	vec3 vlight   = normalize(light - coords);
+	vec3 vlight   = normalize(light - nv_position);
 	float diffuse = max(dot(vlight, vnormal), 0.0);
 	f_diffuse_value = diffuse;
-	f_coord = coords;
-	f_material = material;
-	gl_Position = matrix_mvp * vec4(coords, 1.0);
+	f_coord = nv_position;
+	f_material = nv_color;
+	gl_Position = nv_m_mvp * vec4(nv_position, 1.0);
 }
Index: /trunk/tests/render_test/char.frag
===================================================================
--- /trunk/tests/render_test/char.frag	(revision 237)
+++ /trunk/tests/render_test/char.frag	(revision 238)
@@ -2,5 +2,5 @@
 varying vec3 f_coord;
 varying vec3 f_material;
-uniform sampler2D tex;
+uniform sampler2D nv_t_diffuse;
  
 void main(void) {
@@ -9,6 +9,6 @@
 	float modulus = w - remmod * 16;
 
-	gl_FragColor = texture2D(tex, vec2((fract(f_coord.x) + modulus ), ( fract(f_coord.z) + remmod) )  / 16.0);
+	gl_FragColor = texture2D(nv_t_diffuse, vec2((fract(f_coord.x) + modulus ), ( fract(f_coord.z) + remmod) )  / 16.0);
 
-//	gl_FragColor = texture2D(tex, vec2((fract(f_coord.z - f_coord.x) + modulus ), ( fract(-f_coord.y) + remmod) )  / 16.0);
+//	gl_FragColor = texture2D(nv_t_diffuse, vec2((fract(f_coord.z - f_coord.x) + modulus ), ( fract(-f_coord.y) + remmod) )  / 16.0);
 }
Index: /trunk/tests/render_test/char.vert
===================================================================
--- /trunk/tests/render_test/char.vert	(revision 237)
+++ /trunk/tests/render_test/char.vert	(revision 238)
@@ -1,13 +1,14 @@
 #version 120
-attribute vec3 coords;
-attribute vec3 material;
+attribute vec3 nv_position;
+attribute vec3 nv_color;
+uniform mat4 nv_m_mvp;
+
 varying vec3 f_coord;
 varying vec3 f_material;
-uniform mat4 matrix_mvp;
 uniform vec3 pos;
 
 void main(void) {
-	f_coord = coords;
-	f_material = material;
-	gl_Position = matrix_mvp * vec4(coords + pos, 1.0);
+	f_coord = nv_position;
+	f_material = nv_color;
+	gl_Position = nv_m_mvp * vec4(nv_position + pos, 1.0);
 }
Index: /trunk/tests/render_test/rl.cc
===================================================================
--- /trunk/tests/render_test/rl.cc	(revision 237)
+++ /trunk/tests/render_test/rl.cc	(revision 238)
@@ -67,6 +67,8 @@
 	nv::window* m_window;
 	nv::texture2d* m_texture;
-	nv::clear_state m_clear_state;
+
+	nv::clear_state  m_clear_state;
 	nv::render_state m_render_state;
+	nv::scene_state  m_scene_state;
 
 	nv::program* m_char_program;
@@ -75,4 +77,13 @@
 	nv::vertex_array* m_box_va;
 	unsigned int m_count;
+};
+
+struct vtx
+{
+	nv::i8vec3 position;
+	nv::i8vec3 color;
+
+	vtx( const nv::i8vec3& a_position, const nv::i8vec3& a_color )
+		: position( a_position ), color( a_color ) {}
 };
 
@@ -99,24 +110,20 @@
 {
 	{ // CHARACTER
-		nv::mesh cmesh;
-		nv::vertex_attribute<nv::i8vec3>::list& vtx = cmesh.add_attribute<nv::i8vec3>("coords")->get();
-		nv::vertex_attribute<nv::i8vec3>::list& mat = cmesh.add_attribute<nv::i8vec3>("material")->get();
+		std::vector< vtx > v;
 		int m = 16;	int x = 0; int y = 0; int h = 0;
-		vtx.emplace_back( x,   h, y );  
-		vtx.emplace_back( x,   h, y+1 );
-		vtx.emplace_back( x+1, h, y+1 );
-		vtx.emplace_back( x+1, h, y+1 );
-		vtx.emplace_back( x+1, h, y );  
-		vtx.emplace_back( x,   h, y );  
-		mat.insert( mat.end(), 6, nv::i8vec3( m, 1, 0 ) );
+		nv::i8vec3 mt( m, 1, 0 );
+		v.emplace_back( nv::i8vec3( x,   h, y ), mt );  
+		v.emplace_back( nv::i8vec3( x,   h, y+1 ), mt );
+		v.emplace_back( nv::i8vec3( x+1, h, y+1 ), mt );
+		v.emplace_back( nv::i8vec3( x+1, h, y+1 ), mt );
+		v.emplace_back( nv::i8vec3( x+1, h, y ), mt );  
+		v.emplace_back( nv::i8vec3( x,   h, y ), mt );  
 		m_char_program = m_device->create_program( nv::slurp( "char.vert" ), nv::slurp( "char.frag" ) );
-		m_char_va      = m_device->create_vertex_array( &cmesh, &(m_char_program->get_attributes()), nv::STATIC_DRAW );
+		m_char_va      = m_device->create_vertex_array( v, nv::STATIC_DRAW );
 	}
 
 	{ // WORLD
-		nv::mesh wmesh;
-		nv::vertex_attribute<nv::i8vec3>::list& vtx = wmesh.add_attribute<nv::i8vec3>("coords")->get();
-		nv::vertex_attribute<nv::i8vec3>::list& mat = wmesh.add_attribute<nv::i8vec3>("material")->get();
-
+		std::vector< vtx > v;
+		nv::i8vec3 mt;
 		for (int i = 0; i < size_x * size_y; ++i )
 		{
@@ -124,11 +131,11 @@
 			int y = i / size_x;
 
-			vtx.emplace_back( x,   height[i], y   ); 
-			vtx.emplace_back( x,   height[i], y+1 ); 
-			vtx.emplace_back( x+1, height[i], y+1 ); 
-			vtx.emplace_back( x+1, height[i], y+1 ); 
-			vtx.emplace_back( x+1, height[i], y );   
-			vtx.emplace_back( x,   height[i], y );   
-			mat.insert( mat.end(), 6, nv::i8vec3( map[i], 2, 0 ) );
+			mt = nv::i8vec3( map[i], 2, 0 );
+			v.emplace_back( nv::i8vec3( x,   height[i], y   ), mt );
+			v.emplace_back( nv::i8vec3( x,   height[i], y+1 ), mt );
+			v.emplace_back( nv::i8vec3( x+1, height[i], y+1 ), mt );
+			v.emplace_back( nv::i8vec3( x+1, height[i], y+1 ), mt );
+			v.emplace_back( nv::i8vec3( x+1, height[i], y ), mt );
+			v.emplace_back( nv::i8vec3( x,   height[i], y ), mt );
 
 			if (i > 0 && height[i-1] != height[i])
@@ -139,11 +146,11 @@
 				{
 					m_count += 6;
-					vtx.emplace_back( x, h,     y );   
-					vtx.emplace_back( x, h,     y+1 ); 
-					vtx.emplace_back( x, h+dir, y+1 ); 
-					vtx.emplace_back( x, h+dir, y+1 ); 
-					vtx.emplace_back( x, h+dir, y );   
-					vtx.emplace_back( x, h,     y );   
-					mat.insert( mat.end(), 6, nv::i8vec3( m, -dir, 0 ) );
+					mt = nv::i8vec3( m, -dir, 0 );
+					v.emplace_back( nv::i8vec3( x, h,     y ), mt );
+					v.emplace_back( nv::i8vec3( x, h,     y+1 ), mt );
+					v.emplace_back( nv::i8vec3( x, h+dir, y+1 ), mt );
+					v.emplace_back( nv::i8vec3( x, h+dir, y+1 ), mt );
+					v.emplace_back( nv::i8vec3( x, h+dir, y ), mt );
+					v.emplace_back( nv::i8vec3( x, h,     y ), mt );
 				}
 			}
@@ -155,12 +162,12 @@
 				for ( int h = height[i-size_x]; h != height[i]; h = h + dir )
 				{
-					vtx.emplace_back( x,   h,     y ); 
-					vtx.emplace_back( x,   h+dir, y ); 
-					vtx.emplace_back( x+1, h+dir, y ); 
-
-					vtx.emplace_back( x+1, h+dir, y ); 
-					vtx.emplace_back( x+1, h,     y ); 
-					vtx.emplace_back( x,   h,     y ); 
-					mat.insert( mat.end(), 6, nv::i8vec3( m, -3*dir, 0 ) );
+					mt = nv::i8vec3( m, -3*dir, 0 );
+					v.emplace_back( nv::i8vec3( x,   h,     y ), mt );
+					v.emplace_back( nv::i8vec3( x,   h+dir, y ), mt );
+					v.emplace_back( nv::i8vec3( x+1, h+dir, y ), mt );
+
+					v.emplace_back( nv::i8vec3( x+1, h+dir, y ), mt );
+					v.emplace_back( nv::i8vec3( x+1, h,     y ), mt );
+					v.emplace_back( nv::i8vec3( x,   h,     y ), mt );
 				}
 			}
@@ -168,7 +175,7 @@
 		}
 
-		m_count       = vtx.size();
+		m_count       = v.size();
 		m_box_program = m_device->create_program( nv::slurp( "box.vert" ), nv::slurp( "box.frag" ) );
-		m_box_va      = m_device->create_vertex_array( &wmesh, &(m_box_program->get_attributes()), nv::STATIC_DRAW );
+		m_box_va      = m_device->create_vertex_array( v, nv::STATIC_DRAW );
 	}
 
@@ -181,24 +188,18 @@
 
 	glm::vec3 move( 0, 0, 0 );
+	m_scene_state.get_camera().set_perspective(25.0f, 1.0f*800.0f/600.0f, 0.1f, 100.0f);
 
 	while(!keypress) 
 	{
+		m_scene_state.set_model( glm::translate(glm::mat4(1.0f), glm::vec3(-8.5, 0.0, -8.0)) );
+		m_scene_state.get_camera().set_lookat(glm::vec3(0.0, 20.0, 5.0) + move, glm::vec3(0.0, 4.0, 0.0) + move, glm::vec3(0.0, 1.0, 0.0));
+
 		m_window->get_context()->clear( m_clear_state );
-
-		glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(-8.5, 0.0, -8.0));
-		glm::mat4 view  = glm::lookAt(glm::vec3(0.0, 20.0, 5.0) + move, glm::vec3(0.0, 4.0, 0.0) + move, glm::vec3(0.0, 1.0, 0.0));
-		glm::mat4 projection = glm::perspective(25.0f, 1.0f*800.0f/600.0f, 0.1f, 100.0f);
-		glm::mat4 mv         = view * model;
-
 		m_texture->bind( 0 );
-		m_box_program->set_uniform( "matrix_mvp", projection * mv );
 		m_box_program->set_uniform( "light", glm::vec3(8.5, 4.5, 6.5) + move );
-		m_box_program->set_uniform( "tex", 0 );
-		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_box_program, m_box_va, m_count );
-
-		m_char_program->set_uniform( "matrix_mvp", projection * mv );
+		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_scene_state, m_box_program, m_box_va, m_count );
+
 		m_char_program->set_uniform( "pos", move + glm::vec3( 8, 4.1, 6 ) );
-		m_char_program->set_uniform( "tex", 0 );
-		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_char_program, m_char_va, 6 );
+		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_scene_state, m_char_program, m_char_va, 6 );
 		m_window->swap_buffers();
 
