#version 120

/*
 Copyright (C) 2010-2012 Kristian Duske
 
 This file is part of TrenchBroom.
 
 TrenchBroom is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 TrenchBroom is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with TrenchBroom.  If not, see <http://www.gnu.org/licenses/>.
 */

uniform float Brightness;
uniform float Alpha;
uniform bool ApplyTexture;
uniform sampler2D FaceTexture;
uniform bool ApplyTinting;
uniform vec4 TintColor;
uniform bool GrayScale;
uniform bool RenderGrid;
uniform float GridSize;
uniform float GridAlpha;
uniform bool GridCheckerboard;
uniform bool ShadeFaces;
uniform bool UseFog;

varying vec4 modelCoordinates;
varying vec3 modelNormal;
varying vec4 faceColor;
varying vec3 viewVector;

void gridCheckerboard(vec2 inCoords) {
    bool evenA = mod(floor(inCoords.x / GridSize), 2) == 0;
    bool evenB = mod(floor(inCoords.y / GridSize), 2) == 0;
    
    if (evenA == evenB)
        gl_FragColor.rgb += vec3(GridAlpha * 0.1);
    else
        gl_FragColor.rgb -= vec3(GridAlpha * 0.1);
}

float getSoftStripes(float value, float gridSize, float stripeSize) {
	float mainVal = value * gridSize;
	float triangle = abs(2.0 * fract(mainVal) - 1.0);
	float filterWidth = fwidth(value);
	float edge = filterWidth * gridSize * 2.0;

	// major line shading, currently set to place a major line every 64 units ( candidate for view preferences ?)
	float mValue = 1.0 / (64.0 * gridSize);
	float triMajor = abs(2.0 * fract(mainVal * mValue) - 1.0);
	float isMajor = step(1.0 - mValue, triMajor);

	float outIntensity = isMajor * 0.7 + 0.85; // tweak intensities here
	float sSize = stripeSize;

	return smoothstep(sSize - edge, sSize + edge, triangle) * outIntensity;
}

void gridLinesSoft(vec2 inCoords, float gridRatio, float gridRatio2, float baseStripeSize, float gridBlend) {
	float stripeRatio = baseStripeSize * gridRatio;
	float stripeRatio2 = baseStripeSize * gridRatio2;
	float stripeSize = 1.0 - stripeRatio;
    float stripeSize2 = 1.0 - stripeRatio2;

	float theGrid, nextGrid;

	theGrid = getSoftStripes(inCoords.x, gridRatio, stripeSize);
	theGrid = max(theGrid, getSoftStripes(inCoords.y, gridRatio, stripeSize));
	nextGrid = getSoftStripes(inCoords.x, gridRatio2, stripeSize2);
	nextGrid = max(nextGrid, getSoftStripes(inCoords.y, gridRatio2, stripeSize2));

	theGrid = mix(theGrid, nextGrid, gridBlend);

	gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(1.0), theGrid * GridAlpha * 0.5);
}

void main() {
	if (ApplyTexture)
		gl_FragColor = texture2D(FaceTexture, gl_TexCoord[0].st);
	else
		gl_FragColor = faceColor;

    gl_FragColor = vec4(vec3(Brightness / 2.0 * gl_FragColor), gl_FragColor.a);
    gl_FragColor = clamp(2.0 * gl_FragColor, 0.0, 1.0);
    gl_FragColor.a = Alpha;

    if (GrayScale) {
        float gray = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));
        gl_FragColor = vec4(gray, gray, gray, gl_FragColor.a);
    }

    if (ApplyTinting) {
        gl_FragColor = vec4(gl_FragColor.rgb * TintColor.rgb * TintColor.a, gl_FragColor.a);
        gl_FragColor = clamp(2.0 * gl_FragColor, 0.0, 1.0);
    }

	if (ShadeFaces) {
		// angular dimming ( can be controlled with dimStrength )
		// TODO: make view option
		float dimStrength = 0.25;
		float angleDim = dot(normalize(viewVector), normalize(modelNormal)) * dimStrength + (1.0 - dimStrength);

		gl_FragColor.rgb *= angleDim;
	}

	if (UseFog) {
        float distance = length(viewVector);

		// TODO: make view options
		vec3 fogColor = vec3(0.5, 0.5, 0.5);
		float maxFogAmount = 0.15;
		float fogBias = 0.0;
        float fogScale = 0.00075;
        float fogMinDistance = 512.0;
        
        float fogFactor = max(distance - fogMinDistance, 0.0) * fogScale;

		//gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, clamp(( gl_FragCoord.z / gl_FragCoord.w ) * fogScale + fogBias, 0.0, maxFogAmount ));
		gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, clamp(fogFactor + fogBias, 0.0, maxFogAmount));
	}

	if (RenderGrid && GridAlpha > 0.0) {
		float normX = abs(modelNormal.x);
		float normY = abs(modelNormal.y);
		float normZ = abs(modelNormal.z);

		float gridThickness = GridSize < 4 ? 0.25 : 0.5;
		float fragDepth = length(viewVector); //gl_FragCoord.z / gl_FragCoord.w,
        float gridBlend = 0;
        float baseGridSize = GridSize;
        float nextGridSize = GridSize;

		if (fragDepth > 1024.0) {
			baseGridSize 	= GridSize * 4.0;
			nextGridSize 	= GridSize * 8.0;
			gridBlend = (fragDepth - 1024.0) / 1024.0;
		} else if(fragDepth > 512.0) {
			baseGridSize 	= GridSize * 2.0;
			nextGridSize 	= GridSize * 4.0;
			gridBlend = (fragDepth - 512.0) / 512.0;
		} else if (fragDepth > 256.0) {
			baseGridSize 	= GridSize;
			nextGridSize 	= GridSize * 2.0;
			gridBlend = (fragDepth - 256.0) / 256.0;
		}

		float blendScale = 2.0; // sharpness of the grid falloff

		gridBlend = clamp(gridBlend * blendScale - (blendScale - 1.0), 0.0, 1.0);

		float gridRatio = 1.0 / baseGridSize;
		float gridRatio2 = 1.0 / nextGridSize;

		vec2 baseCoords; // coordinates used for overlay creation

		if (normX > normY) {
			if (normX > normZ)
				baseCoords = modelCoordinates.yz;
			else
				baseCoords = modelCoordinates.xy;
		} else if (normY > normZ) {
			baseCoords = modelCoordinates.xz;
		} else {
			baseCoords = modelCoordinates.xy;
		}

		if (GridCheckerboard) {
			gridCheckerboard(baseCoords);
		} else {
			gridLinesSoft(baseCoords, gridRatio, gridRatio2, gridThickness, gridBlend);
		}
	}
}