//
// Common atmosphere functions
//

//
// Sky shader
// Based on Sean O'Neill's work in GPU Gems 2
// A night texture is used which comes on at night.
//

// Height (scale) at which the average density is located
float fScaleDepth=0.25f;

float CloudCoverAlpha(float a,float clouds)
// Calculates alpha for cloud cover parameter 'clouds'
// Multiple your sky cloud color with this to get affected by the 'clouds' parameter.
{
  // Sharpen color curve and add a little offset for low cloud values
  return max(1/clouds*(a-1)+1-(1-clouds)*0.7,0);
}

float scale(float cosx)
// Something with the atmosphere
{
  float x = 1.0 - cosx;
  return fScaleDepth * exp(-0.10287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}

float CalculateAtmosphereExtinction(
  float3 position,
  float  atmosRayleigh,
  float  atmosMie
)
// For vertex shaders
// Calculate extinction for the atmosphere.
// Sort of a fog calculation; calculates amount of material color vs atmosphere color.
// A value of 1 means full material color, 0 means fully atmosphere (no material color
// remaining).
{
  //float s=position.z;       // A bit faster but uglier
  float s=length(position);
  float e;
  
  // We need to get fog whiteout to avoid clipping
  //const float extinctionFactor=1.0;
  //e=extinctionFactor*exp(-s*(atmosRayleigh+atmosMie));   // Could use outPosition.z
  e=exp(-s*(atmosRayleigh+atmosMie));
  //OUT.extinction=saturate(OUT.extinction);		// 0..1
  return e;
}

void CalculateAtmosphere(
  // Inputs
  float  atmosRayleigh,
  float  atmosMie,
  float  sunIntensity,
  float3 v3CameraPos,
  float3 Position,             // World space (model-transformed) position
  float3 lightDirection,
  // Outputs
  out float4 RayleighColor,
  out float4 MieColor,
  out float3 Direction
)
// For vertex shaders.
// Calculate atmospherical effects based on fog parameters and position.
// Returns Rayleigh & Mie colors that need to be interpolated across the polygon
// and mixed with 'GetSkyColor' below to generate a final sky color.
// (in other words, you need to pass them to the fragment shader)
{
/*
  // Get terrain fogging quickly
  sunIntensity+=terrain*sunIntensity*100;
  atmosRayleigh+=terrain*atmosRayleigh;
  atmosMie+=terrain*atmosMie;
*/

  // Constants
  const float PI=3.141592653589;
  const float fInnerRadius=63789000;
  const float fOuterRadius=fInnerRadius+40000; //fInnerRadius*1.025;
  const float fScale=1/(fOuterRadius-fInnerRadius);
  const float fScaleOverScaleDepth=fScale/fScaleDepth;
  float Kr4PI=atmosRayleigh*4.0*PI;
  float Km4PI=atmosMie*4.0*PI;
  float KrESun=atmosRayleigh*sunIntensity;
  float KmESun=atmosMie*sunIntensity;
  const int   numSamples=1;
  
  // Wavelengths according to forum post
  //float3 v3InvWavelength=float3(1.0f/pow(0.650f, 4.0f),1.0f / pow(0.570f, 4.0f),1.0f / pow(0.475f, 4.0f));
  //float3 v3InvWavelength=float3(5.602, 9.47328, 19.6438);
  // Wavelengths from Bruneton/Neyret; gives a more reddish sky
  float3 v3InvWavelength=float3(5.8,13.5,33.1);

  // Virtual sky dome distance
  const float domeRadius=10000.0;
   
  // Conversions
  float3 v3LightDir=lightDirection;

  // Added by RvG
  float fCameraHeight=fInnerRadius+v3CameraPos.y;

  // Get the ray from the camera to the vertex, and it's length (far point)
  float3 v3Pos = Position.xyz; // / fSkydomeRadius; // + fInnerRadius; 

  // Scale original vertex to a dome location
  float3 v3Ray=v3Pos-v3CameraPos;
  v3Pos=(domeRadius/length(v3Ray))*v3Ray;         // 10000 = sky dome radius

  // Recalculate ray with dome-aligned position
  v3Ray = v3Pos - v3CameraPos;

  // Limit to horizon level; lower than that no gradient (calculations will fail)
  v3Ray.y=max(v3Ray.y,0);
	
  float fFar = length(v3Ray);
  //v3Pos=(domeRadius/fFar)*v3Ray;
	
  // Normalize ray
  v3Ray /= fFar;
  
  // Fix Y positions (RvG hack)
  v3Pos.y+=fInnerRadius;
  v3CameraPos.y+=fInnerRadius;
  		
	// Calculate the ray's starting position, then calculate its scattering offset
	float3 v3Start = v3CameraPos;
	float fHeight = length(v3Start);
	float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));
	float fStartAngle = dot(v3Ray, v3Start) / fHeight;
	float fStartOffset = fDepth * scale(fStartAngle);

	// Init loop variables
	float fSampleLength = fFar / numSamples;
	float fScaledLength = fSampleLength * fScale;
	float3 v3SampleRay = v3Ray * fSampleLength;
	float3 v3SamplePoint = v3Start + v3SampleRay * 0.5f;
	
	// Loop the ray
	float3 color=0; //float3(0,0,0);
	for (int i = 0; i < numSamples; i++)
	{
		float fHeight = length(v3SamplePoint);
		float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius-fHeight));
		
		float fLightAngle = dot(v3LightDir, v3SamplePoint) / fHeight;
		float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;
		
		float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));
		float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * Kr4PI + Km4PI));
		
		// Accumulate color
		v3Attenuate*=(fDepth * fScaledLength);
		color += v3Attenuate; //*(fDepth * fScaledLength);
		
		// Next sample point
		v3SamplePoint += v3SampleRay;
	}
	
  // Finally, scale the Mie and Rayleigh colors
  RayleighColor.rgb=color*(v3InvWavelength*KrESun);	
  RayleighColor.a=0;
//RayleighColor.rgb=0;
	
  MieColor.rgb=color*KmESun*10;     // 10=magic
//MieColor.rgb=0.5;
  MieColor.a=0;

  Direction=v3CameraPos-v3Pos;
}

float3 GetSkyColor(float3 lightDirection,float3 direction,float3 rayleighColor,float3 mieColor,float atmosRayleigh,float atmosMie,float3 lightColor)
// For fragment shaders.
// Returns sky color based on 'lightDirection' and 'direction'.
// Inspiration: Sean O'Neill in combination with ATI
{
  // Conversions
  const float  g=-0.991;
  const float  g2=g*g;

  float cos=dot(lightDirection,direction)/length(direction);
  float cos2=cos*cos;

  // Sean O'Neill phase
  //float rayleighPhase = 0.75f * (1.0f + cos2);
  // ATI Rayleigh phase function (10000=magic)
  //float rayleighPhase=10000*(3.0/(16.0*3.14159265))*atmosRayleigh*(1.0+cos2);
  float rayleighPhase=(3.0/(16.0*3.14159265))*atmosRayleigh*(1.0+cos2);
  float miePhase = 1.5f * ((1.0 - g2) / (2.0 + g2)) *
					 (1.0 + cos2) / pow(1.0 + g2 - 2.0 * g * cos, 1.5f);
  miePhase=0.079577471636878192976419142055225*(atmosMie*0.001)*((1-g)*(1-g))/pow((1+g2-2*g*cos),1.5);
  
  // Amount of night texture
  // y direction offset, to add extra delay before stars.
  //const float nightOffset=-0.10;

  // Go dark when the sun dips below the horizon
  // Extra factor since the texture is bright.
  //float night=0.75*saturate( ( -lightDirection.y - nightOffset ) / ( 1.0 - nightOffset ) );

  float3 finalColor;

  //finalColor=(rayleighPhase*IN.RayleighColor + miePhase*IN.MieColor);
  finalColor=(rayleighPhase*rayleighColor + miePhase*mieColor);
  
  // ATI mix
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighPhase*rayleighColor + miePhase*mieColor);
  finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor)*lightColor;
  
  //return miePhase*mieColor;
  //return mieColor;
  
  return finalColor;
}


/*
// Reasonable only not much color changes
void main(
	  float4 inPosition : POSITION,
	  float3 inNormal   : NORMAL,
	  float2 inTC0      : TEXCOORD0,
	  // Output
      out float4 outPosition      : POSITION,
      //out float4 outColor         : COLOR,
	  out float2 outTC0           : TEXCOORD0,
	  out float3 outNormal        : TEXCOORD1,
	  out float3 outPosW          : TEXCOORD2,
	  //out float  fogExponent      : TEXCOORD3,
	  out float  fogExtinction    : TEXCOORD3,
	  out float3 fogLin           : TEXCOORD4,
  
  // Params
  uniform float4x4 modelViewProj,
  uniform float4x4 modelView,
  uniform float4x4 modelViewIT,
  uniform float4x4 modelMatrix,
  uniform float    fogDensity,
  uniform float3   lightDirection,
  uniform float3   lightColor
)
{
  // Transform vertex
  outPosition = mul(modelViewProj,inPosition);
  
  // Need modelMatrix? Calculate fog.
  //fogExponent=outPosition.z*fogDensity;
  //fogExponent=length(outPosition)*fogDensity;

  // Atmosphere (fog)
  float3 fRayleigh=0.0020;
  float fMie=0.0015;
  // Extinction factor
  float s;
  s=length(outPosition);                 // For terrain
  // Calculate sky distance (skydomes)
  //float hAtmos=10000;
  //s=hAtmos/max(lightDirection.y,0.001);     // Avoid division by 0
  fogExtinction=exp(-s*(fRayleigh+fMie));   // Could use outPosition.z
//fogExtinction=0;  // Full fog
//fogExtinction=1;  // No  fog

  // Calculate sun angle
  float cosSunAngle=dot(normalize(outPosition),normalize(lightDirection));
  
  float g=1;                  // Henyey/Greenstein phase function eccentricity
  // Rayleigh/Mie influence depending on sun angle
  float t;
  float3 fRSun;
  t=0.059683103727658644732314356541419*fRayleigh*(1+cosSunAngle*cosSunAngle);
  // Rayleigh uses 3 wavelengths; red/green/blue
  fRSun.r=t*450/625;
  fRSun.g=t*525/625;
  fRSun.b=t*625/625;
  // Mie scattering is gray
  float3 fMSun;
  t=0.079577471636878192976419142055225*fMie*((1-g)*(1-g))/pow((1+g*g-2*g*cosSunAngle),1.5);
  fMSun=t; //*475/700;
  
  // Sun
  // Single color
  float Esun=20.0;               // Sun radiance
  fogLin=(fRSun+fMSun)/(fRayleigh+fMie)*Esun*(1-fogExtinction);
  // RGB variant
  //float3 Esun3=Esun*lightColor;               // Sun radiance
  //fogLin=(fRSun+fMSun)/(fRayleigh+fMie)*Esun3*(1-fogExtinction);
  
  outPosW = mul(modelMatrix,inPosition);			// Object space to eye space
  
  // Pass texcoords
  outTC0 = inTC0;

  // Transform normal with model transformation
  outNormal = mul(modelMatrix,float4(inNormal,0));		// Don't translate
}
*/

/*
void main(
	  float4 inPosition : POSITION,
	  float3 inNormal   : NORMAL,
	  float2 inTC0      : TEXCOORD0,
	  // Output
      out float4 outPosition      : POSITION,
      //out float4 outColor         : COLOR,
	  out float2 outTC0           : TEXCOORD0,
	  out float3 outNormal        : TEXCOORD1,
	  out float3 outPosW          : TEXCOORD2,
	  //out float  fogExponent      : TEXCOORD3,
	  out float  fogExtinction    : TEXCOORD3,
	  out float  fogLin           : TEXCOORD4,
  
  // Params
  uniform float4x4 modelViewProj,
  uniform float4x4 modelView,
  uniform float4x4 modelViewIT,
  uniform float4x4 modelMatrix,
  uniform float    fogDensity,
  uniform float3   lightDirection
)
{
  // Transform vertex
  outPosition = mul(modelViewProj,inPosition);
  
  // Need modelMatrix? Calculate fog.
  //fogExponent=outPosition.z*fogDensity;
  //fogExponent=length(outPosition)*fogDensity;
  
  // Atmosphere (fog)
  float fRayleigh=0.02;
  float fMie=0.01;
  // Extinction factor
  fogExtinction=exp(-length(outPosition)*(fRayleigh+fMie));   // Could use outPosition.z
fogExtinction=0;  // Full fog

  // Calculate sun angle
  float sa,l;
  l=sqrt(lightDirection.x*lightDirection.x+lightDirection.z*lightDirection.z);
  sa=atan2(l,lightDirection.y);

  // Calculate sun angle
  float sunAngle; //=30.0/57.3;		// Degrees to radians
  //float l=sqrt(outPosition.x*outPosition.x+outPosition.z*outPosition.z);
  //sunAngle=atan2(l,outPosition.y);
  l=sqrt(inPosition.x*inPosition.x+inPosition.z*inPosition.z);
  sunAngle=atan2(l,inPosition.y);
  
  //sunAngle-=sa;

  float cosSunAngle=cos(sunAngle);
  float g=2.2;                  // Henyey/Greenstein phase function eccentricity
  // Rayleigh/Mie influence depending on sun angle
  float fRSun=0.059683103727658644732314356541419*fRayleigh*(1+cosSunAngle*cosSunAngle);
  float fMSun=0.079577471636878192976419142055225*fMie*((1-g)*(1-g))/pow((1+g*g-2*g*cosSunAngle),1.5);
  float Esun=10.0;               // Sun radiance
  fogLin=(fRSun+fMSun)/(fRayleigh+fMie)*Esun*(1-fogExtinction);

  outPosW = mul(modelMatrix,inPosition);			// Object space to eye space
  
  // Pass texcoords
  outTC0 = inTC0;

  // Transform normal with model transformation
  outNormal = mul(modelMatrix,float4(inNormal,0));		// Don't translate
}
*/
