// vert //

out vec3 view_dir, world_dir;

uniform vec4 view_corners[4], world_corners[4];

void main(void) {
	qgl_FragTexture = qgl_Texture;
	gl_Position = qgl_ftransform();
	view_dir  = view_corners [gl_VertexID].xyz;
	world_dir = world_corners[gl_VertexID].xyz;
}


// frag //

in vec3 view_dir, world_dir;

uniform vec2 dt;
uniform float z_near;
uniform sampler2D tex_coeffs[2];
uniform sampler2D tex_0, tex_1, tex_2, tex_3, tex_4;
uniform samplerCube tex_env;
uniform int lights_start, lights_count;

uniform vec4 fog_color = vec4(0.5, 0.5, 0.5, 1);
uniform float fog_decay = 10, fog_density = 0;
uniform mat3 view_mat;

const vec3 luma = vec3(0.299, 0.587, 0.114);
const float _min_rough = 1.e-8, max_lod = 8;

vec4 pos, lpos, shp;
vec3 li, si, ldir, halfV, bn, bn2, lwdir;
//vec3 vds, vds2;
float rough_diff, rough_spec, dist, NdotL, NdotH, spot, ldist, diff, spec, sdist, shadow;

void calcLight(in int index, in vec3 n, in vec3 v) {
	lpos = qgl_light_position[index].position;
	ldir = lpos.xyz - (pos.xyz * lpos.w);
	ldist = length(ldir);
	ldir = normalize(ldir);
	//ldir = vec3(0,0,1);
	halfV = normalize(ldir + v);
	NdotL = max(dot(n, ldir), 1E-8);
	NdotH = max(dot(n, halfV), 1E-8);
	spot = step(1.001E-8, NdotL) * qgl_light_parameter[index].decay_intensity.w;
#ifdef SPOT
	float scos = max(dot(-ldir, qgl_light_position[index].direction.xyz), 0.);
	spot *= scos * step(qgl_light_parameter[index].angles.w, scos);
	spot *= smoothstep(qgl_light_parameter[index].angles.w, qgl_light_parameter[index].angles.y, scos);
	/*//lwdir = mat3(mat_viewi) * qgl_Light[index].direction.xyz;
	//bn = normalize(cross(lwdir, vec3(1, 0, 0)));
	//bn2 = normalize(cross(lwdir, bn));
	float ds = ldist/200.;//max(abs(sdist) / 5000, 0.02);
	//spot *= clamp(1. - sdist, 0, 1);
	vds = ds * bn.xyz;
	vds2 = ds * bn2.xyz;
	float shadow  = getShadow(index, pos.xyz, vec3(0)) * 3.;
		  shadow += getShadow(index, pos.xyz,   vds                    ) * 2.;
		  shadow += getShadow(index, pos.xyz, - vds                    ) * 2.;
		  shadow += getShadow(index, pos.xyz,             - vds2       ) * 2.;
		  shadow += getShadow(index, pos.xyz,             + vds2       ) * 2.;
		  //shadow += getShadow(index, pos.xyz,   vds       - vds2       ) * 1.5;
		  //shadow += getShadow(index, pos.xyz,   vds       + vds2       ) * 1.5;
		  //shadow += getShadow(index, pos.xyz, - vds       - vds2       ) * 1.5;
		  //shadow += getShadow(index, pos.xyz, - vds       + vds2       ) * 1.5;
		  //shadow += getShadow(index, pos.xyz,   vds + vds              );
		  //shadow += getShadow(index, pos.xyz, - vds - vds              );
		  //shadow += getShadow(index, pos.xyz,             - vds2 - vds2);
		  //shadow += getShadow(index, pos.xyz,             + vds2 + vds2);
		  //shadow += getShadow(index, pos.xyz,   vds + vds - vds2       );
		  //shadow += getShadow(index, pos.xyz, - vds - vds - vds2       );
		  //shadow += getShadow(index, pos.xyz,   vds + vds + vds2       );
		  //shadow += getShadow(index, pos.xyz, - vds - vds + vds2       );
		  //shadow += getShadow(index, pos.xyz,   vds       - vds2 - vds2);
		  //shadow += getShadow(index, pos.xyz,   vds       + vds2 + vds2);
		  //shadow += getShadow(index, pos.xyz, - vds       - vds2 - vds2);
		  //shadow += getShadow(index, pos.xyz, - vds       + vds2 + vds2);
		  //shadow += shadow += getShadow(index, pos.xyz, vds+vds2)*10;
	spot *= mix(1., shadow / 11., shadow_on);*/
#endif
	vec3 dist_decay = vec3(1, ldist, ldist*ldist);
	spot /= dot(qgl_light_parameter[index].decay_intensity.xyz, dist_decay);
	
	float NdotLs = NdotL*NdotL;
	float NdotHs = NdotH*NdotH;
	
	float ndlc = (1. - NdotLs) / NdotLs;
	diff = 2. / (1. + sqrt(1. + (1. - rough_diff) * ndlc));
	//diff = texture(tex_coeffs[0], vec2(roughness, (NdotLs))).r;
	li += spot * diff * qgl_light_parameter[index].color.rgb;
	
	ndlc = (1. - NdotHs) / NdotHs;
	float der = NdotHs * (rough_spec + ndlc);
	spec = rough_spec / (der*der) / 3.1416;
	//spec = texture(tex_coeffs[1], vec2(roughness, (NdotHs))).r;
	si += spot * spec * qgl_light_parameter[index].color.rgb;
}



float GeometrySchlickGGX(float NdotV, float roughness) {
    float a = roughness;
    float k = (a * a) / 2.0;
    float nom   = NdotV;
    float denom = NdotV * (1.0 - k) + k;
    return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
    float NdotV = max(dot(N, V), 0.0);
    float NdotL = max(dot(N, L), 0.0);
    float ggx2 = GeometrySchlickGGX(NdotV, roughness);
    float ggx1 = GeometrySchlickGGX(NdotL, roughness);
    return ggx1 * ggx2;
}  



void main(void) {
	ivec2 tc = ivec2(gl_FragCoord.xy);
	vec4 v1 = texelFetch(tex_1, tc, 0);
	float z = v1.w;
	if (z == 1.) {
		discard;
	}
	pos.w = 1;
	pos.xyz = view_dir * z;
	vec3 v = normalize(-pos.xyz);

	vec4 v0 = texelFetch(tex_0, tc, 0),
		 v2 = texelFetch(tex_2, tc, 0),
		 v3 = texelFetch(tex_3, tc, 0),
		 v4 = texelFetch(tex_4, tc, 0);

	vec3 diffuse       = v0.rgb;
	vec3 normal        = v1.xyz;
	vec3 emission      = v3.rgb;
	float alpha        = v0.a;
	float metalness    = v2.r;
	float roughness    = v2.g;
	float reflectivity = v2.b;
	float NdotV = dot(normal, v);
	float roughness3   = roughness*roughness*roughness;
	//bn  = normalize(vec3(v3.w, v4.zw));
	//bn2 = normalize(cross(n, bn));
	rough_diff = max(roughness, _min_rough);
	rough_spec = max(roughness3, _min_rough);
	float shlick = clamp(metalness + (1 - metalness) * pow(1 - NdotV, 5), 0, 1);

	li = vec3(0.);//qgl_AmbientLight.color.rgb * qgl_AmbientLight.intensity;
	si = vec3(0.);
	for (int i = 0; i < lights_count; ++i)
		calcLight(lights_start + i, normal, v);
	si *= shlick;
	li *= (1 - shlick);
	alpha = min(1, alpha * (1 + shlick));
	
	vec2 brdf = texture(tex_coeffs[0], vec2(NdotV*0.99, roughness*0.995)).rg;
	float env_spec = shlick * brdf.x + brdf.y;
	vec3 spec_col = mix(vec3(1), diffuse, metalness);
	vec3 env_dir = view_mat * reflect(-v, normal);
	vec3 env_col = textureLod(tex_env, env_dir, sqrt(roughness) * max_lod).rgb * spec_col;
	
	vec3 res_col = max(vec3(0), li * diffuse + si * spec_col + emission);
	res_col = mix(res_col, env_col, env_spec * reflectivity);
	
	float plen = length(pos.xyz);
	float fog = 1 - exp(-plen / fog_decay);
	fog = clamp(fog * fog_color.a * fog_density, 0, 1);
	res_col = mix(res_col, fog_color.rgb, fog);
	
	qgl_FragColor = vec4(res_col, alpha);
	
	//vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y);
	//qgl_FragColor.rgb = vec3(shlick * brdf.x + brdf.y);
	//qgl_FragColor.rgb = vec3(alpha);
	//qgl_FragColor.rgb = vec3(textureLod(tex_env, world_dir, 0).rgb);
	//qgl_FragColor.a = 1.;
}
