#ifdef GL_ARB_texture_rectangle
	#extension GL_ARB_texture_rectangle : enable
#endif

uniform sampler2DRect tex_entry;
uniform sampler2DRect tex_exit;
uniform sampler3D tex_intensity;
uniform sampler1D tex_transfer;
uniform float samplerate;
uniform float texwidth;
uniform float texheight;
uniform float texdepth;
uniform float minSamplerate;
uniform int trilinear;
uniform int doCompositing;


float trilinearfilter(in vec3 point){

	float posx = floor(point.x*texwidth);
	float posy = floor(point.y*texheight);
	float posz = floor(point.z*texdepth);
	
	
	//get 8 positions around the actual sample point
	vec4 pos1 = texture3D(tex_intensity, vec3(posx/texwidth, posy/texheight, posz/texdepth));
	vec4 pos2 = texture3D(tex_intensity, vec3((posx+1.0)/texwidth, posy/texheight, posz/texdepth));
	vec4 pos3 = texture3D(tex_intensity, vec3((posx+1.0)/texwidth, (posy+1.0)/texheight, posz/texdepth));
	vec4 pos4 = texture3D(tex_intensity, vec3(posx/texwidth, (posy+1.0)/texheight, posz/texdepth));
	
	vec4 pos5 = texture3D(tex_intensity, vec3(posx/texwidth, posy/texheight, (posz+1.0)/texdepth));
	vec4 pos6 = texture3D(tex_intensity, vec3((posx+1.0)/texwidth, posy/texheight, (posz+1.0)/texdepth));
	vec4 pos7 = texture3D(tex_intensity, vec3((posx+1.0)/texwidth, (posy+1.0)/texheight, (posz+1.0)/texdepth));
	vec4 pos8 = texture3D(tex_intensity, vec3(posx/texwidth, (posy+1.0)/texheight, (posz+1.0)/texdepth));
	
	
	posx = (point.x*texwidth)-posx;
	posy = (point.y*texheight)-posy;
	posz = (point.z*texdepth)-posz;
	

	float am1 = pos1.x*((1.0-posx)*(1.0-posy)*(1.0-posz));
	float am2 = pos2.x*(posx*(1.0-posy)*(1.0-posz));
	float am3 = pos3.x*(posx*posy*(1.0-posz));
	float am4 = pos4.x*((1.0-posx)*posy*(1.0-posz));
	float am5 = pos5.x*((1.0-posx)*(1.0-posy)*posz);
	float am6 = pos6.x*(posx*(1.0-posy)*posz);
	float am7 = pos7.x*(posx*posy*posz);
	float am8 = pos8.x*((1.0-posx)*posy*posz);
	
	return am1+am2+am3+am4+am5+am6+am7+am8;
}



void main()
{
	const int maxrange = 347;
	vec4 result = vec4(0.0, 0.0, 0.0, 0.0);
	vec4 entry_point = texture2DRect(tex_entry, vec2(gl_TexCoord[0]));
	vec4 exit_point = texture2DRect(tex_exit, vec2(gl_TexCoord[0]));
	float sampleCount = distance(entry_point, exit_point)/samplerate;
	int maxiter = int(floor(sampleCount));
	vec3 diff=vec3(exit_point-entry_point)/sampleCount;
	
	if (entry_point.w == 0.0) discard;
	else
	{
		vec3 point = entry_point.xyz;
		result = vec4(0.0, 0.0, 0.0, 0.0);
		for(int i = 0; i < maxiter; i++)
		{
			if (doCompositing == 1)
			{
				//**** Compositing *****
				
				if (result.w < 1.0)
				{
					float intensity;
					if(trilinear == 1)
					{
						intensity = trilinearfilter(point);
					}
					else
					{
						vec4 intensitytex = texture3D(tex_intensity, point);
						intensity = intensitytex.x;
					}
					
					vec4 transfered = texture1D(tex_transfer, intensity);
					
					// The last factor (samplerate/minSamplerate) is set for keeping constant opacity
					float currentOpacity = (1.0 - result.w) * transfered.w * (samplerate / minSamplerate);

					result.xyz += currentOpacity * transfered.xyz;
					result.w += currentOpacity;

					point += diff;
				}
			}
			else
			{
				//**** Averaging *****

				vec4 intensity = texture3D(tex_intensity, point);
				vec4 transfered = texture1D(tex_transfer, intensity.x);
				
				result.xyz += transfered.w * transfered.xyz;
				result.w += transfered.w;
				point += diff;
			}
		}
	}
	
	if (doCompositing != 1)
		result /= float(maxiter);
	
	gl_FragColor = result;
}