#version 400 core

#define PI      3.14159265358979323846

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projMatrix;

uniform sampler2D heightTexture;
uniform sampler2D normalTexture;

///////////////////
//Geometry related
//////////////////
uniform int baseX;
uniform int baseY;
uniform int stride;
uniform int lodStep;

uniform vec2 texScale;
uniform vec2 texBias;
uniform vec3 scale;

//////////////////
//Coloring related
//////////////////
uniform float mixStart;
uniform float mixWidth;

uniform vec3 nearColor;
uniform vec3 farColor;
uniform vec3 mixColor;

//////////////////
//Lighting related
//////////////////
uniform vec3 lightDir;

out Vertex
{
	vec3 viewDir;
	vec3 lightDir;
    vec3 color;
    vec2 texCoord;
	vec4 fogParam;
}
vertex;

void main ()
{	
	// Calculate the vertex position based on the VertexID and InstanceID
    int thisX = gl_VertexID % stride + baseX;
    int thisY = gl_InstanceID + gl_VertexID / stride + baseY;

    // Morphing
	vec2 thisXY = vec2 (float (thisX), float (thisY)) * texScale * lodStep;
    float thisZ = texture (heightTexture, thisXY + texBias).r;

    vec3 position = vec3 (thisXY, thisZ) * scale;
    vec3 camera = inverse (viewMatrix)[0].xyz;
    mat4 mv = viewMatrix * modelMatrix;
    vec3 thisPosition = vec3 (mv * vec4 (position, 1));

	//Calculate fog related information
	vec3 fogColor = vec3(0, 0, 0);
	vertex.fogParam.xyz = fogColor;
	vertex.fogParam.w = exp(length(vec4 (thisPosition, 1)) / 900.0) / 2.8;
	
	//Color mixing based on distance
    float thisDist = length (thisPosition);
    float mixFactor = 0;
	
    if (thisDist > mixStart)
    {
        mixFactor = clamp ((thisDist - mixStart) / mixWidth, 0, 1);        
    }
    if (mixFactor < 0.5) 
	{
		vertex.color = mix (nearColor, mixColor, sin (mixFactor * PI));
	}
    else
	{	
		vertex.color = mix (mixColor, farColor, 1 - sin (mixFactor * PI));
	}
	//Output
	vertex.viewDir  = normalize (camera - position);
	vertex.lightDir = lightDir;
    vertex.texCoord = thisXY + texBias;
	gl_Position = projMatrix * vec4 (thisPosition, 1);
}
