struct DensityProfileLayer 
{
	float 	_width;
	float 	_exp_term;
	float 	_exp_scale;
	float 	_linear_term;
	float 	_constant_term;
};

struct DensityProfile 
{
	DensityProfileLayer _layers[2];
};

struct AtmosphereParameters {
 
	vec3 			_solar_irradiance;
	float 			_sun_angular_radius;
	float 			_bottom_radius;
	float 			_top_radius;
	DensityProfile 	_rayleigh_density;
	vec3 			_rayleigh_scattering;
	DensityProfile 	_mie_density;
	vec3			_mie_scattering;
	vec3 			_mie_extinction;
	float 			_mie_phase_function_g;
	DensityProfile 	_absorption_density;
	vec3 			_absorption_extinction;
	vec3 			_ground_albedo;
	float 			_mu_s_min;
};

float ClampCosine(float mu) 
{
	return clamp(mu, Number(-1.0), Number(1.0));
}

float ClampDistance(float d) 
{
	return max(d, 0.0 * m);
}

float ClampRadius(in AtmosphereParameters atmosphere, float r) 
{
	return clamp(r, atmosphere._bottom_radius, atmosphere._top_radius);
}

float SafeSqrt(float a) 
{
	return sqrt(max(a, 0.0 * m2));
}

// TRANSMITTANCE //////////////////////////////////////
float DistanceToTopAtmosphereBoundary(in AtmosphereParameters atmosphere, float r, float mu) 
{
	//assert(r <= atmosphere.top_radius);
	//assert(mu >= -1.0 && mu <= 1.0);
	float discriminant = r * r * (mu * mu - 1.0) + atmosphere._top_radius * atmosphere._top_radius;
	return ClampDistance(-r * mu + SafeSqrt(discriminant));
}

float DistanceToBottomAtmosphereBoundary(in AtmosphereParameters atmosphere, float r, float mu) 
{
	//assert(r >= atmosphere.bottom_radius);
	//assert(mu >= -1.0 && mu <= 1.0);
	float discriminant = r * r * (mu * mu - 1.0) + atmosphere._bottom_radius * atmosphere._bottom_radius;
	return ClampDistance(-r * mu - SafeSqrt(discriminant));
}

bool RayIntersectsGround( in AtmosphereParameters atmosphere, float r, float mu) 
{
	//assert(r >= atmosphere.bottom_radius);
	//assert(mu >= -1.0 && mu <= 1.0);
	return mu < 0.0 && r * r * (mu * mu - 1.0) + atmosphere._bottom_radius * atmosphere._bottom_radius >= 0.0 * m2; //??
}

float GetLayerDensity( in DensityProfileLayer layer, float altitude) 
{
	float density = layer._exp_term * exp(layer._exp_scale * altitude) + layer._linear_term * altitude + layer._constant_term;
	return clamp(density, 0.0, 1.0);
}

float GetProfileDensity( in DensityProfile profile, float altitude) 
{
	return altitude < profile._layers[0].width ? GetLayerDensity(profile._layers[0], altitude) : GetLayerDensity(profile._layers[1], altitude);
}
