diff --git a/Makefiles/Makefile.SDL.Win32.GCC b/Makefiles/Makefile.SDL.Win32.GCC
index 3fae2de..3cf451c 100644
--- a/Makefiles/Makefile.SDL.Win32.GCC
+++ b/Makefiles/Makefile.SDL.Win32.GCC
@@ -22,8 +22,7 @@
 #
 # Makefile.SDL.Win32.GCC
 #
-# GNU Makefile for SDL port using GCC (MinGW) on Windows. Copy this Makefile
-# to the base directory of the source distribution (one up from Src/ and here).
+# GNU Makefile for SDL port using GCC (MinGW) on Windows.
 #
 
 
@@ -89,10 +88,12 @@ OUTFILE = $(BIN_DIR)\Supermodel.exe
 ###############################################################################
 
 CC = gcc
+CXX = g++
 LD = g++
-COMPILER_FLAGS = -I$(SDL_INCLUDEPATH) -ISrc/ -ISrc/OSD/ -ISrc/OSD/SDL/ -ISrc/OSD/Windows/ -c -Wall -O3 -DSUPERMODEL_WIN32 -DGLEW_STATIC
+COMPILER_FLAGS = -I$(SDL_INCLUDEPATH) -ISrc/ -ISrc/OSD/ -ISrc/OSD/SDL/ -ISrc/OSD/Windows/ -c -Wall -O3 -DSUPERMODEL_WIN32 -DGLEW_STATIC 
 CFLAGS = $(COMPILER_FLAGS)
 CPPFLAGS = $(COMPILER_FLAGS)
+#LFLAGS = -s -o $(OUTFILE) $(OBJ) -L$(SDL_LIBPATH) -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -ldinput8 -ldxguid -lole32 -loleaut32 -lz -l:$(WINSDK_LIBPATH)/WbemUuid.lib
 LFLAGS = -s -o $(OUTFILE) $(OBJ) -L$(SDL_LIBPATH) -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -ldinput8 -ldxguid -lole32 -loleaut32 -lz -l:$(WINSDK_LIBPATH)/WbemUuid.lib
 
 #
@@ -188,40 +189,40 @@ $(OBJ_DIR)/m68kopnz.o: $(OBJ_DIR)/m68kopnz.c $(OBJ_DIR)/m68kmake.exe $(OBJ_DIR)/
 # Rules for Supermodel C++ code
 #
 $(OBJ_DIR)/%.o:	Src/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/Model3/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
-$(OBJ_DIR)/%.o:	Src/Graphics/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+$(OBJ_DIR)/%.o:	Src/Graphics/%.cpp Src/Graphics/Shaders2D.h Src/Graphics/Shaders3D.h
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/Sound/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/Debugger/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/Debugger/CPU/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 	
-$(OBJ_DIR)/%.o:	Src/CPU/PowerPC/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+$(OBJ_DIR)/%.o:	Src/CPU/PowerPC/%.cpp Src/CPU/PowerPC/ppc.h Src/CPU/PowerPC/ppc603.c Src/CPU/PowerPC/ppc_ops.c Src/CPU/PowerPC/ppc_ops.h
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/CPU/68K/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/CPU/Z80/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/Inputs/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 			
 $(OBJ_DIR)/%.o:	Src/OSD/SDL/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/OSD/Windows/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
 
 $(OBJ_DIR)/%.o:	Src/Pkgs/%.c
 	$(CC) $< $(CFLAGS) -o $(OBJ_DIR)/$(*F).o
@@ -232,4 +233,4 @@ $(OBJ_DIR)/%.o:	Src/Pkgs/%.c
 # To eliminate name conflicts, object files have the prefix "amp_" attached.
 #
 $(OBJ_DIR)/amp_%.o:	Src/Sound/MPEG/%.cpp
-	$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
\ No newline at end of file
+	$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
\ No newline at end of file
diff --git a/Src/Graphics/Shaders/Fragment.glsl b/Src/Graphics/Shaders/Fragment.glsl
index 09b2e6c..022387a 100644
--- a/Src/Graphics/Shaders/Fragment.glsl
+++ b/Src/Graphics/Shaders/Fragment.glsl
@@ -32,14 +32,16 @@ uniform sampler2D	textureMap;		// complete texture map, 2048x2048 texels
 uniform vec4	spotEllipse;		// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
 uniform vec2	spotRange;			// spotlight Z range: .x=start (viewspace coordinates), .y=limit
 uniform vec3	spotColor;			// spotlight RGB color
+uniform vec3	lighting[2];		// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
 
 // Inputs from vertex shader 
 varying vec4		fsSubTexture;	// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
 varying vec4		fsTexParams;	// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
-varying float		fsTexFormat;	// .x=T1RGB5 contour texture (if > 0)
+varying float		fsTexFormat;	// T1RGB5 contour texture (if > 0)
 varying float		fsTransLevel;	// translucence level, 0.0 (transparent) to 1.0 (opaque)
 varying vec3		fsLightIntensity;	// lighting intensity 
-varying float		fsFogFactor;		// fog factor
+varying float		fsSpecularTerm;	// specular highlight
+varying float		fsFogFactor;	// fog factor
 varying float		fsViewZ;		// Z distance to fragment from viewpoint at origin
 
 /*
@@ -82,12 +84,6 @@ vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEna
 					(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
 					texOffset
 				 ) / 2048.0;
-/*
-	glTexCoord = (	mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
-					(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
-					texOffset
-				 ) / 2048.0;
-*/
 	return glTexCoord;
 }
 
@@ -105,10 +101,11 @@ void main(void)
 	vec2	ellipse;
 	vec3	lightIntensity;
 	float	insideSpot;
+	int		x;
 	
 	// Get polygon color for untextured polygons (textured polygons will overwrite)
-	if (fsTexParams.x==0.0)
-		fragColor = gl_Color;
+	if (fsTexParams.x < 0.5)
+		fragColor = gl_Color;		
 	else
 	// Textured polygons: set fragment color to texel value
 	{			
@@ -163,7 +160,7 @@ void main(void)
 		 * When the alpha value is 0.0 (or close), pixels are discarded 
 		 * entirely.
 		 */
-		if (fsTexParams.y > 0.0)	// contour processing enabled
+		if (fsTexParams.y > 0.5)	// contour processing enabled
 		{
 			if (fragColor.a < 0.01)	// discard anything with alpha == 0
 				discard;
@@ -182,12 +179,13 @@ void main(void)
 	else
 		lightIntensity = fsLightIntensity;
 	fragColor.rgb *= lightIntensity;
-
+	fragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);
+	
 	// Translucency (modulates existing alpha channel for RGBA4 texels)
 	fragColor.a *= fsTransLevel;
 
 	// Apply fog under the control of fog factor setting from polygon header
-	fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );
+	fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);
 
 	// Store final color
 	gl_FragColor = fragColor;
diff --git a/Src/Graphics/Shaders/Vertex.glsl b/Src/Graphics/Shaders/Vertex.glsl
index 27274ab..4ba8952 100644
--- a/Src/Graphics/Shaders/Vertex.glsl
+++ b/Src/Graphics/Shaders/Vertex.glsl
@@ -34,14 +34,14 @@ uniform vec3	lighting[2];		// lighting state (lighting[0] = sun direction, light
 uniform vec4	spotEllipse;		// spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)
 uniform vec2	spotRange;			// spotlight Z range: .x=start (viewspace coordinates), .y=limit
 uniform vec3	spotColor;			// spotlight RGB color
-//uniform vec2	texOffset;			// offset (within 2048x2048 texture sheet) to apply to texture base coordinates
 
 // Custom vertex attributes
 attribute vec4	subTexture;		// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
 attribute vec4	texParams;		// .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
-attribute float	texFormat;		// .x=T1RGB5 contour texture (if > 0)
+attribute float	texFormat;		// T1RGB5 contour texture (if > 0)
 attribute float	transLevel;		// translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value
 attribute float	lightEnable;	// lighting enabled (1.0) or luminous (0.0), drawn at full intensity
+attribute float	shininess;		// specular shininess (if >= 0.0) or disable specular lighting (negative)
 attribute float	fogIntensity;	// fog intensity (1.0, full fog effect, 0.0, no fog) 
 
 // Custom outputs to fragment shader
@@ -50,8 +50,9 @@ varying vec4	fsTexParams;
 varying float	fsTexFormat;
 varying float	fsTransLevel;
 varying vec3	fsLightIntensity;	// total light intensity for this vertex
+varying float	fsSpecularTerm;		// specular light term (additive)
 varying float	fsFogFactor;		// fog factor
-varying float	fsViewZ;
+varying float	fsViewZ;
 
 // Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)
 mat3 GetLinearPart( mat4 m )
@@ -104,7 +105,7 @@ void main(void)
 	gl_FrontColor = gl_Color;	// untextured polygons will use this
 	gl_FrontColor.a = 1.0;	
 	fsLightIntensity = vec3(1.0,1.0,1.0);
-	if (texParams.x > 0.0)		// textured
+	if (texParams.x > 0.5)		// textured
 		fsLightIntensity *= gl_Color.rgb;
 		
 	/*
@@ -113,7 +114,8 @@ void main(void)
 	 * Parallel light source and ambient lighting are only applied for non-
 	 * luminous polygons.
  	 */
-	if (lightEnable > 0.5)	// not luminous
+	fsSpecularTerm = 0.0;
+ 	if (lightEnable > 0.5)	// not luminous
 	{
 		// Normal -> view space
 		viewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);
@@ -125,7 +127,39 @@ void main(void)
 		sunFactor = max(dot(sunVector,viewNormal),0.0);
 		
 		// Total light intensity: sum of all components
-		fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);		
+		fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);
+		
+		/*
+		 * Specular Lighting
+		 *
+		 * The specular term is treated similarly to the "separate specular
+		 * color" functionality of OpenGL: it is added as a highlight in the
+		 * fragment shader. This allows even black textures to be lit.
+		 *
+		 * TO-DO: Ambient intensity viewport parameter is known but what about
+		 * the intensity of the specular term? Always applied with full 
+		 * intensity here but this is unlikely to be correct.
+		 */
+  		if (shininess >= 0.0)
+  		{
+  			// Standard specular lighting equation
+  			vec3 V = normalize(-viewVertex);
+  			vec3 H = normalize(sunVector+V);	// halfway vector
+  			float s = max(10,64-shininess);		// seems to look nice, but probably not correct
+  			fsSpecularTerm = pow(max(dot(viewNormal,H),0),s);
+  			if (sunFactor <= 0) fsSpecularTerm = 0;
+  			
+  			// Faster approximation  			
+  			//float temp = max(dot(viewNormal,H),0);
+  			//float s = 64-shininess;
+  			//fsSpecularTerm = temp/(s-temp*s+temp);
+  			
+  			// Phong formula
+  			//vec3 R = normalize(2*dot(sunVector,viewNormal)*viewNormal - sunVector);
+  			//vec3 V = normalize(-viewVertex);
+  			//float s = max(2,64-shininess);
+  			//fsSpecularTerm = pow(max(dot(R,V),0),s);
+  		}
 	}
 	
 	// Fog
@@ -138,7 +172,6 @@ void main(void)
 	// Pass remaining parameters to fragment shader
 	gl_TexCoord[0] = gl_MultiTexCoord0;
 	fsSubTexture = subTexture;
-	//fsSubTexture.xy += texOffset;	// apply texture offset
 	fsTexParams = texParams;
 	fsTransLevel = transLevel;
 	fsTexFormat = texFormat;
diff --git a/Src/Graphics/Shaders3D.h b/Src/Graphics/Shaders3D.h
index 391ef67..d83e02b 100644
--- a/Src/Graphics/Shaders3D.h
+++ b/Src/Graphics/Shaders3D.h
@@ -31,148 +31,183 @@
 // Vertex shader
 static const char vertexShaderSource[] =
 {
-"/**																				\n"
-" ** Supermodel																		\n"
-" ** A Sega Model 3 Arcade Emulator.												\n"
-" ** Copyright 2011 Bart Trzynadlowski 												\n"
-" **																				\n"
-" ** This file is part of Supermodel.												\n"
-" **																				\n"
-" ** Supermodel is free software: you can redistribute it and/or modify it under	\n"
-" ** the terms of the GNU General Public License as published by the Free 			\n"
-" ** Software Foundation, either version 3 of the License, or (at your option)		\n"
-" ** any later version.																\n"
-" **																				\n"
-" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT		\n"
-" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or			\n"
-" ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for		\n"
-" ** more details.																	\n"
-" **																				\n"
-" ** You should have received a copy of the GNU General Public License along		\n"
-" ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.					\n"
-" **/																				\n"
-"\n"  
-"/*														\n"
-" * Vertex.glsl											\n"
-" *														\n"
-" * Vertex shader for 3D rendering.						\n"
-" */													\n"
+"/**\n"
+" ** Supermodel\n"
+" ** A Sega Model 3 Arcade Emulator.\n"
+" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
+" **\n"
+" ** This file is part of Supermodel.\n"
+" **\n"
+" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
+" ** the terms of the GNU General Public License as published by the Free \n"
+" ** Software Foundation, either version 3 of the License, or (at your option)\n"
+" ** any later version.\n"
+" **\n"
+" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
+" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
+" ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\n"
+" ** more details.\n"
+" **\n"
+" ** You should have received a copy of the GNU General Public License along\n"
+" ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.\n"
+" **/\n"
+"  \n"
+"/*\n"
+" * Vertex.glsl\n"
+" *\n"
+" * Vertex shader for 3D rendering.\n"
+" */\n"
+" \n"
+"#version 120\n"
 "\n"
-"#version 120											\n"
+"// Global uniforms\n"
+"uniform mat4\tmodelViewMatrix;\t// model -> view space matrix\n"
+"uniform mat4\tprojectionMatrix;\t// view space -> screen space matrix\n"
+"uniform vec3\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
+"uniform vec4\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
+"uniform vec2\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
+"uniform vec3\tspotColor;\t\t\t// spotlight RGB color\n"
 "\n"
-"// Global uniforms																																				\n"
-"uniform mat4	modelViewMatrix;	// model -> view space matrix																								\n"
-"uniform mat4	projectionMatrix;	// view space -> screen space matrix																						\n"
-"uniform vec3	lighting[2];		// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)					\n"
-"uniform vec4	spotEllipse;		// spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)	\n"
-"uniform vec2	spotRange;			// spotlight Z range: .x=start (viewspace coordinates), .y=limit															\n"
-"uniform vec3	spotColor;			// spotlight RGB color																										\n"
+"// Custom vertex attributes\n"
+"attribute vec4\tsubTexture;\t\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
+"attribute vec4\ttexParams;\t\t// .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
+"attribute float\ttexFormat;\t\t// T1RGB5 contour texture (if > 0)\n"
+"attribute float\ttransLevel;\t\t// translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value\n"
+"attribute float\tlightEnable;\t// lighting enabled (1.0) or luminous (0.0), drawn at full intensity\n"
+"attribute float\tshininess;\t\t// specular shininess (if >= 0.0) or disable specular lighting (negative)\n"
+"attribute float\tfogIntensity;\t// fog intensity (1.0, full fog effect, 0.0, no fog) \n"
 "\n"
-"// Custom vertex attributes																																\n"
-"attribute vec4	subTexture;		// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)											\n"
-"attribute vec4	texParams;		// .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode		\n"
-"attribute float	texFormat;		// .x=T1RGB5 contour texture (if > 0)																					\n"
-"attribute float	transLevel;		// translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value							\n"
-"attribute float	lightEnable;	// lighting enabled (1.0) or luminous (0.0), drawn at full intensity													\n"
-"attribute float	fogIntensity;	// fog intensity (1.0, full fog effect, 0.0, no fog) 																	\n"
+"// Custom outputs to fragment shader\n"
+"varying vec4\tfsSubTexture;\n"
+"varying vec4\tfsTexParams;\n"
+"varying float\tfsTexFormat;\n"
+"varying float\tfsTransLevel;\n"
+"varying vec3\tfsLightIntensity;\t// total light intensity for this vertex\n"
+"varying float\tfsSpecularTerm;\t\t// specular light term (additive)\n"
+"varying float\tfsFogFactor;\t\t// fog factor\n"
+"varying float\tfsViewZ;\n"
 "\n"
-"// Custom outputs to fragment shader	\n"
-"varying vec4	fsSubTexture;			\n"
-"varying vec4	fsTexParams;			\n"
-"varying float	fsTexFormat;			\n"
-"varying float	fsTransLevel;			\n"
-"varying vec3	fsLightIntensity;	// total light intensity for this vertex	\n"
-"varying float	fsFogFactor;		// fog factor	\n"
-"varying float	fsViewZ;				\n"
+"// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)\n"
+"mat3 GetLinearPart( mat4 m )\n"
+"{\n"
+"\tmat3 result;\n"
+"\t\n"
+"\tresult[0][0] = m[0][0]; \n"
+"\tresult[0][1] = m[0][1]; \n"
+"\tresult[0][2] = m[0][2]; \n"
 "\n"
-"// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)	\n"
-"mat3 GetLinearPart( mat4 m )															\n"
-"{																						\n"
-"	mat3 result;																		\n"
+"\tresult[1][0] = m[1][0]; \n"
+"\tresult[1][1] = m[1][1]; \n"
+"\tresult[1][2] = m[1][2]; \n"
+"\t\n"
+"\tresult[2][0] = m[2][0]; \n"
+"\tresult[2][1] = m[2][1]; \n"
+"\tresult[2][2] = m[2][2]; \n"
+"\t\n"
+"\treturn result;\n"
+"}\n"
 "\n"
-"	result[0][0] = m[0][0]; 															\n"
-"	result[0][1] = m[0][1]; 															\n"
-"	result[0][2] = m[0][2]; 															\n"
+"void main(void)\n"
+"{\n"
+"\tvec3\tviewVertex;\t\t// vertex coordinates in view space\n"
+"\tvec3\tviewNormal;\t\t// vertex normal in view space\n"
+"\tvec3\tsunVector;\t\t// sun lighting vector (as reflecting away from vertex)\n"
+"\tfloat\tsunFactor;\t\t// sun light projection along vertex normal (0.0 to 1.0)\n"
+"\tvec3\thalfway;\n"
+"\tfloat\tspecFactor;\n"
+"\t\n"
+"\t// Transform vertex\n"
+"\tgl_Position = projectionMatrix * modelViewMatrix * gl_Vertex;\n"
+"\tviewVertex = vec3(modelViewMatrix * gl_Vertex);\t\n"
+"\t\n"
+"\t/*\n"
+"\t * Modulation\n"
+"\t *\n"
+" \t * Polygon color serves as material color (modulating the light intensity)\n"
+"\t * for textured polygons. The fragment shader will ignore (overwrite) the\n"
+"\t * the color passed to it if the fragment is textured. \n"
+"\t *\n"
+"\t * Untextured fragments must be set to the polygon color and the light\n"
+"\t * intensity is initialized to 1.0 here. Alpha must be set to 1.0 because\n"
+"\t * the fragment shader multiplies it by the polygon translucency setting. \n"
+"\t *\n"
+"\t * TO-DO: Does OpenGL set alpha to 1.0 by default if no alpha is specified\n"
+"\t * for the vertex? If so, we can remove that line from here.\n"
+"\t */\n"
 "\n"
-"	result[1][0] = m[1][0]; 															\n"
-"	result[1][1] = m[1][1]; 															\n"
-"	result[1][2] = m[1][2]; 															\n"
+"\tgl_FrontColor = gl_Color;\t// untextured polygons will use this\n"
+"\tgl_FrontColor.a = 1.0;\t\n"
+"\tfsLightIntensity = vec3(1.0,1.0,1.0);\n"
+"\tif (texParams.x > 0.5)\t\t// textured\n"
+"\t\tfsLightIntensity *= gl_Color.rgb;\n"
+"\t\t\n"
+"\t/*\n"
+" \t * Sun Light\n"
+"\t *\n"
+"\t * Parallel light source and ambient lighting are only applied for non-\n"
+"\t * luminous polygons.\n"
+" \t */\n"
+"\tfsSpecularTerm = 0.0;\n"
+" \tif (lightEnable > 0.5)\t// not luminous\n"
+"\t{\n"
+"\t\t// Normal -> view space\n"
+"\t\tviewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);\n"
 "\n"
-"	result[2][0] = m[2][0]; 															\n"
-"	result[2][1] = m[2][1]; 															\n"
-"	result[2][2] = m[2][2]; 															\n"
+"\t\t// Real3D -> OpenGL view space convention (TO-DO: do this outside of shader)\n"
+"\t\tsunVector = lighting[0]*vec3(1.0,-1.0,-1.0);\n"
+"\t\t\n"
+"\t\t// Compute diffuse factor for sunlight\n"
+"\t\tsunFactor = max(dot(sunVector,viewNormal),0.0);\n"
+"\t\t\n"
+"\t\t// Total light intensity: sum of all components\n"
+"\t\tfsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);\n"
+"\t\t\n"
+"\t\t/*\n"
+"\t\t * Specular Lighting\n"
+"\t\t *\n"
+"\t\t * The specular term is treated similarly to the \"separate specular\n"
+"\t\t * color\" functionality of OpenGL: it is added as a highlight in the\n"
+"\t\t * fragment shader. This allows even black textures to be lit.\n"
+"\t\t *\n"
+"\t\t * TO-DO: Ambient intensity viewport parameter is known but what about\n"
+"\t\t * the intensity of the specular term? Always applied with full \n"
+"\t\t * intensity here but this is unlikely to be correct.\n"
+"\t\t */\n"
+"  \t\tif (shininess >= 0.0)\n"
+"  \t\t{\n"
+"  \t\t\t// Standard specular lighting equation\n"
+"  \t\t\tvec3 V = normalize(-viewVertex);\n"
+"  \t\t\tvec3 H = normalize(sunVector+V);\t// halfway vector\n"
+"  \t\t\tfloat s = max(10,64-shininess);\t\t// seems to look nice, but probably not correct\n"
+"  \t\t\tfsSpecularTerm = pow(max(dot(viewNormal,H),0),s);\n"
+"  \t\t\tif (sunFactor <= 0) fsSpecularTerm = 0;\n"
+"  \t\t\t\n"
+"  \t\t\t// Faster approximation  \t\t\t\n"
+"  \t\t\t//float temp = max(dot(viewNormal,H),0);\n"
+"  \t\t\t//float s = 64-shininess;\n"
+"  \t\t\t//fsSpecularTerm = temp/(s-temp*s+temp);\n"
+"  \t\t\t\n"
+"  \t\t\t// Phong formula\n"
+"  \t\t\t//vec3 R = normalize(2*dot(sunVector,viewNormal)*viewNormal - sunVector);\n"
+"  \t\t\t//vec3 V = normalize(-viewVertex);\n"
+"  \t\t\t//float s = max(2,64-shininess);\n"
+"  \t\t\t//fsSpecularTerm = pow(max(dot(R,V),0),s);\n"
+"  \t\t}\n"
+"\t}\n"
+"\t\n"
+"\t// Fog\n"
+"\tfloat z = length(viewVertex);\n"
+"\tfsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0);\n"
 "\n"
-"	return result;																		\n"
-"}																						\n"
+"\t// Pass viewspace Z coordinate (for spotlight)\n"
+"\tfsViewZ = -viewVertex.z;\t// convert Z from GL->Real3D convention (want +Z to be further into screen)\n"
 "\n"
-"void main(void)																		\n"
-"{																						\n"
-"	vec3	viewVertex;		// vertex coordinates in view space							\n"
-"	vec3	viewNormal;		// vertex normal in view space								\n"
-"	vec3	sunVector;		// sun lighting vector (as reflecting away from vertex)		\n"
-"	float	sunFactor;		// sun light projection along vertex normal (0.0 to 1.0)	\n"
-"	vec3	halfway;																	\n"
-"	float	specFactor;																	\n"
-"																						\n"
-"	// Transform vertex																	\n"
-"	gl_Position = projectionMatrix * modelViewMatrix * gl_Vertex;						\n"
-"	viewVertex = vec3(modelViewMatrix * gl_Vertex);										\n"
-"																						\n"
-"	/*																					\n"
-"	 * Modulation																		\n"
-"	 *																					\n"
-"	 * Polygon color serves as material color (modulating the light intensity)			\n"
-"	 * for textured polygons. The fragment shader will ignore (overwrite) the			\n"
-"	 * the color passed to it if the fragment is textured. 								\n"
-"	 *																					\n"
-"	 * Untextured fragments must be set to the polygon color and the light				\n"
-"	 * intensity is initialized to 1.0 here. Alpha must be set to 1.0 because			\n"
-"	 * the fragment shader multiplies it by the polygon translucency setting. 			\n"
-"	 *																					\n"
-"	 * To-do: Does OpenGL set alpha to 1.0 by default if no alpha is specified			\n"
-"	 * for the vertex? If so, we can remove that line from here.						\n"
-"	 */																					\n"
-"																						\n"
-"	gl_FrontColor = gl_Color;	// untextured polygons will use this					\n"
-"	gl_FrontColor.a = 1.0;																\n"
-"	fsLightIntensity = vec3(1.0,1.0,1.0);												\n"
-"	if (texParams.x > 0.0)		// textured												\n"
-"		fsLightIntensity *= gl_Color.rgb;												\n"
-"																						\n"
-"	/*																					\n"
-" 	 * Sun Light																		\n"
-"	 *																					\n"
-"	 * Parallel light source and ambient lighting are only applied for non-				\n"
-"	 * luminous polygons.																\n"
-" 	 */																					\n"
-"	if (lightEnable > 0.5)	// not luminous												\n"
-"	{																					\n"
-"		// Normal -> view space															\n"
-"		viewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);				\n"
-"																						\n"
-"		// Real3D -> OpenGL view space convention (TO-DO: do this outside of shader)	\n"
-"		sunVector = lighting[0]*vec3(1.0,-1.0,-1.0);									\n"
-"																						\n"
-"		// Compute diffuse factor for sunlight											\n"
-"		sunFactor = max(dot(sunVector,viewNormal),0.0);									\n"
-"																						\n"
-"		// Total light intensity: sum of all components									\n"
-"		fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);					\n"
-"	}																					\n"
-"																						\n"
-"	// Fog																				\n"
-"	float z = length(viewVertex);														\n"
-"	fsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0);	\n"
-"																						\n"
-"	// Pass viewspace Z coordinate (for spotlight)										\n"
-"	fsViewZ = -viewVertex.z;	// convert Z from GL->Real3D convention (want +Z to be further into screen)	\n"
-"																						\n"
-"	// Pass remaining parameters to fragment shader										\n"
-"	gl_TexCoord[0] = gl_MultiTexCoord0;													\n"
-"	fsSubTexture = subTexture;															\n"
-"	fsTexParams = texParams;															\n"
-"	fsTransLevel = transLevel;															\n"
-"	fsTexFormat = texFormat;															\n"
+"\t// Pass remaining parameters to fragment shader\n"
+"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n"
+"\tfsSubTexture = subTexture;\n"
+"\tfsTexParams = texParams;\n"
+"\tfsTransLevel = transLevel;\n"
+"\tfsTexFormat = texFormat;\n"
 "}\n"
 };
 	
@@ -180,194 +215,198 @@ static const char vertexShaderSource[] =
 // Fragment shader
 static const char fragmentShaderSource[] = 
 {
-"/**																				\n"
-" ** Supermodel																		\n"
-" ** A Sega Model 3 Arcade Emulator.												\n"
-" ** Copyright 2011 Bart Trzynadlowski 												\n"
-" **																				\n"
-" ** This file is part of Supermodel.												\n"
-" **																				\n"
-" ** Supermodel is free software: you can redistribute it and/or modify it under	\n"
-" ** the terms of the GNU General Public License as published by the Free 			\n"
-" ** Software Foundation, either version 3 of the License, or (at your option)		\n"
-" ** any later version.																\n"
-" **																				\n"
-" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT		\n"
-" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or			\n"
-" ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for		\n"
-" ** more details.																	\n"
-" **																				\n"
-" ** You should have received a copy of the GNU General Public License along		\n"
-" ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.					\n"
-" **/																				\n"
-" 													\n"
-"/*													\n"
-" * Fragment.glsl									\n"
-" *													\n"
-" * Fragment shader for 3D rendering.				\n"
-" */												\n"
-"													\n"
-"#version 120										\n"
-"													\n"
-"// Global uniforms									\n"
-"uniform sampler2D	textureMap;		// complete texture map, 2048x2048 texels																					\n"
-"uniform vec4	spotEllipse;		// spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)	\n"
-"uniform vec2	spotRange;			// spotlight Z range: .x=start (viewspace coordinates), .y=limit															\n"
-"uniform vec3	spotColor;			// spotlight RGB color																										\n"
+"/**\n"
+" ** Supermodel\n"
+" ** A Sega Model 3 Arcade Emulator.\n"
+" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
+" **\n"
+" ** This file is part of Supermodel.\n"
+" **\n"
+" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
+" ** the terms of the GNU General Public License as published by the Free \n"
+" ** Software Foundation, either version 3 of the License, or (at your option)\n"
+" ** any later version.\n"
+" **\n"
+" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
+" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
+" ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\n"
+" ** more details.\n"
+" **\n"
+" ** You should have received a copy of the GNU General Public License along\n"
+" ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.\n"
+" **/\n"
+" \n"
+"/*\n"
+" * Fragment.glsl\n"
+" *\n"
+" * Fragment shader for 3D rendering.\n"
+" */\n"
 "\n"
-"// Inputs from vertex shader																																	\n"
-"varying vec4		fsSubTexture;	// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)											\n"
-"varying vec4		fsTexParams;	// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode		\n"
-"varying float		fsTexFormat;	// .x=T1RGB5 contour texture (if > 0)																						\n"
-"varying float		fsTransLevel;	// translucence level, 0.0 (transparent) to 1.0 (opaque)																	\n"
-"varying vec3		fsLightIntensity;	// lighting intensity 																									\n"
-"varying float		fsFogFactor;		// fog factor																											\n"
-"varying float		fsViewZ;		// Z distance to fragment from viewpoint at origin																			\n"
+"#version 120\n"
 "\n"
-"/*																							\n"
-" * WrapTexelCoords():																		\n"
-" *																							\n"
-" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture				\n"
-" * sheet, taking into account wrapping behavior.											\n"
-" *																							\n"
-" * Computing normalized OpenGL texture coordinates (0 to 1) within the 					\n"
-" * Real3D texture sheet:																	\n"
-" *																							\n"
-" * If the texture is not mirrored, we simply have to clamp the								\n"
-" * coordinates to fit within the texture dimensions, add the texture						\n"
-" * X, Y position to select the appropriate one, and normalize by 2048						\n"
-" * (the dimensions of the Real3D texture sheet).											\n"
-" *																							\n"
-" *		= [(u,v)%(w,h)+(x,y)]/(2048,2048)													\n"
-" *																							\n"
-" * If mirroring is enabled, textures are mirrored every odd multiple of					\n"
-" * the original texture. To detect whether we are in an odd multiple, 						\n"
-" * simply divide the coordinate by the texture dimension and check 						\n"
-" * whether the result is odd. Then, clamp the coordinates as before but					\n"
-" * subtract from the last texel to mirror them:											\n"
-" *																							\n"
-" * 		= [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)			\n"
-" *		where M is 1.0 if the texture must be mirrored.										\n"
-" *																							\n"
-" * As an optimization, this function computes TWO texture coordinates						\n"
-" * simultaneously. The first is texCoord.xy, the second is in .zw. The other				\n"
-" * parameters must have .xy = .zw.															\n"
-" */																						\n"
-"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)		\n"
-"{																							\n"
-"	vec4	clampedCoord, mirror, glTexCoord;												\n"
-"																							\n"
-"	clampedCoord = mod(texCoord,texSize);						// clamp coordinates to within texture size	\n"
-"	mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0);	// whether this texel needs to be mirrored	\n"
-"																							\n"
-"	glTexCoord = (	mirror*(texSize-clampedCoord) +											\n"
-"					(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +							\n"
-"					texOffset																\n"
-"				 ) / 2048.0;																\n"
-"	return glTexCoord;																		\n"
-"}																							\n"
+"// Global uniforms\n"
+"uniform sampler2D\ttextureMap;\t\t// complete texture map, 2048x2048 texels\n"
+"uniform vec4\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
+"uniform vec2\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
+"uniform vec3\tspotColor;\t\t\t// spotlight RGB color\n"
+"uniform vec3\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
 "\n"
-"/*																							\n"
-" * main():																					\n"
-" *																							\n"
-" * Fragment shader entry point.															\n"
-" */																						\n"
+"// Inputs from vertex shader \n"
+"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
+"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
+"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n"
+"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n"
+"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n"
+"varying float\t\tfsSpecularTerm;\t// specular highlight\n"
+"varying float\t\tfsFogFactor;\t// fog factor\n"
+"varying float\t\tfsViewZ;\t\t// Z distance to fragment from viewpoint at origin\n"
 "\n"
-"void main(void)																			\n"
-"{																							\n"
-"	vec4	uv_top, uv_bot, c[4];															\n"
-"	vec2	r;																				\n"
-"	vec4	fragColor;																		\n"
-"	vec2	ellipse;																		\n"
-"	vec3	lightIntensity;																	\n"
-"	float	insideSpot;																		\n"
-"																							\n"
-"	// Get polygon color for untextured polygons (textured polygons will overwrite)			\n"
-"	if (fsTexParams.x==0.0)																	\n"
-"		fragColor = gl_Color;																\n"
-"	else																					\n"
-"	// Textured polygons: set fragment color to texel value									\n"
-"	{																						\n"
-"		/*																					\n"
-"		 * Bilinear Filtering																\n"
-"		 *																					\n"
-"		 * In order to get this working on ATI, the number of operations is					\n"
-"		 * reduced by putting everything into vec4s. uv_top holds the UV 					\n"
-"		 * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot				\n"	
-"		 * is for the lower two.															\n"
-"		 */																					\n"
-"																							\n"
-"		// Compute fractional blending factor, r, and lower left corner of texel 0			\n"
-"		uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5);	// move into the lower left blending texel 	\n"
-"		r = uv_bot.xy-floor(uv_bot.xy);					// fractional part							\n"
-"		uv_bot.xy = floor(uv_bot.xy);					// integral part							\n"
-"																							\n"
-"		// Compute texel coordinates														\n"
-"		uv_bot.xy += vec2(0.5,0.5);	// offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)	\n"
-"		uv_bot.zw = uv_bot.xy + vec2(1.0,0.0);			// compute coordinates of the other three neighbors								\n"
-"		uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);											\n"
-"																							\n"
-"		// Compute the properly wrapped texel coordinates									\n"
-"		uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));	\n"
-"		uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));	\n"
-"																							\n"
-"		// Fetch the texels																	\n"
-"		c[0]=texture2D(textureMap,uv_bot.xy);	// bottom-left (base texel)					\n"
-"		c[1]=texture2D(textureMap,uv_bot.zw);	// bottom-right								\n"
-"		c[2]=texture2D(textureMap,uv_top.xy);	// top-left									\n"
-"		c[3]=texture2D(textureMap,uv_top.zw);	// top-right								\n"
-"																							\n"
-"		// Interpolate texels and blend result with material color to determine final (unlit) fragment color	\n"
-"		// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);		\n"
-"		// Faster method:																	\n"
-"		c[0] += (c[1]-c[0])*r.s;			// 2 alu										\n"
-"		c[2] += (c[3]-c[2])*r.s;			// 2 alu										\n"
-"		fragColor = c[0]+(c[2]-c[0])*r.t;	// 2 alu										\n"
-"																							\n"
-"		/*																					\n"
-"		 * T1RGB5:																			\n"
-"		 *																					\n"
-"		 * The transparency bit determines whether to discard pixels (if set).				\n"
-"		 * What is unknown is how this bit behaves when interpolated. OpenGL				\n"
-"		 * processes it as an alpha value, so it might concievably be blended				\n"
-"		 * with neighbors. Here, an arbitrary threshold is chosen.							\n"
-"		 *																					\n"
-"		 * To-do: blending could probably enabled and this would work even					\n"
-"		 * better with a hard threshold.													\n"
-"		 *																					\n"
-"		 * Countour processing also seems to be enabled for RGBA4 textures.					\n"
-"		 * When the alpha value is 0.0 (or close), pixels are discarded 					\n"
-"		 * entirely.																		\n"
-"		 */																					\n"
-"		if (fsTexParams.y > 0.0)	// contour processing enabled							\n"
-"		{																					\n"
-"			if (fragColor.a < 0.01)	// discard anything with alpha == 0						\n"
-"				discard;																	\n"
-"		}																					\n"
-"																							\n"
-"		// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency	\n"
-"		if (fsTexFormat > 0.0)		// contour (T1RGB5) texture map															\n"
-"			fragColor.a = 1.0;																\n"
-"	}																						\n"
-"																							\n"
-"	// Compute spotlight and apply lighting													\n"
-"	ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;								\n"
-"	insideSpot = dot(ellipse,ellipse);														\n"
-"	if ((insideSpot <= 1.0) &&  (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))			\n"
-"		lightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;						\n"
-"	else																					\n"
-"		lightIntensity = fsLightIntensity;													\n"
-"	fragColor.rgb *= lightIntensity;														\n"
-"																							\n"
-"	// Translucency (modulates existing alpha channel for RGBA4 texels)						\n"
-"	fragColor.a *= fsTransLevel;															\n"
-"																							\n"
-"	// Apply fog under the control of fog factor setting from polygon header				\n"
-"	fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );						\n"
-"																							\n"
-"	// Store final color																	\n"
-"	gl_FragColor = fragColor;																\n"
-"}\n"	
+"/*\n"
+" * WrapTexelCoords():\n"
+" *\n"
+" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture\n"
+" * sheet, taking into account wrapping behavior.\n"
+" *\n"
+" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n"
+" * Real3D texture sheet:\n"
+" *\n"
+" * If the texture is not mirrored, we simply have to clamp the\n"
+" * coordinates to fit within the texture dimensions, add the texture\n"
+" * X, Y position to select the appropriate one, and normalize by 2048\n"
+" * (the dimensions of the Real3D texture sheet).\n"
+" *\n"
+" *\t\t= [(u,v)%(w,h)+(x,y)]/(2048,2048)\n"
+" *\n"
+" * If mirroring is enabled, textures are mirrored every odd multiple of\n"
+" * the original texture. To detect whether we are in an odd multiple, \n"
+" * simply divide the coordinate by the texture dimension and check \n"
+" * whether the result is odd. Then, clamp the coordinates as before but\n"
+" * subtract from the last texel to mirror them:\n"
+" *\n"
+" * \t\t= [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)\n"
+" *\t\twhere M is 1.0 if the texture must be mirrored.\n"
+" *\n"
+" * As an optimization, this function computes TWO texture coordinates\n"
+" * simultaneously. The first is texCoord.xy, the second is in .zw. The other\n"
+" * parameters must have .xy = .zw.\n"
+" */\n"
+"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)\n"
+"{\n"
+"\tvec4\tclampedCoord, mirror, glTexCoord;\n"
+"\t\n"
+"\tclampedCoord = mod(texCoord,texSize);\t\t\t\t\t\t// clamp coordinates to within texture size\n"
+"\tmirror = mirrorEnable * mod(floor(texCoord/texSize),2.0);\t// whether this texel needs to be mirrored\n"
+"\n"
+"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n"
+"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n"
+"\t\t\t\t\ttexOffset\n"
+"\t\t\t\t ) / 2048.0;\n"
+"\treturn glTexCoord;\n"
+"}\n"
+"\n"
+"/*\n"
+" * main():\n"
+" *\n"
+" * Fragment shader entry point.\n"
+" */\n"
+"\n"
+"void main(void)\n"
+"{\t\n"
+"\tvec4\tuv_top, uv_bot, c[4];\n"
+"\tvec2\tr;\n"
+"\tvec4\tfragColor;\n"
+"\tvec2\tellipse;\n"
+"\tvec3\tlightIntensity;\n"
+"\tfloat\tinsideSpot;\n"
+"\tint\t\tx;\n"
+"\t\n"
+"\t// Get polygon color for untextured polygons (textured polygons will overwrite)\n"
+"\tif (fsTexParams.x < 0.5)\n"
+"\t\tfragColor = gl_Color;\t\t\n"
+"\telse\n"
+"\t// Textured polygons: set fragment color to texel value\n"
+"\t{\t\t\t\n"
+"\t\t/*\n"
+"\t\t * Bilinear Filtering\n"
+"\t\t *\n"
+"\t\t * In order to get this working on ATI, the number of operations is\n"
+"\t\t * reduced by putting everything into vec4s. uv_top holds the UV \n"
+"\t\t * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot\n"
+"\t\t * is for the lower two.\n"
+"\t\t */\n"
+"\n"
+"\t\t// Compute fractional blending factor, r, and lower left corner of texel 0\n"
+"\t\tuv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5);\t// move into the lower left blending texel \n"
+"\t\tr = uv_bot.xy-floor(uv_bot.xy);\t\t\t\t\t// fractional part\n"
+"\t\tuv_bot.xy = floor(uv_bot.xy);\t\t\t\t\t// integral part\n"
+"\t\t\n"
+"\t\t// Compute texel coordinates\n"
+"\t\tuv_bot.xy += vec2(0.5,0.5);\t// offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)\n"
+"\t\tuv_bot.zw = uv_bot.xy + vec2(1.0,0.0);\t\t\t// compute coordinates of the other three neighbors\n"
+"\t\tuv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);\n"
+"\n"
+"\t\t// Compute the properly wrapped texel coordinates\n"
+"\t\tuv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
+"\t\tuv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
+"\n"
+"\t\t// Fetch the texels\n"
+"\t\tc[0]=texture2D(textureMap,uv_bot.xy);\t// bottom-left (base texel)\n"
+"\t\tc[1]=texture2D(textureMap,uv_bot.zw);\t// bottom-right\n"
+"\t\tc[2]=texture2D(textureMap,uv_top.xy);\t// top-left\n"
+"\t\tc[3]=texture2D(textureMap,uv_top.zw);\t// top-right\t\t\n"
+"\n"
+"\t\t// Interpolate texels and blend result with material color to determine final (unlit) fragment color\n"
+"\t\t// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);\n"
+"\t\t// Faster method:\n"
+"\t\tc[0] += (c[1]-c[0])*r.s;\t\t\t// 2 alu\n"
+"\t\tc[2] += (c[3]-c[2])*r.s;\t\t\t// 2 alu\n"
+"\t\tfragColor = c[0]+(c[2]-c[0])*r.t;\t//2 alu\n"
+"\t\n"
+"\t\t/*\n"
+"\t\t * T1RGB5:\n"
+"\t\t *\n"
+"\t\t * The transparency bit determines whether to discard pixels (if set).\n"
+"\t\t * What is unknown is how this bit behaves when interpolated. OpenGL\n"
+"\t\t * processes it as an alpha value, so it might concievably be blended\n"
+"\t\t * with neighbors. Here, an arbitrary threshold is chosen.\n"
+"\t\t *\n"
+"\t\t * To-do: blending could probably enabled and this would work even\n"
+"\t\t * better with a hard threshold.\n"
+"\t\t *\n"
+"\t\t * Countour processing also seems to be enabled for RGBA4 textures.\n"
+"\t\t * When the alpha value is 0.0 (or close), pixels are discarded \n"
+"\t\t * entirely.\n"
+"\t\t */\n"
+"\t\tif (fsTexParams.y > 0.5)\t// contour processing enabled\n"
+"\t\t{\n"
+"\t\t\tif (fragColor.a < 0.01)\t// discard anything with alpha == 0\n"
+"\t\t\t\tdiscard;\n"
+"\t\t}\n"
+"\t\t\n"
+"\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n"
+"\t\tif (fsTexFormat > 0.0)\t\t// contour (T1RGB5) texture map\n"
+"\t\t\tfragColor.a = 1.0;\n"
+"\t}\n"
+"\n"
+"\t// Compute spotlight and apply lighting\n"
+"\tellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;\n"
+"\tinsideSpot = dot(ellipse,ellipse);\n"
+"\tif ((insideSpot <= 1.0) &&  (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))\n"
+"\t\tlightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;\n"
+"\telse\n"
+"\t\tlightIntensity = fsLightIntensity;\n"
+"\tfragColor.rgb *= lightIntensity;\n"
+"\tfragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);\n"
+"\t\n"
+"\t// Translucency (modulates existing alpha channel for RGBA4 texels)\n"
+"\tfragColor.a *= fsTransLevel;\n"
+"\n"
+"\t// Apply fog under the control of fog factor setting from polygon header\n"
+"\tfragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);\n"
+"\n"
+"\t// Store final color\n"
+"\tgl_FragColor = fragColor;\n"
+"}\n"
 };
 
 #endif	// INCLUDED_SHADERS3D_H