#extension GL_ARB_texture_rectangle : enable

//Variables of type uniform allow the application 
//to provide relatively infrequently changing values to both vertex shaders and fragment shaders.

uniform sampler3D tex_intensity;
uniform sampler3D tex_gradients;
uniform sampler2DRect tex_entry;
uniform sampler2DRect tex_exit;
uniform sampler2DRect tex_postprocessing;
uniform sampler1D tex_transfer;
uniform int renderpass;
uniform int rendermode;
uniform int lighting;
uniform int stepmode;
uniform int applytransfer;
uniform int modi;
uniform float dz; //Schrittweite
//uniform float isoValue;
uniform float lightDirWorldX;
uniform float lightDirWorldY;
uniform float lightDirWorldZ;

varying vec3 view;

void raycasting_comp() {
	const int maxrange = 347;
	
	vec4 entry_point = texture2DRect(tex_entry, gl_FragCoord.xy); //FragCoord gibt einem die Coordinates des Fragmentes an
	vec4 exit_point = texture2DRect(tex_exit, gl_FragCoord.xy);

	vec4 diff, amb, spec; //Lichtwerte diffuse, ambient, specular
	vec4 globalAmb = vec4(0.0, 0.0, 0.0, 0.0);

	float NdotL, NdotH, gradientMulti; //Normal dot Lightdir, Normal dot LightDir+View

	vec3 point = entry_point.xyz;
	vec3 lightDir, normal, grad, H; //H = Lightdir + view
	vec4 intensity, transferred, color;
	vec4 result = vec4(0.0, 0.0, 0.0, 0.0);
	
	vec3 negV = normalize(-view);
	
	float dist = distance(entry_point, exit_point);
	float distPerStep = dist / dz;
	int maxiter = int(floor(distPerStep)); //max an Iterationen; floor: Returns a value equal to the nearest integer that is less than or equal to x.
	vec3 exitMinusEntry = exit_point.xyz - entry_point.xyz;
	vec3 difference = exitMinusEntry / distPerStep;
	

	if(entry_point.w == 0.0)
	{
		discard;	//Abbruch
	}
	else
	{
		float numSamples = 0.0;
	
		for(int i = 0; i < maxrange; i++)
		{	//vec4 texture3D (sampler3D sampler, vec3 coord) Use the texture coordinate coord to access the 3D texture currently specified by sampler
			intensity = texture3D(tex_intensity, point.xyz);
			transferred = texture1D(tex_transfer, intensity.x);
			grad = texture3D(tex_gradients, point.xyz).xyz;
			grad = (grad * 2.0) - 1.0; // transform gradients from [0...1] into real dimensions
			grad = vec3(gl_NormalMatrix * grad);
		
		
			if(lighting == 1)  //lighting enabled
			{
				normal = normalize(grad);
				lightDir = normalize(vec3(gl_ModelViewMatrix * vec4(lightDirWorldX, lightDirWorldY, lightDirWorldZ, 1.0)) - view);
			
				H = normalize(lightDir + negV);
			
				diff = 1.0 * transferred * gl_LightSource[0].diffuse;
				amb = 1.0 * transferred * gl_LightSource[0].ambient;
				spec = 1.0 * transferred * gl_LightSource[0].specular;
			
				NdotL = max(dot(normal, lightDir), 0.0);
			
				color = globalAmb;
				
				//Farbberechnung wie in CG1 gelernt
				if(NdotL > 0.0)
				{
					color += globalAmb;
					color += amb;
					color += diff * NdotL;
					NdotH = max(dot(normal, H), 0.0);
					color += spec * pow(NdotH, gl_FrontMaterial.shininess);
				}
				
				result.xyz += (1.0 - result.w) * transferred.w * color.xyz;
				result.w += (1.0 - result.w) * transferred.w;
			
			
			}//end lighting enabled
			else
			{
				result.xyz += (1.0 - result.w) * transferred.w * transferred.xyz;
				result.w += (1.0 - result.w) * transferred.w;
			}
	
			
			if(stepmode == 0) //adaptive Schrittweite erlaubt
			{
				gradientMulti = 1.0 / length(grad) / 80.0;
				point += gradientMulti * exitMinusEntry;
				if((length(point - entry_point.xyz) >= dist) || (result.w >= 1.0))
				{
					break;
				}
			}
			else
			{
				point += difference;
				if((result.w >= 1.0) || (i >= maxiter))
				{
					break;
				}
			}
		
		
		}//end for
	
		
		//Farbzuweisung fuer das Fragment
		gl_FragColor = result;
	
	}

}

//Muss gemacht werde, da mit den Shadern dann die fixed Pipeline floeten geht
void slicing()
{
	vec3 intensity = texture3D(tex_intensity, gl_TexCoord[0].stp).xyz;
	if(applytransfer == 0)
	{
		gl_FragColor = vec4(intensity.x, intensity.y, intensity.z, 1.0);
	}
	else
	{
		gl_FragColor = texture1D(tex_transfer, intensity.x);
	}

}


//apply a 3x4 gaussian filter
//texture coordinates of a 2DRect-texture are normally not normalized!
void postprocessing()
{
	gl_FragColor =	(texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s - 1.0, gl_TexCoord[5].t + 1.0)) + 
					2.0 * texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s, gl_TexCoord[5].t + 1.0)) + 
					texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s + 1.0, gl_TexCoord[5].t + 1.0)) + 
					2.0 * texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s - 1.0, gl_TexCoord[5].t)) +
					4.0 * texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s, gl_TexCoord[5].t)) +  
					2.0 * texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s + 1.0, gl_TexCoord[5].t)) + 
					texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s + 1.0, gl_TexCoord[5].t + 1.0)) + 
					2.0 * texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s, gl_TexCoord[5].t - 1.0)) + 
					texture2DRect(tex_postprocessing, vec2(gl_TexCoord[5].s + 1.0, gl_TexCoord[5].t - 1.0))) / 16.0;
		

}


void main()
{
	if(renderpass == 1)	//direction texture
	{
		gl_FragColor = vec4(gl_TexCoord[0].x, gl_TexCoord[0].y, gl_TexCoord[0].z, 1.0);
	}
	else if(renderpass == 3)	//postprocessing
	{
		postprocessing();
	}
	else
	{
		if(modi == 0) //slicing
		{
			slicing();
		}
		else //raycasting
		{
			if(rendermode == 0)
			{
				raycasting_comp();
			}
		}
		
	}

}











