// -*-C++-*-
#version 120

#define BLADE_FRACTION 0.1
#define MAX_LAYERS 20
#define MAX_DISTANCE 1000.0

uniform float visibility;
uniform float scattering;
uniform float overlay_bias;
uniform float season;
uniform float max_height;
uniform float grass_density;
uniform float grass_modulate_height_min;

uniform float wind_x;
uniform float wind_y;

uniform float wash_x;
uniform float wash_y;
uniform float wash_strength;

uniform int grass_modulate_by_overlay;
uniform int grass_groups;
uniform int wind_effects;

uniform sampler2D colorTex;
uniform sampler2D densityTex;

uniform float osg_SimulationTime;

varying vec2 g_rawpos;                  // Horizontal position in model space
varying float g_distance_to_eye;        // Distance to the camera. Layers were disregarded
varying float g_layer;				       // The layer where the fragment lives (0-1 range)

float rand2D(in vec2 co);
float Noise2D(in vec2 co, in float wavelength);
vec3 filter_combined (in vec3 color) ;

float getShadowing();


float map(float s, float a1, float a2, float b1, float b2)
{
    return b1+(s-a1)*(b2-b1)/(a2-a1);
}

float decodeBinary(float n, float layer)
{
	return float(mod(floor(n*pow(0.5, layer)), 2.0));
}


float bladeNoise2D(in float x, in float y, in float dDensity, in float layer, in float d_factor, in float h_factor)
{
	float integer_x    = x - fract(x);
    float fractional_x = x - integer_x;

    float integer_y    = y - fract(y);
    float fractional_y = y - integer_y;

	if (rand2D(vec2(integer_x+1.0, integer_y +1.0)) > dDensity)
		{return 0.0;}

	float hfact =  0.7 + 0.3 * (rand2D(vec2(integer_x, integer_y + 2.0)));
	hfact *= h_factor;
	
	if (layer > hfact) {return 0.0;}	
		
    float xoffset = (rand2D(vec2(integer_x, integer_y)) -0.5);
    float yoffset = (rand2D(vec2(integer_x+1.0, integer_y)) - 0.5);
	
	float xbend =  (rand2D(vec2(integer_x+1.0, integer_y + 1.0)) - 0.5);
	float ybend =  (rand2D(vec2(integer_x, integer_y + 1.0)) - 0.5);
	float fraction = BLADE_FRACTION * (0.5 + 0.5 * (1.0 - smoothstep(0.5, 1.0, layer)));

	float bend = 0.5 * layer * layer;
	
	vec2 truePos = vec2 (0.5 + xoffset * (1.0 - 2.0 * BLADE_FRACTION) + xbend * bend, 0.5 + yoffset * (1.0 -2.0 * BLADE_FRACTION) +  ybend * bend);

	float distance = length(truePos - vec2(fractional_x, fractional_y));
	return 1.0 - step (fraction * d_factor, distance);
}

float BladeNoise2D(in vec2 coord, in float wavelength, in float dDensity, in float layer, in float d_factor, in float h_factor)
{
return bladeNoise2D(coord.x/wavelength, coord.y/wavelength, dDensity, layer, d_factor, h_factor);
}

void main()
{

	if (season > 1.6) {discard;}
	if (g_distance_to_eye > MAX_DISTANCE) {discard;}

	vec2 texCoord = gl_TexCoord[0].st;
		
	if (wind_effects > 1)
	{
	
		vec2 eyePos = (gl_ModelViewMatrixInverse * vec4 (0.0, 0.0, 0.0, 1.0)).xy;
		
		vec2 washDir = vec2 (wash_x, wash_y) - (g_rawpos - eyePos);
		float washStrength = 20.0 * min(14.0 * wash_strength/(length(washDir) + 1.0), 1.0);
		washStrength *= (1.0 - 0.8 * sin(20.0 * osg_SimulationTime + length(washDir) + dot(normalize(washDir), vec2(1.0, 0.0))));
				
		vec2 windDir = normalize(vec2 (max(wind_x, 0.1), wind_y) + washStrength * vec2 (-washDir.y, washDir.x));
		
		float windStrength = 0.5 * length(vec2 (wind_x, wind_y)) + washStrength;
		float windAmplitude = 1.0 + 0.3 * windStrength;
		float sineTerm = sin(0.7 * windStrength * osg_SimulationTime + 0.05 * (g_rawpos.x + g_rawpos.y));
		sineTerm = sineTerm + sin(0.6 * windStrength * osg_SimulationTime + 0.04 * (g_rawpos.x + g_rawpos.y));
		sineTerm = sineTerm + sin(0.44 * windStrength * osg_SimulationTime + 0.05 * (g_rawpos.x + g_rawpos.y));
		sineTerm = sineTerm/3.0;
		sineTerm = 5.0 * sineTerm * sineTerm;

		float windDisplacement = pow(g_layer/32.0, 2.0) * clamp((windStrength + windAmplitude * sineTerm), -35.0, 35.0);

		texCoord += (windDisplacement * windDir);
	}

	

	
	float noise_1m = Noise2D(g_rawpos.xy, 1.0); 
	float noise_2m = Noise2D(g_rawpos.xy, 2.0); ;
	float noise_10m = Noise2D(g_rawpos.xy, 10.0);
	
	float h_factor;
	float overlay_mix = smoothstep(0.45, 0.65, overlay_bias + (0.5 * noise_1m + 0.1 * noise_2m + 0.4 * noise_10m));
	
	if (grass_modulate_by_overlay == 1)
			{h_factor = grass_modulate_height_min + (1.0 - grass_modulate_height_min) * (1.0 - overlay_mix) ;}
	else	
			{h_factor = 1.0;}
	
	float value = 0.0;
	
	float d_fade =smoothstep(100.0, MAX_DISTANCE, g_distance_to_eye);
	float d_factor = 1.0 + 1.0 * d_fade;
	d_factor *= clamp(max_height/0.3,0.5, 1.0);
	
	float bladeFlag = BladeNoise2D(texCoord, 0.015, grass_density, g_layer, d_factor, h_factor);
	if (grass_groups >1) {bladeFlag += BladeNoise2D(texCoord, 0.01, grass_density, g_layer, d_factor, h_factor);}
	if (grass_groups >2) {bladeFlag += BladeNoise2D(texCoord, 0.007, grass_density, g_layer, d_factor, h_factor);}

		
	if (bladeFlag > 0.0) {value = 1.0;}
	else {discard;}
	
	vec3 texel = texture2D(colorTex, texCoord).rgb;

	// autumn coloring
	
	texel.r = min(1.0, (1.0 + 2.5  * 0.1 * season) * texel.r);
	texel.g = texel.g;
	texel.b = max(0.0, (1.0 - 4.0  * 0.1 * season) *  texel.b);
	float intensity = length(texel.rgb) * (1.0 - 0.5 * smoothstep(1.1,2.0,season)) * mix(0.3, 1.0, getShadowing());
	texel.rgb = intensity * normalize(mix(texel.rgb, vec3(0.23,0.17,0.08), smoothstep(1.1,2.0, season)));
		
	float base_alpha = clamp(0.4 * max_height/0.3, 0.4, 1.0);
	value*= base_alpha * (1.0 - d_fade);
	
	value *= 1.0 - smoothstep(visibility* 0.5, visibility, g_distance_to_eye);
	value= clamp(value, 0.0, 1.0);
	
	texel *= length(gl_LightSource[0].diffuse.rgb)/1.73 * scattering;
	texel = clamp(texel, 0.0, 1.0);
	
	
	vec4 fragColor = vec4 (texel, value);
	fragColor.rgb = filter_combined(fragColor.rgb);

    gl_FragColor = fragColor;
}