#version 330 core

uniform sampler2D dirt;
uniform sampler2D dirt_bump;
uniform sampler2D grass;
uniform sampler2D grass_bump;
uniform sampler2D rock;
uniform sampler2D rock_bump;
uniform sampler2D snow;
uniform sampler2D snow_bump;
uniform sampler2D detail;
uniform sampler2D detail_bump;
uniform sampler2D road;
uniform sampler2D road_bump;
uniform sampler2D road_opacity;

// .x = min height, .y = max height, .z = min slope, .w = max slope
uniform vec4 rockParams;
uniform vec4 dirtParams;
uniform vec4 snowParams;
uniform vec4 grassParams;
// .x=grass, .y=dirt, .z=rock, .w=snow
uniform vec4 mapStrength;

uniform vec2 detailParams; // x=tex coord scale, y=detail strength
uniform vec2 roadParams; // x=tex coord scale

in vec2 pos_zw;
in vec3 terrain_normal;
in vec3 normal;
in vec3 ec_pos;
in vec3 uvw;
in vec2 uv;
in float height;

layout (location = 0) out vec4 diffuse_specular;
layout (location = 1) out vec4 normal_depth;

#include <deferred/tangentspace_include.frag>

float computeWeight(float value, float minExtent, float maxExtent)
{
	if ( value >= minExtent && value <= maxExtent )
	{
		float range = maxExtent - minExtent;

		float weight = value - minExtent;

		weight *= 1.0 / range;
		weight -= 0.5;
		weight *= 2.0;

		weight *= weight;

		weight = 1.0 - abs(weight);
		weight = clamp(weight, 0.001, 1.0);

		return weight;
	}
	else
	{
		return 0.0;
	}
}


vec4 weightedTexture(in sampler2D smp, in vec3 tpw, in vec3 uvw)
{
	vec4 temp = texture(smp, uvw.xy) * tpw.z;
	temp += texture(smp, uvw.yz) * tpw.x;
	temp += texture(smp, uvw.xz) * tpw.y;
	return temp;
}


vec3 weightedTextureBump(in sampler2D smp, in mat3 tsr[3], in vec3 tpw, in vec3 uvw)
{
	vec3 temp = (tsr[0] * (texture(smp, uvw.xy).xyz*2.0 - vec3(1.0))) * tpw.z;
	temp += (tsr[1] * (texture(smp, uvw.yz).xyz*2.0 - vec3(1.0))) * tpw.x;
	temp += (tsr[2] * (texture(smp, uvw.xz).xyz*2.0 - vec3(1.0))) * tpw.y;
	return temp;
}

void main()
{
	// basic weights
	vec3 n = normalize(terrain_normal);
	float slope = 1.0 - n.y;

	vec4 weights;
	weights.x = computeWeight(height, rockParams.x, rockParams.y) * computeWeight(slope, rockParams.z, rockParams.w) * mapStrength.z;
	weights.y = computeWeight(height, dirtParams.x, dirtParams.y) * computeWeight(slope, dirtParams.z, dirtParams.w) * mapStrength.y;
	weights.z = computeWeight(height, snowParams.x, snowParams.y) * computeWeight(slope, snowParams.z, snowParams.w) * mapStrength.w;
	weights.w = computeWeight(height, grassParams.x, grassParams.y) * computeWeight(slope, grassParams.z, grassParams.w) * mapStrength.x;
	weights *= 1.0 / (weights.x + weights.y + weights.z + weights.w);

	// planar slope
	vec3 tpw = abs(n);
	tpw = (tpw - vec3(0.2)) * 7.0;
	tpw = max(tpw, vec3(0.0));
	tpw *= 1.0 / (tpw.x + tpw.y + tpw.z);

	// diffuse
	vec4 final = weightedTexture(dirt, tpw, uvw) * weights.y;
	final += weightedTexture(grass, tpw, uvw) * weights.w;
	final += weightedTexture(rock, tpw, uvw) * weights.x;
	final += weightedTexture(snow, tpw, uvw) * weights.z;

	float road_vis = pow(texture(road_opacity, uv).r, 4.0);
	final = mix(final, weightedTexture(road, tpw, uvw*roadParams.x), road_vis);

	final += (weightedTexture(detail, tpw, uvw*detailParams.x)*2.0 - vec4(1.0)) * detailParams.y;

	diffuse_specular.rgb = final.rgb;
	diffuse_specular.a = 0.0;

	// bump
	mat3 tsr[3];
	vec3 t, b;
	n = normalize(normal);

	per_fragment_tangent_space(ec_pos, uvw.xy, t, b);
	tsr[0] = mat3(t, b, n);

	per_fragment_tangent_space(ec_pos, uvw.yz, t, b);
	tsr[1] = mat3(t, b, n);

	per_fragment_tangent_space(ec_pos, uvw.xz, t, b);
	tsr[2] = mat3(t, b, n);

	vec3 bump = weightedTextureBump(dirt_bump, tsr, tpw, uvw) * weights.y;
	bump += weightedTextureBump(grass_bump, tsr, tpw, uvw) * weights.w;
	bump += weightedTextureBump(rock_bump, tsr, tpw, uvw) * weights.x;
	bump += weightedTextureBump(snow_bump, tsr, tpw, uvw) * weights.z;

	bump = mix(bump, weightedTextureBump(road_bump, tsr, tpw, uvw*roadParams.x), road_vis);

	bump += weightedTextureBump(detail_bump, tsr, tpw, uvw*detailParams.x) * detailParams.y;

	normal_depth.xyz = normalize(bump);
	normal_depth.w = pos_zw.x/pos_zw.y;
}
