//
// 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 saturate(1.0/clouds*(a-1.0)+1.0-(1.0-clouds)*0.7);
  return pow(a,1.0/(clouds+0.001))*pow(clouds,0.5);
}

float scale(float cosx)
// Something with the atmosphere - looks like a Taylor series
{
  float  x=1.0-cosx;
  return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}

float CalculateAtmosphereExtinction(
  float3 position,
  float  atmosRayleigh,
  float  atmosMie,
  float  extinctionFactor
)
// 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=0.5;
  //e=extinctionFactor*exp(-s*(atmosRayleigh+atmosMie));   // Could use outPosition.z
  e=exp(-s*extinctionFactor*(atmosRayleigh+atmosMie));
  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)
{
  // Constants
  const float PI=3.141592653589;
  const float fInnerRadius=977000;
  const float fOuterRadius=fInnerRadius+30000; //fInnerRadius*1.025;
  //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*10000;
  float KmESun=atmosMie*sunIntensity*10000*10;
  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 - used during ray sampling
  float3 v3InvWavelength=float3(5.8,13.5,33.1);
  // This one used during postprocessing to scale colors
  float3 v3InvWavelengthPost=float3(5.8,13.5 * 0.8,33.1);

  // Virtual sky dome distance
  const float domeRadius=20000.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
  Direction=v3CameraPos-v3Pos;
  v3Ray=-Direction;
  //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);   // More samples would mean we'd use color+=
		
		// Next sample point
//		v3SamplePoint += v3SampleRay;
	}

  // Finally, scale the Mie and Rayleigh colors
  RayleighColor=float4(color*(v3InvWavelengthPost*KrESun),0);
  // Take out a bit of green
  //RayleighColor.rgb=color*(v3InvWavelength*KrESun);	
  //RayleighColor.g*=0.8;
  //RayleighColor.a=0;
  
  MieColor=float4(color*KmESun,0);
  //MieColor.a=0;
}

float3 GetSkyColor(float3 lightDirection,float3 direction,float3 rayleighColor,float3 mieColor,
  float atmosRayleigh,float atmosMie,float3 lightColor,float3 lightAmbient)
// For fragment shaders and non-atmosphere materials.
// Returns sky color based on 'lightDirection' and 'direction'.
// Inspiration: Sean O'Neill in combination with ATI
{
  //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.5-cos*0.5);
  float miePhase=0.000079577471636878192976419142055225*atmosMie;
    // *((1-g)*(1-g))/pow((1+g2-2*g*cos),1.5);   // With disk
  
  // 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;

  // ATI mix - Mie is responsible for the sun circle, so remove it for normal surface
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighPhase*rayleighColor + miePhase*mieColor);
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor);
  //finalColor=(rayleighPhase/atmosRayleigh)*rayleighColor+(miePhase/atmosMie)*mieColor;
  finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor);
  
  return finalColor;
}

float3 GetSkyColorAtm(float3 lightDirection,float3 direction,float3 rayleighColor,float3 mieColor,
  float atmosRayleigh,float atmosMie,float3 lightColor,float3 lightAmbient,float sunny)
// For fragment shaders and shaders that represent the sky.
// Returns sky color based on 'lightDirection' and 'direction'.
// Inspiration: Sean O'Neill in combination with ATI
// This one adds the sun 'disc' in sky materials.
{
  // Conversions
  const float g=-0.991; //*lightColor.r;
  const float g2=g*g;

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

//rayleighColor=lightAmbient;
//mieColor=lightColor;

  // 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.5-cos*0.5);
  //float miePhase = 0.001*1.5f * ((1.0 - g2) / (2.0 + g2)) *
  //                     (1.0 + cos2) / pow(1.0 + g2 - 2.0 * g * cos, 1.5f);
  float miePhase=sunny*0.000079577471636878192976419142055225*atmosMie*((1-g)*(1-g))/pow((1+g2-2*g*cos),1.5);

  //miePhase=0;
  // 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;

  // ATI mix - Mie is responsible for the sun circle, so remove it for normal surface
  //finalColor=(rayleighPhase/atmosRayleigh)*rayleighColor+(miePhase/atmosMie)*mieColor;
  finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor);
  //finalColor=rayleighPhase*4000.0f;

//return float3(1,1,1);
  
  return finalColor;
}

float3 GetSkyColorAtm0(float3 lightDirection,float3 direction,float3 rayleighColor,float3 mieColor,
  float atmosRayleigh,float atmosMie,float3 lightColor,float3 lightAmbient)
// For fragment shaders and shaders that represent the sky.
// Returns sky color based on 'lightDirection' and 'direction'.
// Inspiration: Sean O'Neill in combination with ATI
// This one adds the sun 'disc' in sky materials.
// Older version
{
  // 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);
  //finalColor=rayleighPhase*rayleighColor;
  
  
  // ATI mix
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighPhase*rayleighColor + miePhase*mieColor);
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor)*max(lightColor,lightAmbient);
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor)*(lightColor+lightAmbient);
  // Future takes into account diffuse+ambient sun colors?
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor)*lightColor+lightAmbient;
  //finalColor=(rayleighPhase)/(atmosRayleigh)*(rayleighColor)*lightColor;
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor)*(lightColor+lightAmbient);
  //finalColor=(rayleighPhase+miePhase)/(atmosRayleigh+atmosMie)*(rayleighColor+mieColor)*lightColor; //float3(1,1,1);
  
  //finalColor=(rayleighPhase/atmosRayleigh*rayleighColor)*lightAmbient +
  //           (miePhase/atmosMie*mieColor)*lightColor;
  
//finalColor=rayleighPhase*1000.0f;
  //return miePhase*mieColor;
  //return mieColor;
  
  return finalColor;
}
