

layout(location = 0, index = 0) out vec4 outcol0;

uniform sampler2DShadow shadowmap_tex;
uniform sampler2D bgdepth_tex;
uniform sampler2D frontpos_tex;

uniform mat4 mat_objectToShadowmap;
uniform mat4 mat_worldToObj;
uniform mat4 mat_viewToWorld;
uniform mat4 mat_invCamProj;
//TODO: uniform mat4 mat_viewToObj;
uniform mat4 mat_projtoshadow;

uniform vec4 resolution;
uniform vec4 time_ms;
//uniform vec4 objscale;

uniform vec4 os_eyepos;

uniform vec4 fogColor;

in vec4 v2f_pos_ls;
in vec4 v2f_pos_os;


// ====
float sat( float v ) {
	return clamp( v, 0, 1 );
}

// ====
float nrand( vec2 n ) {
  return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453);
}

// ====
//note: [-1;1]
float srand( vec2 n ) {
  return -1 + 2*nrand(n);
}

// ====
float calc_lightfalloff( vec3 in_sms_pos )
{
	float light_falloff = 1.0f - sat( 2*length( in_sms_pos.xy-vec2(0.5f) ) );
	light_falloff = sat( 4*light_falloff );
	return light_falloff * light_falloff;
}

vec3 depthsample_to_sms( vec2 fs_texcoord_nm, float depth_sample )
{
	vec4 ss_pos = vec4( fs_texcoord_nm*2-1, (2*depth_sample-1), 1 );
	vec4 ls_pos = mat_projtoshadow * ss_pos;
	vec3 sms_pos = 0.5f + 0.5f * ls_pos.xyz / ls_pos.w; //TODO: build into matrix on cpu
	//...phew
	return sms_pos;
}

// ====
void main()
{

	vec2 fs_texcoord_nm = gl_FragCoord.xy * resolution.zw;
	float bg_depth_sample = texture( bgdepth_tex, fs_texcoord_nm ).r;

	float frontpos_depth_sample = texture( frontpos_tex, fs_texcoord_nm ).r;

	//note: nearclipped
	if ( frontpos_depth_sample > 0.99999f ) {
		frontpos_depth_sample = 0.0f;
	}

	//note: manual zbuffering
	if( frontpos_depth_sample > bg_depth_sample ) {
		discard;
	}

	vec4 ls_back_pos = v2f_pos_ls;
	vec3 sms_back_pos = 0.5f + 0.5f * ls_back_pos.xyz / ls_back_pos.w;

	vec3 sms_bg_pos = depthsample_to_sms( fs_texcoord_nm, bg_depth_sample );
	vec3 sms_front_pos = depthsample_to_sms( fs_texcoord_nm, frontpos_depth_sample );

	vec3 frust_diffvec = sms_front_pos - sms_back_pos;
	vec3 bg_diffvec = sms_front_pos - sms_bg_pos;
	float frustum_len = length( frust_diffvec ); //TODO: len^2
	float bg_len = length( bg_diffvec );
	float bg_mask = step( bg_depth_sample, 0.99999f ); //note: don't use bg when inf away
	bg_mask *= step( 0.0f, dot( frust_diffvec, bg_diffvec) ); //note: only use bg if closer than backfacing

	vec3 sms_start_pos = sms_front_pos;
	vec3 sms_end_pos = (bg_len < (frustum_len * bg_mask) ) ? sms_bg_pos : sms_back_pos;
	float trace_length = length( sms_end_pos - sms_start_pos );

	const int num_steps = 32;

	const float num_steps_f = float( num_steps );
	const float inv_num_steps = 1.0f / num_steps_f;
	float stepsiz = trace_length * inv_num_steps;

	vec3 sms_tracevec = normalize( sms_end_pos - sms_start_pos );
	vec3 stepvec = stepsiz * sms_tracevec;

	//TODO: setting rnd to 0 is around 0.5-1.0ms - probably texture-cache
	float rnd = srand( gl_FragCoord.xy + fract(time_ms.xx) * 0.001f );
	vec3 sms_pos = sms_start_pos + 0.5f * rnd * stepvec;

	//TODO: move offset to shadow-fill pass
	const float eps = 0.0f; 

	float its = 0;
	for ( int i=0; i<num_steps; ++i )
	{
		sms_pos += stepvec;

		float shadowfact = texture( shadowmap_tex, sms_pos.xyz + vec3(0,0,eps) );

		//TODO: falloff with distance from light
		float inc_its = calc_lightfalloff( sms_pos );
		its += inc_its * shadowfact;
	}
	its *= inv_num_steps;
	its *= trace_length;

	outcol0 = vec4( fogColor.rgb*its, 0 ); //fogColor.a*its ); //note: premult alpha

	//DEBUG
	//outcol0 = vec4( vec3( trace_length ), 1 );
}
