Index: trunk/nv/gl/gl_program.hh
===================================================================
--- trunk/nv/gl/gl_program.hh	(revision 36)
+++ trunk/nv/gl/gl_program.hh	(revision 36)
@@ -0,0 +1,67 @@
+// 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
+/**
+ * @file gl_program.hh
+ * @author Kornel Kisielewicz epyon@chaosforge.org
+ * @brief Program class
+ */
+
+#ifndef NV_GL_PROGRAM_HH
+#define NV_GL_PROGRAM_HH
+
+#include <nv/common.hh>
+#include <nv/interface/program.hh>
+#include <nv/string.hh>
+#include <nv/gl/gl_names.hh>
+
+namespace nv
+{
+
+	class gl_shader
+	{
+	public:
+		explicit gl_shader( uint32 shader_type );
+		gl_shader( uint32 shader_type, const string& shader_code );
+		~gl_shader();
+
+		bool compile( const std::string& shader_code );
+
+		void generate();
+		void free();
+		bool validate();
+
+		bool is_valid() const { return object_id != 0; }
+		unsigned int get_id() const	{ return object_id;	}
+	private:
+		uint32 shader_type;
+		uint32 object_id;
+	};
+
+	class gl_program : public program
+	{
+	public:
+		gl_program( const string& vertex_program, const string& fragment_program, const string& geometry_program = "");
+		bool compile( const string& vertex_program, const string& fragment_program, const string& geometry_program = "" );
+
+		virtual void bind();
+		virtual void unbind();
+		virtual bool is_valid() const;
+
+		~gl_program();
+	protected:
+		bool validate();
+		void load_attributes();
+		void load_uniforms();
+
+		gl_shader_name m_name;
+		gl_shader vertex_shader;
+		gl_shader fragment_shader;
+		gl_shader geometry_shader;
+	};
+
+} // namespace nv
+
+#endif // NV_GL_PROGRAM_HH
Index: trunk/src/gl/gl_enum.cc
===================================================================
--- trunk/src/gl/gl_enum.cc	(revision 35)
+++ trunk/src/gl/gl_enum.cc	(revision 36)
@@ -1,3 +1,3 @@
-// Copyright (C) 2011 Kornel Kisielewicz
+// Copyright (C) 2012-2013 Kornel Kisielewicz
 // This file is part of NV Libraries.
 // For conditions of distribution and use, see copyright notice in nv.hh
Index: trunk/src/gl/gl_names.cc
===================================================================
--- trunk/src/gl/gl_names.cc	(revision 35)
+++ trunk/src/gl/gl_names.cc	(revision 36)
@@ -1,5 +1,5 @@
-// Copyright (C) 2012 Kornel Kisielewicz
-// This file is part of NOVA Libraries.
-// For conditions of distribution and use, see copyright notice in nova.hh
+// Copyright (C) 2012-2013 Kornel Kisielewicz
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
 
 #include "nv/gl/gl_names.hh"
Index: trunk/src/gl/gl_program.cc
===================================================================
--- trunk/src/gl/gl_program.cc	(revision 36)
+++ trunk/src/gl/gl_program.cc	(revision 36)
@@ -0,0 +1,220 @@
+// Copyright (C) 2012-2013 Kornel Kisielewicz
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/gl/gl_program.hh"
+
+#include "nv/gl/gl_enum.hh"
+#include "nv/logging.hh"
+#include "nv/lib/gl.hh"
+
+using namespace nv;
+
+gl_shader::gl_shader( uint32 shader_type ) 
+	: object_id(0), shader_type( shader_type )
+{
+	// no op
+}
+
+gl_shader::gl_shader( uint32 shader_type, const string& shader_code )
+	: object_id(0), shader_type( shader_type )
+{
+	compile( shader_code );
+}
+
+gl_shader::~gl_shader()
+{
+	free();
+}
+
+bool gl_shader::compile( const string& shader_code )
+{
+	generate();
+
+	const char* pc = shader_code.c_str();
+
+	glShaderSource( object_id,   1, &pc, 0 );
+	glCompileShader( object_id );
+
+	return validate();
+}
+
+void gl_shader::generate()
+{
+	if ( is_valid() )
+	{
+		free();
+	}
+	object_id = glCreateShader( shader_type );
+}
+
+void gl_shader::free()
+{
+	glDeleteShader( object_id );
+	object_id = 0;
+}
+
+bool gl_shader::validate()
+{
+	const uint32 buffer_size = 1024;
+	char buffer[ buffer_size ] = { 0 };
+	int length;
+	glGetShaderInfoLog( object_id, buffer_size, &length, buffer );
+
+	if ( length > 0 )
+	{
+		NV_LOG( LOG_INFO, "Shader #" << object_id << " compiled: " << buffer );
+	}
+	else
+	{
+		NV_LOG( LOG_INFO, "Shader #" << object_id << " compiled successfully." );
+	}
+	return true;
+}
+
+gl_program::gl_program( const string& vertex_program, const string& fragment_program, const string& geometry_program /*= ""*/ )
+	: vertex_shader( GL_VERTEX_SHADER ), fragment_shader( GL_FRAGMENT_SHADER ), geometry_shader( GL_GEOMETRY_SHADER )
+{
+	compile( vertex_program, fragment_program, geometry_program );
+}
+
+gl_program::~gl_program()
+{
+	if ( is_valid() )
+	{
+		// Detach the shaders from the program
+		glDetachShader( m_name.get_value(), vertex_shader.get_id() );
+		glDetachShader( m_name.get_value(), fragment_shader.get_id() );
+		if ( geometry_shader.is_valid() )
+		{
+			glDetachShader( m_name.get_value(), geometry_shader.get_id() );
+		}
+	}
+}
+
+bool gl_program::compile( const string& vertex_program, const string& fragment_program, const string& geometry_program /*= "" */ )
+{
+	if (!vertex_shader.compile( vertex_program )) { return false; }
+	if (!fragment_shader.compile( fragment_program )) { return false; }
+	if (!geometry_program.empty())
+	{
+		if (!geometry_shader.compile( geometry_program )) { return false; }
+	}
+
+	glAttachShader( m_name.get_value(), fragment_shader.get_id() );
+	glAttachShader( m_name.get_value(), vertex_shader.get_id() );
+	if (!geometry_program.empty())
+	{
+		glAttachShader( m_name.get_value(), geometry_shader.get_id() );
+	}
+	glLinkProgram( m_name.get_value() );
+
+	if (!validate())
+	{
+		return false;
+	}
+	load_attributes();
+	load_uniforms();
+	return true;
+}
+
+void gl_program::bind()
+{
+	glUseProgram( m_name.get_value() );
+}
+
+void gl_program::unbind()
+{
+	glUseProgram( 0 );
+}
+
+bool gl_program::is_valid() const
+{
+	return m_name.is_valid();
+}
+
+void gl_program::load_attributes()
+{
+	int params;
+	glGetProgramiv( m_name.get_value(), GL_ACTIVE_ATTRIBUTES, &params );
+
+	for ( int i = 0; i < params; ++i )
+	{
+		int attr_nlen;
+		int attr_len;
+		unsigned attr_type;
+		char name_buffer[128];
+
+		glGetActiveAttrib( m_name.get_value(), i, 128, &attr_nlen, &attr_len, &attr_type, name_buffer );
+
+		string name( name_buffer, attr_nlen );
+
+		// skip built-ins
+		if ( name.substr(0,3) == "gl_" ) continue;
+
+		int attr_loc = glGetAttribLocation( m_name.get_value(), name.c_str() );
+
+		m_attribute_map[ name ] = new attribute( name, attr_loc, gl_enum_to_type( attr_type ), attr_len );
+	}
+}
+
+void gl_program::load_uniforms()
+{
+	int params;
+	glGetProgramiv( m_name.get_value(), GL_ACTIVE_UNIFORMS, &params );
+
+	for ( int i = 0; i < params; ++i )
+	{
+		int uni_nlen;
+		int uni_len;
+		unsigned uni_type;
+		char name_buffer[128];
+
+		glGetActiveUniform( m_name.get_value(), i, 128, &uni_nlen, &uni_len, &uni_type, name_buffer );
+
+		string name( name_buffer, uni_nlen );
+
+		// skip built-ins
+		if ( name.substr(0,3) == "gl_" ) continue;
+
+		int uni_loc = glGetUniformLocation( m_name.get_value(), name.c_str() );
+		type utype = gl_enum_to_type( uni_type );
+		m_uniform_map[ name ] = create_uniform( gl_enum_to_type( uni_type ), name, uni_loc, uni_len );
+	}
+}
+
+bool gl_program::validate()
+{
+	const uint32 buffer_size = 2048;
+	char buffer[ buffer_size ] = { 0 };
+	int length;
+	int status;
+
+	glGetProgramiv( m_name.get_value(), GL_LINK_STATUS, &status );
+	glGetProgramInfoLog( m_name.get_value(), buffer_size, &length, buffer );
+
+	NV_LOG( LOG_INFO, "Program #" << m_name.get_value() << (status == GL_FALSE ? "failed to compile!" : "compiled successfuly.") );
+
+	if ( length > 0 )
+	{
+		NV_LOG( LOG_INFO, "Program #" << m_name.get_value() << " log: " << buffer );
+	}
+
+	if ( status == GL_FALSE ) 
+	{
+		return false;
+	}
+
+	glValidateProgram( m_name.get_value() );
+	glGetProgramiv( m_name.get_value(), GL_VALIDATE_STATUS, &status );
+
+	if ( status == GL_FALSE )
+	{
+		glGetProgramInfoLog( m_name.get_value(), buffer_size, &length, buffer );
+		NV_LOG( LOG_ERROR, "Program #" << m_name.get_value() << " validation error : " << buffer );
+		return false;
+	}
+	return true;
+}
+
+
