/*
This file is part of Caelum.
See http://www.ogre3d.org/wiki/index.php/Caelum 

Copyright (c) 2006-2007 Caelum team. See Contributors.txt for details.

Caelum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Caelum 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Caelum. If not, see <http://www.gnu.org/licenses/>.
*/

// Return fogging through a layer of fog which drops exponentially by height.
// Foginess at a certain point is exp(-verticalDecay * (h - fogLevel));
// A fogLevel from the ground fogginess is 1.
float ExpGroundFog (float invSinView, float h1, float h2,
		float density, float verticalDecay, float fogLevel)
{
	// Integrate fog on a vertical line from h1 to h2.
	float vFog = (-1 / verticalDecay) * 
		(exp (-verticalDecay * (h2 - fogLevel)) -
		 exp (-verticalDecay * (h1 - fogLevel)));
	return 1 - exp (-density * invSinView * vFog);
}

// Just like ExpGroundFog with infinite h2
float ExpGroundFogInf (float invSinView, float h1,
		float density, float verticalDecay, float fogLevel)
{
	// Integrate fog on a vertical line from h1 to h2.
	float vFog = exp (-verticalDecay * (h1 - fogLevel)) / verticalDecay;
	return 1 - exp (-density * invSinView * vFog);
}

// Entry point for GroundFog vertex program.
void GroundFog_vp
(
		float4 position : POSITION,
		
		out float4 oPosition : POSITION,
		out float4 worldPos : TEXCOORD0,
		
		uniform float4x4 worldViewProj,
		uniform float4x4 world
) {
	oPosition = mul(worldViewProj, position);
	worldPos = mul(world, position);
}

// Entry point for GroundFog fragment program.
void GroundFog_fp
(
		float3 worldPos : TEXCOORD0,

		uniform float3 camPos,
		uniform float4 fogColour,
		uniform float fogDensity,
		uniform float fogVerticalDecay,
		uniform float fogGroundLevel,
				
		out float4 oCol : COLOR
) {
	float h1 = camPos.y;
	float h2 = worldPos.y;
	float invSinView = length(camPos - worldPos) / (h2 - h1);
	float fog = ExpGroundFog(invSinView,
			h1, h2, fogDensity, fogVerticalDecay, fogGroundLevel);

	oCol.rgb = fogColour.rgb;
	oCol.a = fog;
}

// Entry point for GroundFogDome vertex program.
void GroundFogDome_vp
(
		in float4 position : POSITION,
		out float4 oPosition : POSITION,
		out float3 relPosition : TEXCOORD0,
		uniform float4x4 worldViewProj
) {
	oPosition = mul(worldViewProj, position);
	relPosition = normalize(position);
}

// Entry point for the GroundFogDome fragment program.
void GroundFogDome_fp
(
		in float3 relPosition : TEXCOORD0,

		uniform float cameraHeight,
		uniform float4 fogColour,
		uniform float fogDensity,
		uniform float fogVerticalDecay,
		uniform float fogGroundLevel,

		out float4 oCol : COLOR
) {
	// Fog magic.
	float invSinView = 1 / (relPosition.y);
	float h1 = cameraHeight;
	float aFog;
	
	if (invSinView < 0) {
		// Gazing into the abyss
		aFog = 1;
	} else {
		aFog = saturate (ExpGroundFogInf (
			invSinView, h1, fogDensity, fogVerticalDecay, fogGroundLevel));
	}
	
	oCol.a = aFog;
	oCol.rgb = fogColour.rgb;
}