/**
 ** Supermodel
 ** A Sega Model 3 Arcade Emulator.
 ** Copyright 2011 Bart Trzynadlowski 
 **
 ** This file is part of Supermodel.
 **
 ** Supermodel 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.
 **
 ** Supermodel 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 Supermodel.  If not, see <http://www.gnu.org/licenses/>.
 **/
 
/*
 * Shaders3D.h
 * 
 * Header file containing the 3D vertex and fragment shaders.
 */

#ifndef INCLUDED_SHADERS3D_H
#define INCLUDED_SHADERS3D_H

// 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"
"#version 120											\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"
"\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"
"\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"
"\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"
"\n"
"	result[0][0] = m[0][0]; 															\n"
"	result[0][1] = m[0][1]; 															\n"
"	result[0][2] = m[0][2]; 															\n"
"\n"
"	result[1][0] = m[1][0]; 															\n"
"	result[1][1] = m[1][1]; 															\n"
"	result[1][2] = m[1][2]; 															\n"
"\n"
"	result[2][0] = m[2][0]; 															\n"
"	result[2][1] = m[2][1]; 															\n"
"	result[2][2] = m[2][2]; 															\n"
"\n"
"	return result;																		\n"
"}																						\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"
"		fsLightIntensity = clamp(fsLightIntensity,0.0,1.0);								\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"
"}\n"
};
	
	
// 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"
"// 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"
"\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"
"\n"
"/*																							\n"
" * main():																					\n"
" *																							\n"
" * Fragment shader entry point.															\n"
" */																						\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 = min(fsLightIntensity+(1.0-insideSpot)*spotColor,1.0);				\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"	
};

#endif	// INCLUDED_SHADERS3D_H