Index: /trunk/nv/interface/program.hh
===================================================================
--- /trunk/nv/interface/program.hh	(revision 138)
+++ /trunk/nv/interface/program.hh	(revision 139)
@@ -155,4 +155,20 @@
 			// }
 			((uniform<T>*)( base ))->set_value( value );
+		}
+
+		template < typename T >
+		void set_opt_uniform( const string& name, const T& value )
+		{
+			uniform_base* base = try_get_uniform( name );
+			if ( base != nullptr )
+			{
+				// 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;
+				// }
+				((uniform<T>*)( base ))->set_value( value );
+			}
 		}
 	protected:
Index: /trunk/tests/objload_test/obj.frag
===================================================================
--- /trunk/tests/objload_test/obj.frag	(revision 138)
+++ /trunk/tests/objload_test/obj.frag	(revision 139)
@@ -1,8 +1,41 @@
 #version 120
-varying vec2 f_texcoord;
-varying float f_diffuse_value;
-uniform sampler2D tex;
+
+uniform sampler2D diffuse;
+uniform sampler2D specular;
+uniform vec4 light_diffuse;
+uniform vec4 light_specular;
+uniform vec3 custom_color_1;
+uniform vec3 custom_color_2;
+
+varying vec2 v_texcoord;
+varying vec3 v_normal;
+varying vec3 v_light_vector;
+varying vec3 v_view_vector;
  
 void main(void) {
-	gl_FragColor = vec4( texture2D( tex, f_texcoord ).rgb * f_diffuse_value, 1.0 );
+	vec3 nnormal         = normalize( v_normal );
+	vec3 nlight_vector   = normalize( v_light_vector );
+	vec3 nview_vector    = normalize( v_view_vector );
+	vec3 nreflect_vector = reflect( -nlight_vector, nnormal );
+
+	float specular_value = clamp( dot( nreflect_vector, nview_vector ), 0.0, 1.0 );
+	specular_value       = pow( specular_value, 6.0 );
+	float diffuse_value  = max( dot( nlight_vector, nnormal ), 0.0 );
+
+	vec3 diff_texel      = vec3( texture2D( diffuse, v_texcoord ) );	
+	vec4 spec_texel      = texture2D( specular, v_texcoord );
+	
+	float specular_amount = spec_texel.x;
+	float diffuse_amount  = 1.0 - specular_amount;
+
+	vec3 custom_color     = (custom_color_1 * (1.0-spec_texel.z) + custom_color_2 * (1.0-spec_texel.a))*0.4;
+
+	float final_specular = specular_amount * specular_value;
+	float final_diffuse  = diffuse_amount * diffuse_value;
+
+	vec3 self_ilum_color = spec_texel.y * diff_texel;
+	vec3 diffuse_color   = light_diffuse.xyz * final_diffuse * clamp( diff_texel + custom_color, 0.0, 1.0 );
+	vec3 specular_color  = light_specular.xyz * final_specular;
+
+	gl_FragColor = vec4( max( self_ilum_color, diffuse_color + specular_color), 1.0 );
 }
Index: /trunk/tests/objload_test/obj.vert
===================================================================
--- /trunk/tests/objload_test/obj.vert	(revision 138)
+++ /trunk/tests/objload_test/obj.vert	(revision 139)
@@ -1,17 +1,27 @@
 #version 120
+
 attribute vec3 position;
 attribute vec2 texcoord;
 attribute vec3 normal;
-varying vec2 f_texcoord;
-varying float f_diffuse_value;
+
+varying vec3 v_normal;
+varying vec3 v_light_vector;
+varying vec3 v_view_vector;
+varying vec2 v_texcoord;
+
 uniform mat4 matrix_mvp;
-uniform vec3 light;
+uniform mat4 nv_m_modelview;
+uniform mat4 nv_m_projection;
+uniform mat3 nv_m_normal;
+uniform vec3 light_position;
 
 void main(void) {
-	vec3 vnormal    = normalize(normal);
-	vec3 vlight     = normalize(light - position);
-	float diffuse   = max(dot(vlight, vnormal), 0.0);
-	f_diffuse_value = diffuse;
-	f_texcoord      = texcoord;
-	gl_Position     = matrix_mvp * vec4(position, 1.0);
+	vec4 vertex     = vec4( position, 1.0 );
+	vec3 eye_pos    = vec3( nv_m_modelview * vertex );
+	v_normal        = normalize( nv_m_normal * normal );
+	v_light_vector  = vec3( normalize( light_position - eye_pos ) );
+	v_view_vector   = vec3( normalize( -eye_pos ) );
+
+	v_texcoord      = texcoord;
+	gl_Position     = matrix_mvp * vertex;
 }
Index: /trunk/tests/objload_test/objload_test.cc
===================================================================
--- /trunk/tests/objload_test/objload_test.cc	(revision 138)
+++ /trunk/tests/objload_test/objload_test.cc	(revision 139)
@@ -24,9 +24,10 @@
 	~application();
 protected:
-	nv::device* m_device;
-	nv::window* m_window;
-	nv::texture2d* m_texture;
-	nv::clear_state m_clear_state;
-	nv::render_state m_render_state;
+	nv::device*       m_device;
+	nv::window*       m_window;
+	nv::texture2d*    m_diffuse;
+	nv::texture2d*    m_specular;
+	nv::clear_state   m_clear_state;
+	nv::render_state  m_render_state;
 
 	nv::vertex_array* m_va;
@@ -40,8 +41,12 @@
 	m_device = new nv::gl_device();
 	m_window = m_device->create_window( 800, 600 );
+	nv::sampler sampler( nv::sampler::LINEAR, nv::sampler::REPEAT );
 	
 	nv::image_data* sprites = m_device->create_image_data( "diffuse.png" );
-	nv::sampler sampler( nv::sampler::LINEAR, nv::sampler::REPEAT );
-	m_texture = m_device->create_texture2d( sprites->get_size(), nv::RGBA, nv::UBYTE, sampler, (void*)sprites->get_data() );
+	m_diffuse  = m_device->create_texture2d( sprites->get_size(), nv::RGBA, nv::UBYTE, sampler, (void*)sprites->get_data() );
+	delete sprites;
+
+	sprites    = m_device->create_image_data( "specular.png" );
+	m_specular = m_device->create_texture2d( sprites->get_size(), nv::RGBA, nv::UBYTE, sampler, (void*)sprites->get_data() );
 	delete sprites;
 
@@ -86,9 +91,19 @@
 		glm::mat4 projection = glm::perspective(60.0f, 1.0f*800.0f/600.0f, 0.1f, 1000.0f);
 		glm::mat4 mv         = view * model;
+		glm::mat3 normal_matrix = glm::transpose(glm::inverse(glm::mat3(mv)));
 
-		m_texture->bind( 0 );
+		m_diffuse->bind( 0 );
+		m_specular->bind( 1 );
+		m_program->set_opt_uniform( "nv_m_modelview", mv );
+		m_program->set_opt_uniform( "nv_m_projection", projection );
+		m_program->set_opt_uniform( "nv_m_normal", normal_matrix );
 		m_program->set_uniform( "matrix_mvp", projection * mv );
-		m_program->set_uniform( "light", glm::vec3(8.5, 8.5, 0) );
-		m_program->set_uniform( "tex", 0 );
+		m_program->set_uniform( "light_position", glm::vec3(12.0, 12.0, 0) );
+		m_program->set_uniform( "light_diffuse",  glm::vec4(0.7,0.7,0.7,1.0) );
+		m_program->set_uniform( "light_specular", glm::vec4(1.0,1.0,1.0,1.0) );
+		m_program->set_uniform( "custom_color_1", glm::vec3(1.0,0.0,0.0) );
+		m_program->set_uniform( "custom_color_2", glm::vec3(0.0,0.0,1.0) );
+		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->swap_buffers();
@@ -127,5 +142,6 @@
 	delete m_program;
 	delete m_mesh;
-	delete m_texture;
+	delete m_diffuse;
+	delete m_specular;
 	delete m_window;
 	delete m_device;
