Index: trunk/nv/interface/camera.hh
===================================================================
--- trunk/nv/interface/camera.hh	(revision 234)
+++ trunk/nv/interface/camera.hh	(revision 235)
@@ -25,5 +25,7 @@
 		void set_lookat( const vec3& eye, const vec3& focus, const vec3& up )
 		{
-			m_view  = glm::lookAt( eye, focus, up );
+			m_view      = glm::lookAt( eye, focus, up );
+			m_position  = eye;
+			m_direction = glm::normalize( focus - eye );
 		}
 
@@ -40,6 +42,16 @@
 			return m_view;
 		}
+		const vec3& get_position() const
+		{
+			return m_position;
+		}
+		const vec3& get_direction() const
+		{
+			return m_direction;
+		}
 	private:
 		bool m_dirty;
+		vec3 m_position;
+		vec3 m_direction;
 		mat4 m_projection;
 		mat4 m_view;
@@ -62,4 +74,5 @@
 
 		mat4 get_view_inv()   const { return glm::inverse( get_view() ); }
+		mat4 get_model_inv()  const { return glm::inverse( get_model() ); }
 		mat3 get_normal()     const { return glm::transpose(glm::inverse(glm::mat3( get_modelview() ) ) ); }
 	protected:
Index: trunk/nv/interface/program.hh
===================================================================
--- trunk/nv/interface/program.hh	(revision 234)
+++ trunk/nv/interface/program.hh	(revision 235)
@@ -27,9 +27,11 @@
 	enum slot
 	{
-		POSITION = 0,
-		TEXCOORD = 1,
-		NORMAL   = 2,
-		COLOR    = 3,
-		TANGENT  = 4,
+		POSITION   = 0,
+		TEXCOORD   = 1,
+		NORMAL     = 2,
+		TANGENT    = 3,
+		BONEINDEX  = 4,
+		BONEWEIGHT = 5,
+		COLOR      = 6,
 	};
 
@@ -95,9 +97,19 @@
 		}
 
-		attribute* get_attribute( const string& name ) const
+		int try_get_attribute_location( const string& name ) const
 		{
 			attribute_map::const_iterator i = m_attribute_map.find( name );
 			if ( i != m_attribute_map.end() )
 			{
+				return i->second->get_location();
+			}
+			return -1;
+		}
+
+		attribute* get_attribute( const string& name ) const
+		{
+			attribute_map::const_iterator i = m_attribute_map.find( name );
+			if ( i != m_attribute_map.end() )
+			{
 				return i->second;
 			}
@@ -128,7 +140,70 @@
 
 		template < typename T >
+		void set_uniform_array( const string& name, const T* value, uint32 count )
+		{
+			uniform_base* base = get_uniform( name );
+			// restore typechecking, but remember to accept int for float!
+			// if ( /* base->get_type() != type_to_enum<T>::type */ )
+			// {
+			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
+			//		return;
+			// }
+			// TODO: nicer check
+			NV_ASSERT( count <= base->get_length(), "LENGTH CHECK FAIL" );
+			((uniform<T>*)( base ))->set_value( value, count );
+		}
+
+		template < typename T >
+		void set_uniform_array( const string& name, const std::vector<T>& value )
+		{
+			uniform_base* base = get_uniform( name );
+			// restore typechecking, but remember to accept int for float!
+			// if ( /* base->get_type() != type_to_enum<T>::type */ )
+			// {
+			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
+			//		return;
+			// }
+			// TODO: nicer check
+			NV_ASSERT( (int)value.size() <= base->get_length(), "LENGTH CHECK FAIL" );
+			((uniform<T>*)( base ))->set_value( value.data(), value.size() );
+		}
+
+		template < typename T >
+		void set_opt_uniform_array( const string& name, const T* value, uint32 count )
+		{
+			uniform_base* base = get_uniform( name );
+			if (!base) return;
+			// restore typechecking, but remember to accept int for float!
+			// if ( /* base->get_type() != type_to_enum<T>::type */ )
+			// {
+			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
+			//		return;
+			// }
+			// TODO: nicer check
+			NV_ASSERT( (int)count <= base->get_length(), "LENGTH CHECK FAIL" );
+			((uniform<T>*)( base ))->set_value( value, count );
+		}
+
+		template < typename T >
+		void set_opt_uniform_array( const string& name, const std::vector<T>& value )
+		{
+			uniform_base* base = try_get_uniform( name );
+			if (!base) return;
+			// restore typechecking, but remember to accept int for float!
+			// if ( /* base->get_type() != type_to_enum<T>::type */ )
+			// {
+			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
+			//		return;
+			// }
+			// TODO: nicer check
+			NV_ASSERT( (int)value.size() <= base->get_length(), "LENGTH CHECK FAIL" );
+			((uniform<T>*)( base ))->set_value( value.data(), value.size() );
+		}
+
+
+		template < typename T >
 		void set_uniform( const string& name, const T& value )
 		{
-			uniform_base* base = get_uniform( name );
+			uniform_base* base = try_get_uniform( name );
 			// restore typechecking, but remember to accept int for float!
 			// if ( /* base->get_type() != type_to_enum<T>::type */ )
@@ -209,10 +284,12 @@
 			factory_map[ "nv_m_view" ]       = new engine_uniform_factory< engine_uniform_m_view >();
 			factory_map[ "nv_m_view_inv" ]   = new engine_uniform_factory< engine_uniform_m_view_inv >();
-			factory_map[ "nv_m_view" ]       = new engine_uniform_factory< engine_uniform_m_view >();
 			factory_map[ "nv_m_model" ]      = new engine_uniform_factory< engine_uniform_m_model >();
+			factory_map[ "nv_m_model_inv" ]  = new engine_uniform_factory< engine_uniform_m_model_inv >();
 			factory_map[ "nv_m_modelview" ]  = new engine_uniform_factory< engine_uniform_m_modelview >();
 			factory_map[ "nv_m_projection" ] = new engine_uniform_factory< engine_uniform_m_projection >();
 			factory_map[ "nv_m_normal" ]     = new engine_uniform_factory< engine_uniform_m_normal >();
 			factory_map[ "nv_m_mvp" ]        = new engine_uniform_factory< engine_uniform_m_mvp >();
+			factory_map[ "nv_v_camera_position" ]  = new engine_uniform_factory< engine_uniform_v_camera_position >();
+			factory_map[ "nv_v_camera_direction" ] = new engine_uniform_factory< engine_uniform_v_camera_direction >();
 
 			engine_link_uniform_factory_map& factory_link_map = get_link_uniform_factory();
Index: trunk/nv/interface/uniform.hh
===================================================================
--- trunk/nv/interface/uniform.hh	(revision 234)
+++ trunk/nv/interface/uniform.hh	(revision 235)
@@ -32,4 +32,5 @@
 		bool is_dirty() const { return m_dirty; }
 		void clean() { m_dirty = false; }
+		virtual ~uniform_base() {}
 	protected:
 		string   m_name;
@@ -47,19 +48,33 @@
 
 		uniform( const string& name, int location, int length ) 
-			: uniform_base( name, type_to_enum< T >::type, location, length ), m_value()
-		{}
+			: uniform_base( name, type_to_enum< T >::type, location, length ), m_value( nullptr )
+		{
+			m_value = new T[ m_length ];
+		}
 
 		void set_value( const T& value ) 
 		{ 
-			if ( value != m_value )
+			NV_ASSERT( m_length == 1, "set_value on array uniform!" );
+			if ( value != m_value[0] )
 			{
-				m_value = value; 
+				m_value[0] = value; 
 				m_dirty = true;
 			}
 		}
 
-		const T& get_value() { return m_value; }
+		void set_value( const T* value, uint32 count ) 
+		{ 
+			// TODO: memcmp?
+			std::copy( value, value + count, m_value );
+			m_dirty = true;
+		}
+
+		const T* get_value() { return m_value; }
+		virtual ~uniform() 
+		{
+			delete[] m_value;
+		}
 	protected:
-		T m_value;
+		T* m_value;
 	};
 
@@ -145,4 +160,11 @@
 	};
 
+	class engine_uniform_m_model_inv : public engine_uniform< mat4 >
+	{
+	public:
+		engine_uniform_m_model_inv( uniform_base* u ) : engine_uniform( u ) {}
+		virtual void set( const context* , const scene_state* s ) { m_uniform->set_value( s->get_model_inv() ); }
+	};
+
 	class engine_uniform_m_modelview : public engine_uniform< mat4 >
 	{
@@ -173,4 +195,18 @@
 	};
 
+	class engine_uniform_v_camera_position : public engine_uniform< vec3 >
+	{
+	public:
+		engine_uniform_v_camera_position( uniform_base* u ) : engine_uniform( u ) {}
+		virtual void set( const context* , const scene_state* s ) { m_uniform->set_value( s->get_camera().get_position() ); }
+	};
+
+	class engine_uniform_v_camera_direction : public engine_uniform< vec3 >
+	{
+	public:
+		engine_uniform_v_camera_direction( uniform_base* u ) : engine_uniform( u ) {}
+		virtual void set( const context* , const scene_state* s ) { m_uniform->set_value( s->get_camera().get_direction() ); }
+	};
+
 	template< int VALUE >
 	class engine_link_uniform_int : public engine_link_uniform< int >
Index: trunk/nv/math.hh
===================================================================
--- trunk/nv/math.hh	(revision 234)
+++ trunk/nv/math.hh	(revision 235)
@@ -27,4 +27,8 @@
 	typedef glm::detail::tvec3<sint8> i8vec3;
 	typedef glm::detail::tvec4<sint8> i8vec4;
+
+	typedef glm::detail::tvec2<sint16> i16vec2;
+	typedef glm::detail::tvec3<sint16> i16vec3;
+	typedef glm::detail::tvec4<sint16> i16vec4;
 
 	typedef glm::vec2 vec2;
Index: trunk/src/gl/gl_program.cc
===================================================================
--- trunk/src/gl/gl_program.cc	(revision 234)
+++ trunk/src/gl/gl_program.cc	(revision 235)
@@ -106,9 +106,11 @@
 	if (!fragment_shader.compile( fragment_program )) { return false; }
 
-	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::POSITION ), "nv_position" );
-	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::TEXCOORD ), "nv_texcoord" );
-	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::NORMAL   ), "nv_normal"   );
-	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::COLOR    ), "nv_color"    );
-	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::TANGENT  ), "nv_tangent"  );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::POSITION   ), "nv_position"  );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::TEXCOORD   ), "nv_texcoord"  );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::NORMAL     ), "nv_normal"    );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::COLOR      ), "nv_color"     );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::TANGENT    ), "nv_tangent"   );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::BONEINDEX  ), "nv_boneindex" );
+	glBindAttribLocation( m_name.get_value(), static_cast<GLuint>( slot::BONEWEIGHT ), "nv_boneweight");
 
 	glAttachShader( m_name.get_value(), fragment_shader.get_id() );
@@ -181,5 +183,5 @@
 
 		string name( name_buffer, size_t(uni_nlen) );
-
+		
 		// skip built-ins
 		if ( name.substr(0,3) == "gl_" ) continue;
@@ -187,4 +189,12 @@
 		int uni_loc = glGetUniformLocation( m_name.get_value(), name.c_str() );
 		datatype utype = gl_enum_to_datatype( uni_type );
+		
+		// check for array
+		string::size_type arrchar = name.find('[');
+		if ( arrchar != string::npos )
+		{
+			name = name.substr( 0, arrchar );
+		}
+
 		m_uniform_map[ name ] = create_uniform( utype, name, uni_loc, uni_len );
 	}
@@ -204,15 +214,15 @@
 			switch( ubase->get_type() )
 			{
-			case FLOAT          : glUniform1f( uloc, ((uniform< enum_to_type< FLOAT >::type >*)( ubase ))->get_value() ); break;
-			case INT            : glUniform1i( uloc, ((uniform< enum_to_type< INT >::type >*)( ubase ))->get_value() ); break;
-			case FLOAT_VECTOR_2 : glUniform2fv( uloc, 1, glm::value_ptr(((uniform< enum_to_type< FLOAT_VECTOR_2 >::type >*)( ubase ))->get_value()) ); break;
-			case FLOAT_VECTOR_3 : glUniform3fv( uloc, 1, glm::value_ptr(((uniform< enum_to_type< FLOAT_VECTOR_3 >::type >*)( ubase ))->get_value()) ); break;
-			case FLOAT_VECTOR_4 : glUniform4fv( uloc, 1, glm::value_ptr(((uniform< enum_to_type< FLOAT_VECTOR_4 >::type >*)( ubase ))->get_value()) ); break;
-			case INT_VECTOR_2   : glUniform2iv( uloc, 1, glm::value_ptr(((uniform< enum_to_type< INT_VECTOR_2 >::type >*)( ubase ))->get_value()) ); break;
-			case INT_VECTOR_3   : glUniform3iv( uloc, 1, glm::value_ptr(((uniform< enum_to_type< INT_VECTOR_3 >::type >*)( ubase ))->get_value()) ); break;
-			case INT_VECTOR_4   : glUniform4iv( uloc, 1, glm::value_ptr(((uniform< enum_to_type< INT_VECTOR_4 >::type >*)( ubase ))->get_value()) ); break;
-			case FLOAT_MATRIX_2 : glUniformMatrix2fv( uloc, 1, GL_FALSE, glm::value_ptr(((uniform< enum_to_type< FLOAT_MATRIX_2 >::type >*)( ubase ))->get_value()) ); break;
-			case FLOAT_MATRIX_3 : glUniformMatrix3fv( uloc, 1, GL_FALSE, glm::value_ptr(((uniform< enum_to_type< FLOAT_MATRIX_3 >::type >*)( ubase ))->get_value()) ); break;
-			case FLOAT_MATRIX_4 : glUniformMatrix4fv( uloc, 1, GL_FALSE, glm::value_ptr(((uniform< enum_to_type< FLOAT_MATRIX_4 >::type >*)( ubase ))->get_value()) ); break;
+			case FLOAT          : glUniform1fv( uloc, ubase->get_length(), ((uniform< enum_to_type< FLOAT >::type >*)( ubase ))->get_value() ); break;
+			case INT            : glUniform1iv( uloc, ubase->get_length(), ((uniform< enum_to_type< INT >::type >*)( ubase ))->get_value() ); break;
+			case FLOAT_VECTOR_2 : glUniform2fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_VECTOR_3 : glUniform3fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_VECTOR_4 : glUniform4fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
+			case INT_VECTOR_2   : glUniform2iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
+			case INT_VECTOR_3   : glUniform3iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
+			case INT_VECTOR_4   : glUniform4iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_MATRIX_2 : glUniformMatrix2fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_2 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_MATRIX_3 : glUniformMatrix3fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_3 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_MATRIX_4 : glUniformMatrix4fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_4 >::type >*)( ubase ))->get_value()); break;
 			default : break; // error?
 			}
