From f3eb27f4a18de4eafa904955293e70cadb005967 Mon Sep 17 00:00:00 2001
From: dhewg <dhewg@wiibrew.org>
Date: Fri, 6 Jan 2012 20:26:08 +0100
Subject: [PATCH] Add GLSL backend

GLSL backend by:
Pat Raynor <raynorpat@raynorpat.com>
Sander van Rossen <sander.vanrossen@gmail.com>

Quashed commit due to me being lazy and the pain with diverging
trees. But cleaned it up a little.
---
 base/gl2progs/ambientInteraction.fs |   43 +++
 base/gl2progs/ambientInteraction.vs |   53 ++++
 base/gl2progs/interaction.fs        |  123 ++++++++
 base/gl2progs/interaction.vs        |   62 ++++
 base/gl2progs/stencilshadow.fs      |    7 +
 base/gl2progs/stencilshadow.vs      |   19 ++
 neo/CMakeLists.txt                  |    1 +
 neo/renderer/RenderSystem.cpp       |    9 +
 neo/renderer/RenderSystem.h         |    2 +
 neo/renderer/RenderSystem_init.cpp  |  102 ++++++-
 neo/renderer/draw_common.cpp        |    9 +-
 neo/renderer/draw_glsl.cpp          |  569 +++++++++++++++++++++++++++++++++++
 neo/renderer/qgl.h                  |   45 +++
 neo/renderer/tr_local.h             |   48 ++-
 14 files changed, 1089 insertions(+), 3 deletions(-)
 create mode 100644 base/gl2progs/ambientInteraction.fs
 create mode 100644 base/gl2progs/ambientInteraction.vs
 create mode 100644 base/gl2progs/interaction.fs
 create mode 100644 base/gl2progs/interaction.vs
 create mode 100644 base/gl2progs/stencilshadow.fs
 create mode 100644 base/gl2progs/stencilshadow.vs
 create mode 100644 neo/renderer/draw_glsl.cpp

diff --git a/base/gl2progs/ambientInteraction.fs b/base/gl2progs/ambientInteraction.fs
new file mode 100644
index 0000000..9923ac7
--- /dev/null
+++ b/base/gl2progs/ambientInteraction.fs
@@ -0,0 +1,43 @@
+#version 120
+
+varying vec3				var_Position;
+varying vec2				var_TexDiffuse;
+varying vec2				var_TexNormal;
+varying vec4				var_TexLight;
+varying mat3				var_TangentBinormalNormalMatrix;
+varying vec4				var_Color;
+
+uniform sampler2D			u_normalTexture;
+uniform sampler2D			u_lightFalloffTexture;
+uniform sampler2D			u_lightProjectionTexture;
+uniform sampler2D			u_diffuseTexture;
+
+uniform vec4				u_lightOrigin;
+uniform vec4				u_diffuseColor;
+
+void main( void ) {
+	// compute normalized light direction
+	vec3 L = normalize( u_lightOrigin.xyz - var_Position );
+
+	// compute normal from normal map, move from [0, 1] to [-1, 1] range, normalize
+	vec3 N = normalize( ( 2.0 * texture2D ( u_normalTexture, var_TexNormal.st ).agb ) - 1.0 );
+	N = var_TangentBinormalNormalMatrix * N;
+
+	// compute the diffuse term
+	vec4 diffuse = texture2D( u_diffuseTexture, var_TexDiffuse );
+	diffuse *= u_diffuseColor;
+	diffuse *= max( dot( N, L ), 0.0);
+
+	// compute light projection and falloff
+	vec3 lightFalloff		= texture2D( u_lightFalloffTexture, vec2( var_TexLight.z, 0.5 ) ).rgb;
+	vec3 lightProjection	= texture2DProj( u_lightProjectionTexture, var_TexLight.xyw ).rgb;
+
+	// compute lighting model
+	vec4 color = diffuse;
+	color.rgb *= lightProjection;
+	color.rgb *= lightFalloff;
+	color.rgb *= var_Color.rgb;
+
+	// output final color
+	gl_FragColor = color;
+}
diff --git a/base/gl2progs/ambientInteraction.vs b/base/gl2progs/ambientInteraction.vs
new file mode 100644
index 0000000..65d7a0d
--- /dev/null
+++ b/base/gl2progs/ambientInteraction.vs
@@ -0,0 +1,53 @@
+#version 120
+
+varying vec3				var_Position;
+varying vec2				var_TexDiffuse;
+varying vec2				var_TexNormal;
+varying vec4				var_TexLight;
+varying mat3				var_TangentBinormalNormalMatrix;
+varying vec4				var_Color;
+
+attribute vec4				attr_TexCoord;
+attribute vec3				attr_Tangent;
+attribute vec3				attr_Binormal;
+attribute vec3				attr_Normal;
+
+uniform mat4				u_modelMatrix;
+uniform vec4				u_lightProjectionS;
+uniform vec4				u_lightProjectionT;
+uniform vec4				u_lightProjectionQ;
+uniform vec4				u_lightFalloff;
+uniform vec4				u_bumpMatrixS;
+uniform vec4				u_bumpMatrixT;
+uniform vec4				u_diffuseMatrixS;
+uniform vec4				u_diffuseMatrixT;
+uniform vec4				u_colorModulate;
+uniform vec4				u_colorAdd;
+
+void main( void ) {
+	// transform vertex position into homogenous clip-space
+	gl_Position = ftransform( );
+
+	// transform vertex position into world space
+	var_Position = gl_Vertex.xyz;//( u_modelMatrix * gl_Vertex ).xyz;
+
+	// normal map texgen
+	var_TexNormal.x = dot( u_bumpMatrixS, attr_TexCoord );
+	var_TexNormal.y = dot( u_bumpMatrixT, attr_TexCoord );
+
+	// diffuse map texgen
+	var_TexDiffuse.x = dot( u_diffuseMatrixS, attr_TexCoord );
+	var_TexDiffuse.y = dot( u_diffuseMatrixT, attr_TexCoord );
+
+	// light projection texgen
+	var_TexLight.x = dot( u_lightProjectionS, gl_Vertex );
+	var_TexLight.y = dot( u_lightProjectionT, gl_Vertex );
+	var_TexLight.z = dot( u_lightFalloff, gl_Vertex );
+	var_TexLight.w = dot( u_lightProjectionQ, gl_Vertex );
+
+	// construct tangent-binormal-normal 3x3 matrix
+	var_TangentBinormalNormalMatrix = mat3( attr_Tangent, attr_Binormal, attr_Normal );
+
+	// primary color
+	var_Color = (gl_FrontColor * u_colorModulate) + u_colorAdd;
+}
diff --git a/base/gl2progs/interaction.fs b/base/gl2progs/interaction.fs
new file mode 100644
index 0000000..d9f1ea8
--- /dev/null
+++ b/base/gl2progs/interaction.fs
@@ -0,0 +1,123 @@
+#version 120
+
+varying vec3				var_Position;
+varying vec2				var_TexDiffuse;
+varying vec2				var_TexNormal;
+varying vec2				var_TexSpecular;
+varying vec4				var_TexLight;
+varying mat3				var_TangentBitangentNormalMatrix;
+varying vec4				var_Color;
+
+uniform sampler2D			u_normalTexture;
+uniform sampler2D			u_lightFalloffTexture;
+uniform sampler2D			u_lightProjectionTexture;
+uniform sampler2D			u_diffuseTexture;
+uniform sampler2D			u_specularTexture;
+
+uniform vec4				u_lightOrigin;
+uniform vec4				u_viewOrigin;
+uniform vec4				u_diffuseColor;
+uniform vec4				u_specularColor;
+
+/*
+vec3 SchlickFresnel(vec3 H, vec3 V, float power, vec3 k) {
+	float w = pow(1 - max( dot( H, V ), 0), power);
+	return mix(k, vec3(1,1,1), w);
+}
+*/
+
+float SchlickFresnel(vec3 H, vec3 V, float power, float k) {
+	float w = pow(1 - max( dot( H, V ), 0), power);
+	return w + (1.0-w)*k;
+	//mix(k, 1, w);
+}
+
+// Ward isotropic specular term
+float wardiso( vec3 L, vec3 V, vec3 N, vec3 H, float roughness) {
+	float ndotv = dot( N, V);
+	float ndoth = dot( N, H);
+	float ndotl = dot( N, L);
+	float tandelta		= tan( acos(ndoth));
+	float powtandelta	= pow( tandelta, 2.0);
+	float a				= clamp( (1.0 / sqrt( ndotl * ndotv )), 0, 1 );
+	float powroughness	= pow( roughness, 2 );
+	return max(exp( -( powtandelta / powroughness)) * a / (4 * powroughness), 0);
+}
+
+// Minnaert diffuse term
+float Minnaert( vec3 N, vec3 L, float k) {
+	float ndotl = max(dot(N, L), 0);
+	return pow(ndotl, k);
+}
+
+// lambertian blinn-phong
+float Lambertian( vec3 N, vec3 L) {
+	return max( dot( N, L ) , 0);
+}
+
+// half-lambertian blinn-phong
+float HalfLambertian( vec3 N, vec3 L, float k) {
+	float ndotl = (max(dot( N, L ), 0) + 0.50) / ( 1.0 + 0.5 );
+	return pow(ndotl, k);
+}
+
+vec4 lightingModel( vec4 diffuseTerm, vec3 specularTerm, vec3 L, vec3 V, vec3 N, vec3 H ) {
+#if 0
+	float specularity	= length(specularTerm);
+	float roughness		= (1 - specularity) - 0.25;
+
+	vec3 R = reflect(-L, N); // same as: 2 * N * max(dot(N, L), 0) - L;
+
+	float diffuseIntensity	= HalfLambertian(N, L, 1.5);
+	float fresnelTerm		= SchlickFresnel(H, V, 4, specularity * 2);
+	float specularIntensity = wardiso(L, V, N, H, roughness);
+							  //pow(max(dot( R, V ), 0), 4);
+							  //pow(max((dot( N, H ) - 0.75) * 4, 0), 2) * 2; // the doom3 approximation
+
+	vec4 diffuseReflection  = max( diffuseTerm * diffuseIntensity , 0 );
+	vec3 specularReflection = max( specularTerm * specularIntensity * fresnelTerm, 0 );
+
+	return diffuseReflection + vec4(specularReflection.rgb, 1);
+#else
+	float diffuseIntensity  = Lambertian(N, L);
+	float specularIntensity = pow(max((dot( N, H ) - 0.75) * 4, 0), 2) * 2; // doom3 'adjusted' specular angle
+
+	vec4 diffuseReflection  = max( diffuseTerm  * diffuseIntensity , 0 );
+	vec3 specularReflection = max( specularTerm * specularIntensity, 0 );
+
+	return diffuseReflection + vec4(specularReflection.rgb, 1);
+#endif
+}
+
+void main( void ) {
+	vec3 lightDir	= u_lightOrigin.xyz - var_Position;
+	vec3 viewDir	= u_viewOrigin.xyz - var_Position;
+
+	// compute normalized light, view and half angle vectors
+	vec3 L = normalize( lightDir );
+	vec3 V = normalize( viewDir );
+	vec3 H = normalize( L + V );
+
+	// compute normal from normal map, move from [0, 1] to [-1, 1] range, normalize
+	vec3 N = normalize( ( 2.0 * texture2D ( u_normalTexture, var_TexNormal.st ).wyz ) - 1.0 );
+	N = var_TangentBitangentNormalMatrix * N;
+
+	// compute the diffuse term
+	vec4 diffuse = texture2D( u_diffuseTexture, var_TexDiffuse ) * u_diffuseColor;
+
+	// compute the specular term
+	vec3 specular = texture2D( u_specularTexture, var_TexSpecular ).rgb * u_specularColor.rgb;
+
+	// compute light projection and falloff
+	vec3 lightFalloff		= texture2D( u_lightFalloffTexture, vec2( var_TexLight.z, 0.5 ) ).rgb;
+	vec3 lightProjection	= texture2DProj( u_lightProjectionTexture, var_TexLight.xyw ).rgb;
+
+	// compute lighting model
+	vec4 color = lightingModel(diffuse, specular, L, V, N, H );
+	color.rgb *= lightProjection;
+	color.rgb *= lightFalloff;
+	color.rgb *= var_Color.rgb;
+
+	// output final color
+	gl_FragColor = color;
+}
diff --git a/base/gl2progs/interaction.vs b/base/gl2progs/interaction.vs
new file mode 100644
index 0000000..33d3673
--- /dev/null
+++ b/base/gl2progs/interaction.vs
@@ -0,0 +1,62 @@
+#version 120
+
+varying vec3				var_Position;
+varying vec2				var_TexDiffuse;
+varying vec2				var_TexNormal;
+varying vec2				var_TexSpecular;
+varying vec4				var_TexLight;
+varying mat3				var_TangentBitangentNormalMatrix;
+varying vec4				var_Color;
+
+attribute vec4				attr_TexCoord;
+attribute vec3				attr_Tangent;
+attribute vec3				attr_Bitangent;
+attribute vec3				attr_Normal;
+
+uniform mat4				u_modelMatrix;
+uniform vec4				u_lightProjectionS;
+uniform vec4				u_lightProjectionT;
+uniform vec4				u_lightFalloff;
+uniform vec4				u_lightProjectionQ;
+uniform vec4				u_bumpMatrixS;
+uniform vec4				u_bumpMatrixT;
+uniform vec4				u_diffuseMatrixS;
+uniform vec4				u_diffuseMatrixT;
+uniform vec4				u_specularMatrixS;
+uniform vec4				u_specularMatrixT;
+uniform vec4				u_colorModulate;
+uniform vec4				u_colorAdd;
+uniform vec4				u_lightOrigin;
+uniform vec4				u_viewOrigin;
+
+void main( void ) {
+	// transform vertex position into homogenous clip-space
+	gl_Position = ftransform( );
+
+	// transform vertex position into world space
+	var_Position = gl_Vertex.xyz;//( u_modelMatrix * gl_Vertex ).xyz;
+
+	// normal map texgen
+	var_TexNormal.x = dot( u_bumpMatrixS, attr_TexCoord );
+	var_TexNormal.y = dot( u_bumpMatrixT, attr_TexCoord );
+
+	// diffuse map texgen
+	var_TexDiffuse.x = dot( u_diffuseMatrixS, attr_TexCoord );
+	var_TexDiffuse.y = dot( u_diffuseMatrixT, attr_TexCoord );
+
+	// specular map texgen
+	var_TexSpecular.x = dot( u_specularMatrixS, attr_TexCoord );
+	var_TexSpecular.y = dot( u_specularMatrixT, attr_TexCoord );
+
+	// light projection texgen
+	var_TexLight.x = dot( u_lightProjectionS, gl_Vertex );
+	var_TexLight.y = dot( u_lightProjectionT, gl_Vertex );
+	var_TexLight.z = dot( u_lightFalloff, gl_Vertex );
+	var_TexLight.w = dot( u_lightProjectionQ, gl_Vertex );
+
+	// construct tangent-bitangent-normal 3x3 matrix
+	var_TangentBitangentNormalMatrix = mat3( clamp(attr_Tangent,-1,1), clamp(attr_Bitangent,-1,1), clamp(attr_Normal,-1,1) );
+
+	// primary color
+	var_Color = (gl_FrontColor * u_colorModulate) + u_colorAdd;
+}
diff --git a/base/gl2progs/stencilshadow.fs b/base/gl2progs/stencilshadow.fs
new file mode 100644
index 0000000..d219888
--- /dev/null
+++ b/base/gl2progs/stencilshadow.fs
@@ -0,0 +1,7 @@
+#version 120
+
+varying vec4				var_Color;
+
+void main( void ) {
+	gl_FragColor = var_Color;
+}
diff --git a/base/gl2progs/stencilshadow.vs b/base/gl2progs/stencilshadow.vs
new file mode 100644
index 0000000..4a74df8
--- /dev/null
+++ b/base/gl2progs/stencilshadow.vs
@@ -0,0 +1,19 @@
+#version 120
+
+varying vec4				var_Color;
+
+uniform vec4				u_lightOrigin;
+
+void main( void ) {
+	if( gl_Vertex.w == 1.0 ) {
+		// mvp transform into clip space
+		gl_Position = ftransform();
+	} else {
+		// project vertex position to infinity
+		vec4 vertex = gl_Vertex - u_lightOrigin;
+		gl_Position = gl_ModelViewProjectionMatrix * vertex;
+	}
+
+	// primary color
+	var_Color = gl_Color;
+}
diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt
index 69c8e47..46a90a6 100644
--- a/neo/CMakeLists.txt
+++ b/neo/CMakeLists.txt
@@ -272,6 +272,7 @@ set(src_renderer
 	renderer/VertexCache.cpp
 	renderer/draw_arb.cpp
 	renderer/draw_arb2.cpp
+	renderer/draw_glsl.cpp
 	renderer/draw_common.cpp
 	renderer/draw_nv10.cpp
 	renderer/draw_nv20.cpp
diff --git a/neo/renderer/RenderSystem.cpp b/neo/renderer/RenderSystem.cpp
index 22c7db1..f0729f2 100644
--- a/neo/renderer/RenderSystem.cpp
+++ b/neo/renderer/RenderSystem.cpp
@@ -553,6 +553,10 @@ void idRenderSystemLocal::SetBackEndRenderer() {
 		if ( glConfig.allowARB2Path ) {
 			backEndRenderer = BE_ARB2;
 		}
+	} else if ( idStr::Icmp( r_renderer.GetString(), "glsl" ) == 0 ) {
+		if ( glConfig.allowGLSLPath ) {
+			backEndRenderer = BE_GLSL;
+		}
 	} else if ( idStr::Icmp( r_renderer.GetString(), "nv10" ) == 0 ) {
 		if ( glConfig.allowNV10Path ) {
 			backEndRenderer = BE_NV10;
@@ -607,6 +611,11 @@ void idRenderSystemLocal::SetBackEndRenderer() {
 		backEndRendererHasVertexPrograms = true;
 		backEndRendererMaxLight = 999;
 		break;
+	case BE_GLSL:
+		common->Printf( "using GLSL renderSystem\n" );
+		backEndRendererHasVertexPrograms = true;
+		backEndRendererMaxLight = 999;
+		break;
 	default:
 		common->FatalError( "SetbackEndRenderer: bad back end" );
 	}
diff --git a/neo/renderer/RenderSystem.h b/neo/renderer/RenderSystem.h
index 79609b5..ed0aad5 100644
--- a/neo/renderer/RenderSystem.h
+++ b/neo/renderer/RenderSystem.h
@@ -79,6 +79,7 @@ typedef struct glconfig_s {
 	bool				twoSidedStencilAvailable;
 	bool				textureNonPowerOfTwoAvailable;
 	bool				depthBoundsTestAvailable;
+	bool				GLSLAvailable;
 
 	// ati r200 extensions
 	bool				atiFragmentShaderAvailable;
@@ -97,6 +98,7 @@ typedef struct glconfig_s {
 	bool				allowNV10Path;
 	bool				allowR200Path;
 	bool				allowARB2Path;
+	bool				allowGLSLPath;
 
 	bool				isInitialized;
 } glconfig_t;
diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp
index 7fdb1c2..2128245 100644
--- a/neo/renderer/RenderSystem_init.cpp
+++ b/neo/renderer/RenderSystem_init.cpp
@@ -49,7 +49,7 @@ If you have questions concerning this license or the applicable additional terms
 
 glconfig_t	glConfig;
 
-const char *r_rendererArgs[] = { "best", "arb", "arb2", "nv10", "nv20", "r200", NULL };
+const char *r_rendererArgs[] = { "best", "arb", "arb2", "glsl", "nv10", "nv20", "r200", NULL };
 
 idCVar r_inhibitFragmentProgram( "r_inhibitFragmentProgram", "0", CVAR_RENDERER | CVAR_BOOL, "ignore the fragment program extension" );
 idCVar r_useLightPortalFlow( "r_useLightPortalFlow", "1", CVAR_RENDERER | CVAR_BOOL, "use a more precise area reference determination" );
@@ -312,6 +312,50 @@ PFNGLPROGRAMLOCALPARAMETER4FVARBPROC	qglProgramLocalParameter4fvARB;
 // GL_EXT_depth_bounds_test
 PFNGLDEPTHBOUNDSEXTPROC                 qglDepthBoundsEXT;
 
+// GL_ARB_shading_language_100
+void ( APIENTRY * qglDeleteObjectARB )( GLhandleARB obj );
+GLhandleARB ( APIENTRY * qglGetHandleARB )( GLenum pname );
+void ( APIENTRY * qglDetachObjectARB )( GLhandleARB containerObj, GLhandleARB attachedObj );
+GLhandleARB ( APIENTRY * qglCreateShaderObjectARB )( GLenum shaderType );
+void ( APIENTRY * qglShaderSourceARB )( GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length ); void ( APIENTRY * qglCompileShaderARB )( GLhandleARB shaderObj );
+GLhandleARB ( APIENTRY * qglCreateProgramObjectARB )( void );
+void ( APIENTRY * qglAttachObjectARB )( GLhandleARB containerObj, GLhandleARB obj );
+void ( APIENTRY * qglLinkProgramARB )( GLhandleARB programObj );
+void ( APIENTRY * qglUseProgramObjectARB )( GLhandleARB programObj );
+void ( APIENTRY * qglValidateProgramARB )( GLhandleARB programObj );
+void ( APIENTRY * qglUniform1fARB )( GLint location, GLfloat v0 );
+void ( APIENTRY * qglUniform2fARB )( GLint location, GLfloat v0, GLfloat v1 );
+void ( APIENTRY * qglUniform3fARB )( GLint location, GLfloat v0, GLfloat v1, GLfloat v2 );
+void ( APIENTRY * qglUniform4fARB )( GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 );
+void ( APIENTRY * qglUniform1iARB )( GLint location, GLint v0 );
+void ( APIENTRY * qglUniform2iARB )( GLint location, GLint v0, GLint v1 );
+void ( APIENTRY * qglUniform3iARB )( GLint location, GLint v0, GLint v1, GLint v2 );
+void ( APIENTRY * qglUniform4iARB )( GLint location, GLint v0, GLint v1, GLint v2, GLint v3 );
+void ( APIENTRY * qglUniform2fvARB )( GLint location, GLsizei count, const GLfloat *value );
+void ( APIENTRY * qglUniform3fvARB )( GLint location, GLsizei count, const GLfloat *value );
+void ( APIENTRY * qglUniform4fvARB )( GLint location, GLsizei count, const GLfloat *value );
+void ( APIENTRY * qglUniform2ivARB )( GLint location, GLsizei count, const GLint *value );
+void ( APIENTRY * qglUniform3ivARB )( GLint location, GLsizei count, const GLint *value );
+void ( APIENTRY * qglUniform4ivARB )( GLint location, GLsizei count, const GLint *value );
+void ( APIENTRY * qglUniformMatrix2fvARB )( GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );
+void ( APIENTRY * qglUniformMatrix3fvARB )( GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );
+void ( APIENTRY * qglUniformMatrix4fvARB )( GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );
+void ( APIENTRY * qglGetObjectParameterfvARB )( GLhandleARB obj, GLenum pname, GLfloat *params );
+void ( APIENTRY * qglGetObjectParameterivARB )( GLhandleARB obj, GLenum pname, GLint *params );
+void ( APIENTRY * qglGetInfoLogARB )( GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog );
+void ( APIENTRY * qglGetAttachedObjectsARB )( GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj );
+GLint ( APIENTRY * qglGetUniformLocationARB )( GLhandleARB programObj, const GLcharARB *name );
+void ( APIENTRY * qglGetActiveUniformARB )( GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name );
+void ( APIENTRY * qglGetUniformfvARB )( GLhandleARB programObj, GLint location, GLfloat *params );
+void ( APIENTRY * qglGetUniformivARB )( GLhandleARB programObj, GLint location, GLint *params );
+void ( APIENTRY * qglGetShaderSourceARB )( GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source );
+void ( APIENTRY * qglGetShaderiv )(GLuint shader, GLenum pname, GLint* param);
+void ( APIENTRY * qglDeleteShader )(GLuint shader);
+void ( APIENTRY * qglGetShaderInfoLog )(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+void ( APIENTRY * qglBindAttribLocationARB )( GLhandleARB programObj, GLuint index, const GLcharARB *name );
+void ( APIENTRY * qglGetActiveAttribARB )( GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name );
+GLint ( APIENTRY * qglGetAttribLocationARB )( GLhandleARB programObj, const GLcharARB *name );
+
 /*
 =================
 R_CheckExtension
@@ -537,6 +581,53 @@ static void R_CheckPortableExtensions( void ) {
 		qglDepthBoundsEXT = (PFNGLDEPTHBOUNDSEXTPROC)GLimp_ExtensionPointer( "glDepthBoundsEXT" );
 	}
 
+	// GL_ARB_shading_language_100
+	glConfig.GLSLAvailable = R_CheckExtension( "GL_ARB_shading_language_100" );
+	if (glConfig.GLSLAvailable) {
+		qglDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)GLimp_ExtensionPointer( "glDeleteObjectARB" );
+		qglGetHandleARB = (PFNGLGETHANDLEARBPROC)GLimp_ExtensionPointer( "glGetHandleARB" );
+		qglDetachObjectARB = (PFNGLDETACHOBJECTARBPROC)GLimp_ExtensionPointer( "glDetachObjectARB" );
+		qglCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)GLimp_ExtensionPointer( "glCreateShaderObjectARB" );
+		qglShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)GLimp_ExtensionPointer( "glShaderSourceARB" );
+		qglCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)GLimp_ExtensionPointer( "glCompileShaderARB" );
+		qglCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)GLimp_ExtensionPointer( "glCreateProgramObjectARB" );
+		qglAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)GLimp_ExtensionPointer( "glAttachObjectARB" );
+		qglLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)GLimp_ExtensionPointer( "glLinkProgramARB" );
+		qglUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)GLimp_ExtensionPointer( "glUseProgramObjectARB" );
+		qglValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC)GLimp_ExtensionPointer( "glValidateProgramARB" );
+		qglUniform1fARB = (PFNGLUNIFORM1FARBPROC)GLimp_ExtensionPointer( "glUniform1fARB" );
+		qglUniform2fARB = (PFNGLUNIFORM2FARBPROC)GLimp_ExtensionPointer( "glUniform2fARB" );
+		qglUniform3fARB = (PFNGLUNIFORM3FARBPROC)GLimp_ExtensionPointer( "glUniform3fARB" );
+		qglUniform4fARB = (PFNGLUNIFORM4FARBPROC)GLimp_ExtensionPointer( "glUniform4fARB" );
+		qglUniform1iARB = (PFNGLUNIFORM1IARBPROC)GLimp_ExtensionPointer( "glUniform1iARB" );
+		qglUniform2iARB = (PFNGLUNIFORM2IARBPROC)GLimp_ExtensionPointer( "glUniform2iARB" );
+		qglUniform3iARB = (PFNGLUNIFORM3IARBPROC)GLimp_ExtensionPointer( "glUniform3iARB" );
+		qglUniform4iARB = (PFNGLUNIFORM4IARBPROC)GLimp_ExtensionPointer( "glUniform4iARB" );
+		qglUniform2fvARB = (PFNGLUNIFORM2FVARBPROC)GLimp_ExtensionPointer( "glUniform2fvARB" );
+		qglUniform3fvARB = (PFNGLUNIFORM3FVARBPROC)GLimp_ExtensionPointer( "glUniform3fvARB" );
+		qglUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)GLimp_ExtensionPointer( "glUniform4fvARB" );
+		qglUniform2ivARB = (PFNGLUNIFORM2IVARBPROC)GLimp_ExtensionPointer( "glUniform2ivARB" );
+		qglUniform3ivARB = (PFNGLUNIFORM3IVARBPROC)GLimp_ExtensionPointer( "glUniform3ivARB" );
+		qglUniform4ivARB = (PFNGLUNIFORM4IVARBPROC)GLimp_ExtensionPointer( "glUniform4ivARB" );
+		qglUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC)GLimp_ExtensionPointer( "glUniformMatrix2fvARB" );
+		qglUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC)GLimp_ExtensionPointer( "glUniformMatrix3fvARB" );
+		qglUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC)GLimp_ExtensionPointer( "glUniformMatrix4fvARB" );
+		qglGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC)GLimp_ExtensionPointer( "glGetObjectParameterfvARB" );
+		qglGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)GLimp_ExtensionPointer( "glGetObjectParameterivARB" );
+		qglGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)GLimp_ExtensionPointer( "glGetInfoLogARB" );
+		qglGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC)GLimp_ExtensionPointer( "glGetAttachedObjectsARB" );
+		qglGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)GLimp_ExtensionPointer( "glGetUniformLocationARB" );
+		qglGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC)GLimp_ExtensionPointer( "glGetActiveUniformARB" );
+		qglGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC)GLimp_ExtensionPointer( "glGetUniformfvARB" );
+		qglGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC)GLimp_ExtensionPointer( "glGetUniformivARB" );
+		qglGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC)GLimp_ExtensionPointer( "glGetShaderSourceARB" );
+		qglBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC)GLimp_ExtensionPointer( "glBindAttribLocationARB" );
+		qglGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC)GLimp_ExtensionPointer( "glGetActiveAttribARB" );
+		qglGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)GLimp_ExtensionPointer( "glGetAttribLocationARB" );
+		qglGetShaderiv = (PFNGLGETSHADERIVPROC)GLimp_ExtensionPointer( "glGetShaderiv" );
+		qglDeleteShader = (PFNGLDELETESHADERPROC)GLimp_ExtensionPointer( "glDeleteShader" );
+		qglGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)GLimp_ExtensionPointer( "glGetShaderInfoLog" );
+	}
 }
 
 
@@ -689,9 +780,12 @@ void R_InitOpenGL( void ) {
 	R_NV20_Init();
 	R_R200_Init();
 	R_ARB2_Init();
+	R_GLSL_Init();
 
 	cmdSystem->AddCommand( "reloadARBprograms", R_ReloadARBPrograms_f, CMD_FL_RENDERER, "reloads ARB programs" );
+	cmdSystem->AddCommand( "reloadGLSLprograms", R_ReloadGLSLPrograms_f, CMD_FL_RENDERER, "reloads GLSL programs" );
 	R_ReloadARBPrograms_f( idCmdArgs() );
+	R_ReloadGLSLPrograms_f( idCmdArgs() );
 
 	// allocate the vertex array range or vertex objects
 	vertexCache.Init();
@@ -1820,6 +1914,12 @@ static void GfxInfo_f( const idCmdArgs &args ) {
 		common->Printf( "ARB2 path disabled\n" );
 	}
 
+	if ( glConfig.allowGLSLPath ) {
+		common->Printf( "GLSL path ENABLED%s\n", active[tr.backEndRenderer == BE_GLSL] );
+	} else {
+		common->Printf( "GLSL path disabled\n" );
+	}
+
 	//=============================
 
 	common->Printf( "-------\n" );
diff --git a/neo/renderer/draw_common.cpp b/neo/renderer/draw_common.cpp
index 7083c86..325ee73 100644
--- a/neo/renderer/draw_common.cpp
+++ b/neo/renderer/draw_common.cpp
@@ -1057,7 +1057,11 @@ static void RB_T_Shadow( const drawSurf_t *surf ) {
 
 		R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.vLight->globalLightOrigin, localLight.ToVec3() );
 		localLight.w = 0.0f;
-		qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, localLight.ToFloatPtr() );
+
+		if ( tr.backEndRenderer == BE_GLSL )
+			qglUniform4fvARB( stencilShadowShader.localLightOrigin, 1, localLight.ToFloatPtr() );
+		else
+			qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, localLight.ToFloatPtr() );
 	}
 
 	tri = surf->geo;
@@ -1678,6 +1682,9 @@ void	RB_STD_DrawView( void ) {
 	case BE_ARB2:
 		RB_ARB2_DrawInteractions();
 		break;
+	case BE_GLSL:
+		RB_GLSL_DrawInteractions();
+		break;
 	case BE_NV20:
 		RB_NV20_DrawInteractions();
 		break;
diff --git a/neo/renderer/draw_glsl.cpp b/neo/renderer/draw_glsl.cpp
new file mode 100644
index 0000000..4b7abaa
--- /dev/null
+++ b/neo/renderer/draw_glsl.cpp
@@ -0,0 +1,569 @@
+/*
+===========================================================================
+
+Doom 3 GPL Source Code
+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
+
+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
+
+Doom 3 Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Doom 3 Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+
+===========================================================================
+*/
+
+#include "sys/platform.h"
+#include "renderer/VertexCache.h"
+
+#include "renderer/tr_local.h"
+
+shaderProgram_t		interactionShader = { -1 };
+shaderProgram_t		ambientInteractionShader = { -1 };
+shaderProgram_t		stencilShadowShader = { -1 };
+
+static void GLSL_SelectTextureNoClient(int unit) {
+	backEnd.glState.currenttmu = unit;
+	qglActiveTextureARB(GL_TEXTURE0_ARB + unit);
+}
+
+static void GLSL_DrawInteraction(const drawInteraction_t *din) {
+	// load all the shader parameters
+	if (din->ambientLight) {
+		qglUniform4fvARB(ambientInteractionShader.localLightOrigin, 1, din->localLightOrigin.ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.lightProjectionS, 1, din->lightProjection[0].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.lightProjectionT, 1, din->lightProjection[1].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.lightProjectionQ, 1, din->lightProjection[2].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.lightFalloff, 1, din->lightProjection[3].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.bumpMatrixS, 1, din->bumpMatrix[0].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.bumpMatrixT, 1, din->bumpMatrix[1].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.diffuseMatrixS, 1, din->diffuseMatrix[0].ToFloatPtr());
+		qglUniform4fvARB(ambientInteractionShader.diffuseMatrixT, 1, din->diffuseMatrix[1].ToFloatPtr());
+
+		static const float zero[4] = { 0, 0, 0, 0 };
+		static const float one[4] = { 1, 1, 1, 1 };
+		static const float negOne[4] = { -1, -1, -1, -1 };
+
+		switch (din->vertexColor) {
+		case SVC_IGNORE:
+			qglUniform4fARB(ambientInteractionShader.colorModulate, zero[0], zero[1], zero[2], zero[3]);
+			qglUniform4fARB(ambientInteractionShader.colorAdd, one[0], one[1], one[2], one[3]);
+			break;
+		case SVC_MODULATE:
+			qglUniform4fARB(ambientInteractionShader.colorModulate, one[0], one[1], one[2], one[3]);
+			qglUniform4fARB(ambientInteractionShader.colorAdd, zero[0], zero[1], zero[2], zero[3]);
+			break;
+		case SVC_INVERSE_MODULATE:
+			qglUniform4fARB(ambientInteractionShader.colorModulate, negOne[0], negOne[1], negOne[2], negOne[3]);
+			qglUniform4fARB(ambientInteractionShader.colorAdd, one[0], one[1], one[2], one[3]);
+			break;
+		}
+
+		// set the constant color
+		qglUniform4fvARB(ambientInteractionShader.diffuseColor, 1, din->diffuseColor.ToFloatPtr());
+	} else {
+		qglUniform4fvARB(interactionShader.localLightOrigin, 1, din->localLightOrigin.ToFloatPtr());
+		qglUniform4fvARB(interactionShader.localViewOrigin, 1, din->localViewOrigin.ToFloatPtr());
+		qglUniform4fvARB(interactionShader.lightProjectionS, 1, din->lightProjection[0].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.lightProjectionT, 1, din->lightProjection[1].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.lightProjectionQ, 1, din->lightProjection[2].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.lightFalloff, 1, din->lightProjection[3].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.bumpMatrixS, 1, din->bumpMatrix[0].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.bumpMatrixT, 1, din->bumpMatrix[1].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.diffuseMatrixS, 1, din->diffuseMatrix[0].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.diffuseMatrixT, 1, din->diffuseMatrix[1].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.specularMatrixS, 1, din->specularMatrix[0].ToFloatPtr());
+		qglUniform4fvARB(interactionShader.specularMatrixT, 1, din->specularMatrix[1].ToFloatPtr());
+
+		static const float zero[4] = { 0, 0, 0, 0 };
+		static const float one[4] = { 1, 1, 1, 1 };
+		static const float negOne[4] = { -1, -1, -1, -1 };
+
+		switch (din->vertexColor) {
+		case SVC_IGNORE:
+			qglUniform4fARB(interactionShader.colorModulate, zero[0], zero[1], zero[2], zero[3]);
+			qglUniform4fARB(interactionShader.colorAdd, one[0], one[1], one[2], one[3]);
+			break;
+		case SVC_MODULATE:
+			qglUniform4fARB(interactionShader.colorModulate, one[0], one[1], one[2], one[3]);
+			qglUniform4fARB(interactionShader.colorAdd, zero[0], zero[1], zero[2], zero[3]);
+			break;
+		case SVC_INVERSE_MODULATE:
+			qglUniform4fARB(interactionShader.colorModulate, negOne[0], negOne[1], negOne[2], negOne[3]);
+			qglUniform4fARB(interactionShader.colorAdd, one[0], one[1], one[2], one[3]);
+			break;
+		}
+
+		// set the constant colors
+		qglUniform4fvARB(interactionShader.diffuseColor, 1, din->diffuseColor.ToFloatPtr());
+		qglUniform4fvARB(interactionShader.specularColor, 1, din->specularColor.ToFloatPtr());
+	}
+
+	// set the textures
+
+	// texture 0 will be the per-surface bump map
+	GLSL_SelectTextureNoClient(0);
+	din->bumpImage->Bind();
+
+	// texture 1 will be the light falloff texture
+	GLSL_SelectTextureNoClient(1);
+	din->lightFalloffImage->Bind();
+
+	// texture 2 will be the light projection texture
+	GLSL_SelectTextureNoClient(2);
+	din->lightImage->Bind();
+
+	// texture 3 is the per-surface diffuse map
+	GLSL_SelectTextureNoClient(3);
+	din->diffuseImage->Bind();
+
+	if (!din->ambientLight) {
+		// texture 4 is the per-surface specular map
+		GLSL_SelectTextureNoClient(4);
+		din->specularImage->Bind();
+	}
+
+	// draw it
+	RB_DrawElementsWithCounters(din->surf->geo);
+}
+
+static void GLSL_CreateDrawInteractions(const drawSurf_t *surf) {
+	if (!surf)
+		return;
+
+	// perform setup here that will be constant for all interactions
+	GL_State(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc);
+
+	// bind the vertex and fragment program
+	if (backEnd.vLight->lightShader->IsAmbientLight()) {
+		if (ambientInteractionShader.program == -1)
+			qglUseProgramObjectARB(0);
+		else
+			qglUseProgramObjectARB(ambientInteractionShader.program);
+	} else {
+		if (interactionShader.program == -1)
+			qglUseProgramObjectARB(0);
+		else
+			qglUseProgramObjectARB(interactionShader.program);
+	}
+
+	// enable the vertex arrays
+	qglEnableVertexAttribArrayARB(8);
+	qglEnableVertexAttribArrayARB(9);
+	qglEnableVertexAttribArrayARB(10);
+	qglEnableVertexAttribArrayARB(11);
+	qglEnableClientState(GL_COLOR_ARRAY);
+
+	for (; surf; surf = surf->nextOnLight) {
+		// set the vertex pointers
+		idDrawVert	*ac = (idDrawVert *)vertexCache.Position(surf->geo->ambientCache);
+		qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(idDrawVert), ac->color);
+		qglVertexAttribPointerARB(11, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->normal.ToFloatPtr());
+		qglVertexAttribPointerARB(10, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->tangents[1].ToFloatPtr());
+		qglVertexAttribPointerARB(9, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->tangents[0].ToFloatPtr());
+		qglVertexAttribPointerARB(8, 2, GL_FLOAT, false, sizeof(idDrawVert), ac->st.ToFloatPtr());
+		qglVertexPointer(3, GL_FLOAT, sizeof(idDrawVert), ac->xyz.ToFloatPtr());
+
+		// set model matrix
+		if (backEnd.vLight->lightShader->IsAmbientLight())
+			qglUniformMatrix4fvARB(ambientInteractionShader.modelMatrix, 1, false, surf->space->modelMatrix);
+		else
+			qglUniformMatrix4fvARB(interactionShader.modelMatrix, 1, false, surf->space->modelMatrix);
+
+		// this may cause GLSL_DrawInteraction to be executed multiple
+		// times with different colors and images if the surface or light have multiple layers
+		RB_CreateSingleDrawInteractions(surf, GLSL_DrawInteraction);
+	}
+
+	qglDisableVertexAttribArrayARB(8);
+	qglDisableVertexAttribArrayARB(9);
+	qglDisableVertexAttribArrayARB(10);
+	qglDisableVertexAttribArrayARB(11);
+	qglDisableClientState(GL_COLOR_ARRAY);
+
+	// disable features
+	GLSL_SelectTextureNoClient(4);
+	globalImages->BindNull();
+
+	GLSL_SelectTextureNoClient(3);
+	globalImages->BindNull();
+
+	GLSL_SelectTextureNoClient(2);
+	globalImages->BindNull();
+
+	GLSL_SelectTextureNoClient(1);
+	globalImages->BindNull();
+
+	backEnd.glState.currenttmu = -1;
+	GL_SelectTexture(0);
+
+	qglUseProgramObjectARB(0);
+}
+
+/*
+==================
+RB_GLSL_DrawInteractions
+==================
+*/
+void RB_GLSL_DrawInteractions(void) {
+	viewLight_t *vLight;
+
+	GL_SelectTexture(0);
+	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+	// for each light, perform adding and shadowing
+	for (vLight = backEnd.viewDef->viewLights; vLight ;vLight = vLight->next) {
+		backEnd.vLight = vLight;
+
+		// do fogging later
+		if (vLight->lightShader->IsFogLight())
+			continue;
+
+		if (vLight->lightShader->IsBlendLight())
+			continue;
+
+		// if there are no interactions, get out!
+		if (!vLight->localInteractions &&
+			!vLight->globalInteractions &&
+			!vLight->translucentInteractions) {
+			continue;
+		}
+
+		// clear the stencil buffer if needed
+		if (vLight->globalShadows || vLight->localShadows) {
+			backEnd.currentScissor = vLight->scissorRect;
+
+			if (r_useScissor.GetBool()) {
+				qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
+					backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
+					backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
+					backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
+			}
+
+			qglClear(GL_STENCIL_BUFFER_BIT);
+		} else {
+			// no shadows, so no need to read or write the stencil buffer
+			// we might in theory want to use GL_ALWAYS instead of disabling
+			// completely, to satisfy the invarience rules
+			qglStencilFunc(GL_ALWAYS, 128, 255);
+		}
+
+		if (r_useShadowVertexProgram.GetBool()) {
+			qglUseProgramObjectARB(stencilShadowShader.program);
+
+			RB_StencilShadowPass(vLight->globalShadows);
+			GLSL_CreateDrawInteractions(vLight->localInteractions);
+
+			qglUseProgramObjectARB(stencilShadowShader.program);
+			RB_StencilShadowPass(vLight->localShadows);
+			GLSL_CreateDrawInteractions(vLight->globalInteractions);
+
+			// if there weren't any globalInteractions, it would have stayed on
+			qglUseProgramObjectARB(0);
+		} else {
+			RB_StencilShadowPass(vLight->globalShadows);
+			GLSL_CreateDrawInteractions(vLight->localInteractions);
+
+			RB_StencilShadowPass(vLight->localShadows);
+			GLSL_CreateDrawInteractions(vLight->globalInteractions);
+		}
+
+		// translucent surfaces never get stencil shadowed
+		if (r_skipTranslucent.GetBool())
+			continue;
+
+		qglStencilFunc(GL_ALWAYS, 128, 255);
+
+		backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
+		GLSL_CreateDrawInteractions(vLight->translucentInteractions);
+		backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
+	}
+
+	// disable stencil shadow test
+	qglStencilFunc(GL_ALWAYS, 128, 255);
+
+	GL_SelectTexture(0);
+	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+static void GLSL_DeleteShader(GLhandleARB *shader) {
+	if (*shader != -1) {
+		qglDeleteShader(*shader);
+		*shader = -1;
+	}
+}
+
+static bool GLSL_LoadShader(const char *path, GLhandleARB *shader, GLenum type) {
+	void *buf;
+
+	if (!glConfig.isInitialized)
+		return false;
+
+	GLSL_DeleteShader(shader);
+
+	// load the program even if we don't support it, so
+	// fs_copyfiles can generate cross-platform data dumps
+	fileSystem->ReadFile(path, &buf, NULL);
+	if (!buf) {
+		common->Printf("%s: File not found\n", path);
+		return false;
+	}
+
+	*shader = qglCreateShaderObjectARB( type );
+	qglShaderSourceARB(*shader, 1, (const GLcharARB **)&buf, 0);
+	qglCompileShaderARB(*shader);
+
+	fileSystem->FreeFile( buf );
+
+	GLint logLength;
+	qglGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
+	if (logLength > 1) {
+		GLchar *log = (GLchar *)malloc(logLength);
+		qglGetShaderInfoLog(*shader, logLength, &logLength, log);
+		common->Printf("glGetShaderInfoLog: %s\n", log);
+		free(log);
+	}
+
+	GLint status;
+	qglGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
+	if (status == 0) {
+		GLSL_DeleteShader(shader);
+		return false;
+	}
+
+	return true;
+}
+
+static void GLSL_DeleteProgram(shaderProgram_t *shaderProgram) {
+	qglDetachObjectARB(shaderProgram->program, shaderProgram->fragmentShader);
+	qglDetachObjectARB(shaderProgram->program, shaderProgram->vertexShader);
+
+	GLSL_DeleteShader(&shaderProgram->vertexShader);
+	GLSL_DeleteShader(&shaderProgram->fragmentShader);
+}
+
+static bool GLSL_LinkProgram(shaderProgram_t *shaderProgram, bool needsAttributes) {
+	GLint linked;
+
+	shaderProgram->program = qglCreateProgramObjectARB( );
+
+	qglAttachObjectARB(shaderProgram->program, shaderProgram->vertexShader);
+	qglAttachObjectARB(shaderProgram->program, shaderProgram->fragmentShader);
+
+	if (needsAttributes) {
+		qglBindAttribLocationARB(shaderProgram->program, 8, "attr_TexCoord");
+		qglBindAttribLocationARB(shaderProgram->program, 9, "attr_Tangent");
+		qglBindAttribLocationARB(shaderProgram->program, 10, "attr_Bitangent");
+		qglBindAttribLocationARB(shaderProgram->program, 11, "attr_Normal");
+	}
+
+	qglLinkProgramARB( shaderProgram->program );
+
+	qglGetObjectParameterivARB( shaderProgram->program, GL_OBJECT_LINK_STATUS_ARB, &linked );
+
+	if (!linked) {
+		GLSL_DeleteProgram(shaderProgram);
+		return false;
+	}
+
+	return true;
+}
+
+static bool GLSL_ValidateProgram(shaderProgram_t *shaderProgram) {
+	GLint validProgram;
+
+	qglValidateProgramARB(shaderProgram->program);
+	qglGetObjectParameterivARB(shaderProgram->program, GL_OBJECT_VALIDATE_STATUS_ARB, &validProgram);
+
+	if (!validProgram) {
+		GLSL_DeleteProgram(shaderProgram);
+		return false;
+	}
+
+	return true;
+}
+
+static bool GLSL_LoadProgram(const char *basename, shaderProgram_t *shaderProgram, bool needsAttributes) {
+	char path[1024];
+
+	common->Printf("%s\n", basename);
+
+	GLSL_DeleteProgram(shaderProgram);
+
+	idStr::snPrintf(path, sizeof(path), "gl2progs/%s.vs", basename);
+	if (!GLSL_LoadShader(path, &shaderProgram->vertexShader, GL_VERTEX_SHADER_ARB)) {
+		GLSL_DeleteProgram(shaderProgram);
+		return false;
+	}
+
+	idStr::snPrintf(path, sizeof(path), "gl2progs/%s.fs", basename);
+	if (!GLSL_LoadShader(path, &shaderProgram->fragmentShader, GL_FRAGMENT_SHADER_ARB)) {
+		GLSL_DeleteShader(&shaderProgram->vertexShader);
+		return false;
+	}
+
+	if (!GLSL_LinkProgram(shaderProgram, needsAttributes)) {
+		common->Printf("program failed to linkn\n" );
+		return false;
+	}
+
+	if (!GLSL_ValidateProgram(shaderProgram)) {
+		common->Printf("program is invalid.\n" );
+		return false;
+	}
+
+	return true;
+}
+
+static void DeleteAllGLSLPrograms() {
+	GLSL_DeleteProgram(&interactionShader);
+	GLSL_DeleteProgram(&ambientInteractionShader);
+	GLSL_DeleteProgram(&stencilShadowShader);
+}
+
+static bool InitGLSLShaders() {
+	DeleteAllGLSLPrograms();
+
+	// load interation shaders
+	if (!GLSL_LoadProgram("interaction", &interactionShader, true)) {
+		common->Printf("GLSL interactionShader unavailable.\n");
+		return false;
+	}
+
+	// set uniform locations
+	interactionShader.u_normalTexture = qglGetUniformLocationARB(interactionShader.program, "u_normalTexture");
+	interactionShader.u_lightFalloffTexture = qglGetUniformLocationARB(interactionShader.program, "u_lightFalloffTexture");
+	interactionShader.u_lightProjectionTexture = qglGetUniformLocationARB(interactionShader.program, "u_lightProjectionTexture");
+	interactionShader.u_diffuseTexture = qglGetUniformLocationARB(interactionShader.program, "u_diffuseTexture");
+	interactionShader.u_specularTexture = qglGetUniformLocationARB(interactionShader.program, "u_specularTexture");
+
+	interactionShader.modelMatrix = qglGetUniformLocationARB(interactionShader.program, "u_modelMatrix");
+
+	interactionShader.localLightOrigin = qglGetUniformLocationARB(interactionShader.program, "u_lightOrigin");
+	interactionShader.localViewOrigin = qglGetUniformLocationARB(interactionShader.program, "u_viewOrigin");
+	interactionShader.lightProjectionS = qglGetUniformLocationARB(interactionShader.program, "u_lightProjectionS");
+	interactionShader.lightProjectionT = qglGetUniformLocationARB(interactionShader.program, "u_lightProjectionT");
+	interactionShader.lightProjectionQ = qglGetUniformLocationARB(interactionShader.program, "u_lightProjectionQ");
+	interactionShader.lightFalloff = qglGetUniformLocationARB(interactionShader.program, "u_lightFalloff");
+
+	interactionShader.bumpMatrixS = qglGetUniformLocationARB(interactionShader.program, "u_bumpMatrixS");
+	interactionShader.bumpMatrixT = qglGetUniformLocationARB(interactionShader.program, "u_bumpMatrixT");
+	interactionShader.diffuseMatrixS = qglGetUniformLocationARB(interactionShader.program, "u_diffuseMatrixS");
+	interactionShader.diffuseMatrixT = qglGetUniformLocationARB(interactionShader.program, "u_diffuseMatrixT");
+	interactionShader.specularMatrixS = qglGetUniformLocationARB(interactionShader.program, "u_specularMatrixS");
+	interactionShader.specularMatrixT = qglGetUniformLocationARB(interactionShader.program, "u_specularMatrixT");
+
+	interactionShader.colorModulate = qglGetUniformLocationARB(interactionShader.program, "u_colorModulate");
+	interactionShader.colorAdd = qglGetUniformLocationARB(interactionShader.program, "u_colorAdd");
+
+	interactionShader.diffuseColor = qglGetUniformLocationARB(interactionShader.program, "u_diffuseColor");
+	interactionShader.specularColor = qglGetUniformLocationARB(interactionShader.program, "u_specularColor");
+
+	// set texture locations
+	qglUseProgramObjectARB(interactionShader.program);
+	qglUniform1iARB(interactionShader.u_normalTexture, 0);
+	qglUniform1iARB(interactionShader.u_lightFalloffTexture, 1);
+	qglUniform1iARB(interactionShader.u_lightProjectionTexture, 2);
+	qglUniform1iARB(interactionShader.u_diffuseTexture, 3);
+	qglUniform1iARB(interactionShader.u_specularTexture, 4);
+	qglUseProgramObjectARB(0);
+
+	// load ambient interation shaders
+	if (!GLSL_LoadProgram("ambientInteraction", &ambientInteractionShader, true)) {
+		common->Printf("GLSL ambientInteractionShader unavailable.\n");
+		DeleteAllGLSLPrograms();
+		return false;
+	}
+
+	// set uniform locations
+	ambientInteractionShader.u_normalTexture = qglGetUniformLocationARB(ambientInteractionShader.program, "u_normalTexture");
+	ambientInteractionShader.u_lightFalloffTexture = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightFalloffTexture");
+	ambientInteractionShader.u_lightProjectionTexture = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightProjectionTexture");
+	ambientInteractionShader.u_diffuseTexture = qglGetUniformLocationARB(ambientInteractionShader.program, "u_diffuseTexture");
+
+	ambientInteractionShader.modelMatrix = qglGetUniformLocationARB(ambientInteractionShader.program, "u_modelMatrix");
+
+	ambientInteractionShader.localLightOrigin = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightOrigin");
+	ambientInteractionShader.lightProjectionS = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightProjectionS");
+	ambientInteractionShader.lightProjectionT = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightProjectionT");
+	ambientInteractionShader.lightProjectionQ = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightProjectionQ");
+	ambientInteractionShader.lightFalloff = qglGetUniformLocationARB(ambientInteractionShader.program, "u_lightFalloff");
+
+	ambientInteractionShader.bumpMatrixS = qglGetUniformLocationARB(ambientInteractionShader.program, "u_bumpMatrixS");
+	ambientInteractionShader.bumpMatrixT = qglGetUniformLocationARB(ambientInteractionShader.program, "u_bumpMatrixT");
+	ambientInteractionShader.diffuseMatrixS = qglGetUniformLocationARB(ambientInteractionShader.program, "u_diffuseMatrixS");
+	ambientInteractionShader.diffuseMatrixT = qglGetUniformLocationARB(ambientInteractionShader.program, "u_diffuseMatrixT");
+
+	ambientInteractionShader.colorModulate = qglGetUniformLocationARB(ambientInteractionShader.program, "u_colorModulate");
+	ambientInteractionShader.colorAdd = qglGetUniformLocationARB(ambientInteractionShader.program, "u_colorAdd");
+
+	ambientInteractionShader.diffuseColor = qglGetUniformLocationARB(ambientInteractionShader.program, "u_diffuseColor");
+
+	// set texture locations
+	qglUseProgramObjectARB(ambientInteractionShader.program);
+	qglUniform1iARB(ambientInteractionShader.u_normalTexture, 0);
+	qglUniform1iARB(ambientInteractionShader.u_lightFalloffTexture, 1);
+	qglUniform1iARB(ambientInteractionShader.u_lightProjectionTexture, 2);
+	qglUniform1iARB(ambientInteractionShader.u_diffuseTexture, 3);
+	qglUseProgramObjectARB(0);
+
+	// load stencil shadow extrusion shaders
+	if (!GLSL_LoadProgram("stencilshadow", &stencilShadowShader, true)) {
+		common->Printf("GLSL stencilShadowShader unavailable\n.");
+		DeleteAllGLSLPrograms();
+		return false;
+	}
+
+	// set uniform locations
+	stencilShadowShader.localLightOrigin = qglGetUniformLocationARB(stencilShadowShader.program, "u_lightOrigin");
+
+	return true;
+}
+
+/*
+==================
+R_ReloadGLSLPrograms_f
+==================
+*/
+void R_ReloadGLSLPrograms_f(const idCmdArgs &args) {
+	common->Printf("----- R_ReloadGLSLPrograms_f -----\n");
+
+	if (glConfig.GLSLAvailable) {
+		glConfig.allowGLSLPath = InitGLSLShaders();
+
+		if (!glConfig.allowGLSLPath)
+			common->Printf("GLSL shaders failed to init.\n");
+	} else {
+		common->Printf("Not available.\n");
+	}
+
+	common->Printf("----------------------------------\n");
+}
+
+void R_GLSL_Init(void) {
+	glConfig.allowGLSLPath = false;
+
+	common->Printf("---------- R_GLSL_Init ----------\n");
+
+	if (glConfig.GLSLAvailable)
+		common->Printf("Available.\n");
+	else
+		common->Printf("Not available.\n");
+
+	common->Printf("---------------------------------\n");
+
+	glConfig.allowGLSLPath = true;
+}
diff --git a/neo/renderer/qgl.h b/neo/renderer/qgl.h
index afe40f1..ee2baf5 100644
--- a/neo/renderer/qgl.h
+++ b/neo/renderer/qgl.h
@@ -125,6 +125,51 @@ extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC	qglProgramLocalParameter4fvARB;
 // GL_EXT_depth_bounds_test
 extern PFNGLDEPTHBOUNDSEXTPROC              qglDepthBoundsEXT;
 
+// GL_ARB_shading_language_100
+extern void ( APIENTRY * qglDeleteObjectARB )( GLhandleARB obj );
+extern GLhandleARB ( APIENTRY * qglGetHandleARB )( GLenum pname );
+extern void ( APIENTRY * qglDetachObjectARB )( GLhandleARB containerObj, GLhandleARB attachedObj );
+extern GLhandleARB ( APIENTRY * qglCreateShaderObjectARB )( GLenum shaderType );
+extern void ( APIENTRY * qglShaderSourceARB )( GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length ); extern void ( APIENTRY * qglCompileShaderARB )( GLhandleARB shaderObj );
+extern GLhandleARB ( APIENTRY * qglCreateProgramObjectARB )( void );
+extern void ( APIENTRY * qglAttachObjectARB )( GLhandleARB containerObj, GLhandleARB obj );
+extern void ( APIENTRY * qglLinkProgramARB )( GLhandleARB programObj );
+extern void ( APIENTRY * qglUseProgramObjectARB )( GLhandleARB programObj );
+extern void ( APIENTRY * qglValidateProgramARB )( GLhandleARB programObj );
+extern void ( APIENTRY * qglUniform1fARB )( GLint location, GLfloat v0 );
+extern void ( APIENTRY * qglUniform2fARB )( GLint location, GLfloat v0, GLfloat v1 );
+extern void ( APIENTRY * qglUniform3fARB )( GLint location, GLfloat v0, GLfloat v1, GLfloat v2 );
+extern void ( APIENTRY * qglUniform4fARB )( GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 );
+extern void ( APIENTRY * qglUniform1iARB )( GLint location, GLint v0 );
+extern void ( APIENTRY * qglUniform2iARB )( GLint location, GLint v0, GLint v1 );
+extern void ( APIENTRY * qglUniform3iARB )( GLint location, GLint v0, GLint v1, GLint v2 );
+extern void ( APIENTRY * qglUniform4iARB )( GLint location, GLint v0, GLint v1, GLint v2, GLint v3 );
+extern void ( APIENTRY * qglUniform2fvARB )( GLint location, GLsizei count, const GLfloat *value );
+extern void ( APIENTRY * qglUniform3fvARB )( GLint location, GLsizei count, const GLfloat *value );
+extern void ( APIENTRY * qglUniform4fvARB )( GLint location, GLsizei count, const GLfloat *value );
+extern void ( APIENTRY * qglUniform2ivARB )( GLint location, GLsizei count, const GLint *value );
+extern void ( APIENTRY * qglUniform3ivARB )( GLint location, GLsizei count, const GLint *value );
+extern void ( APIENTRY * qglUniform4ivARB )( GLint location, GLsizei count, const GLint *value );
+extern void ( APIENTRY * qglUniformMatrix2fvARB )( GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );
+extern void ( APIENTRY * qglUniformMatrix3fvARB )( GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );
+extern void ( APIENTRY * qglUniformMatrix4fvARB )( GLint location, GLsizei count, GLboolean transpose, const GLfloat *value );
+extern void ( APIENTRY * qglGetObjectParameterfvARB )( GLhandleARB obj, GLenum pname, GLfloat *params );
+extern void ( APIENTRY * qglGetObjectParameterivARB )( GLhandleARB obj, GLenum pname, GLint *params );
+extern void ( APIENTRY * qglGetInfoLogARB )( GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog );
+extern void ( APIENTRY * qglGetAttachedObjectsARB )( GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj );
+extern GLint ( APIENTRY * qglGetUniformLocationARB )( GLhandleARB programObj, const GLcharARB *name );
+extern void ( APIENTRY * qglGetActiveUniformARB )( GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name );
+extern void ( APIENTRY * qglGetUniformfvARB )( GLhandleARB programObj, GLint location, GLfloat *params );
+extern void ( APIENTRY * qglGetUniformivARB )( GLhandleARB programObj, GLint location, GLint *params );
+extern void ( APIENTRY * qglGetShaderSourceARB )( GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source );
+extern void ( APIENTRY * qglBindAttribLocationARB )( GLhandleARB programObj, GLuint index, const GLcharARB *name );
+extern void ( APIENTRY * qglGetActiveAttribARB )( GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name );
+extern GLint ( APIENTRY * qglGetAttribLocationARB )( GLhandleARB programObj, const GLcharARB *name );
+
+extern void ( APIENTRY * qglGetShaderiv )(GLuint shader, GLenum pname, GLint* param);
+extern void ( APIENTRY * qglDeleteShader )(GLuint shader);
+extern void ( APIENTRY * qglGetShaderInfoLog )(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+
 //===========================================================================
 
 // non-windows systems will just redefine qgl* to gl*
diff --git a/neo/renderer/tr_local.h b/neo/renderer/tr_local.h
index b23c9ed..36cd365 100644
--- a/neo/renderer/tr_local.h
+++ b/neo/renderer/tr_local.h
@@ -677,6 +677,7 @@ typedef enum {
 	BE_NV20,
 	BE_R200,
 	BE_ARB2,
+	BE_GLSL,
 	BE_BAD
 } backEndName_t;
 
@@ -835,7 +836,7 @@ extern idCVar r_flareSize;				// scale the flare deforms from the material def
 extern idCVar r_gamma;					// changes gamma tables
 extern idCVar r_brightness;				// changes gamma tables
 
-extern idCVar r_renderer;				// arb, nv10, nv20, r200, gl2, etc
+extern idCVar r_renderer;				// arb, nv10, nv20, r200, glsl, etc
 
 extern idCVar r_checkBounds;			// compare all surface bounds with precalculated ones
 
@@ -1291,8 +1292,12 @@ void	RB_NV20_DrawInteractions( void );
 void	R_ARB2_Init( void );
 void	RB_ARB2_DrawInteractions( void );
 void	R_ReloadARBPrograms_f( const idCmdArgs &args );
+void	R_ReloadGLSLPrograms_f( const idCmdArgs &args );
 int		R_FindARBProgram( GLenum target, const char *program );
 
+void	R_GLSL_Init( void );
+void	RB_GLSL_DrawInteractions( void );
+
 typedef enum {
 	PROG_INVALID,
 	VPROG_INTERACTION,
@@ -1316,6 +1321,47 @@ typedef enum {
 	PROG_USER
 } program_t;
 
+struct shaderProgram_t {
+	GLhandleARB		program;					// program = vertex + fragment shader
+
+	GLhandleARB		vertexShader;
+	GLhandleARB		fragmentShader;
+
+	// uniform parameters
+	GLint			u_normalTexture;
+	GLint			u_lightFalloffTexture;
+	GLint			u_lightProjectionTexture;
+	GLint			u_diffuseTexture;
+	GLint			u_specularTexture;
+
+	GLint			modelMatrix;
+
+	GLint			localLightOrigin;
+	GLint			localViewOrigin;
+
+	GLint			lightProjectionS;
+	GLint			lightProjectionT;
+	GLint			lightProjectionQ;
+	GLint			lightFalloff;
+
+	GLint			bumpMatrixS;
+	GLint			bumpMatrixT;
+	GLint			diffuseMatrixS;
+	GLint			diffuseMatrixT;
+	GLint			specularMatrixS;
+	GLint			specularMatrixT;
+
+	GLint			colorModulate;
+	GLint			colorAdd;
+
+	GLint			diffuseColor;
+	GLint			specularColor;
+};
+
+extern shaderProgram_t  interactionShader;
+extern shaderProgram_t  ambientInteractionShader;
+extern shaderProgram_t	stencilShadowShader;
+
 /*
 
   All vertex programs use the same constant register layout:
-- 
1.7.10.4

