#version 120
const float tau = 6.28318530716;
const bool parallax = 				#{{level:extreme}};
const bool advancedProcedurals = 	#{{level:high}};

// it needs special tweaks to not bleed out in overexposure
bool lightShaftsOn = #{{bGodRays}};
bool bloomOn = #{{bBloom}};

// parallax scale, bias and steps
const vec2 scaleBias = vec2(0.005, 0.0025);

uniform sampler2D texture[2];
uniform float cycles[5];
uniform float temperature;
uniform float nodeScale;

varying float distortion;
varying vec2 uvA, uvB;
varying vec3 vertCol, normal, origo;
varying vec3 v;

const float maxVariance = 0.03; //should be kept low
const float deviationCycles = 3.25; //How much variation is based on base color

const float two_pi = 6.28318531;

const float noise1Base = 0.84, noise1Range = 0.25;
const float noise2Base = 0.93, noise2Range = 0.34;

const float diffPower = 2.3;
const float tempInfluence = 0.25;
const float baseTemp = 10000.0;

const float noiseOffsetNoise = 0.1;

float mixRange(float x, float low, float hi) {
	return clamp((x - low) / (hi - low), 0.0, 1.0);
}

vec3 blackBody(float temp) {
	vec3 c;
	c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
	c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
	if(temp < 6600.0)
		c.g = mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0));
	else
		c.g = mix(0.976, 0.75, mixRange(temp, 6600.0, 29800.0));
	if(temp > 13000.0)
		c += vec3(mixRange(temp, 13000.0, 29800.0));
	return c;
}

vec3 dp1Calc(vec3 p)
{
	return dFdx(p);
}

vec3 dp2Calc(vec3 p)
{
	return dFdy(p);
}

vec4 duv1Calc(vec4 uv)
{
	return dFdx(uv);
}

vec4 duv2Calc(vec4 uv)
{
	return dFdy(uv);
}
float pow4(float x){
	x*= x;
	return x*x;
}
/*
float mip_map_level(vec2 uv)
{

    vec2  dx_vtc        = dFdx(uv);
    vec2  dy_vtc        = dFdy(uv);
    float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc));
 
 
    //return max(0.0, 0.5 * log2(delta_max_sqr) - 1.0); // == log2(sqrt(delta_max_sqr));
    return 0.5 * log2(delta_max_sqr); // == log2(sqrt(delta_max_sqr));
}
*/
float starSampMip(vec2 uv, float mip){
		vec2 noiseCoord = uv * 3.0 + (texture2D(texture[1], uv * 16.0, mip).xz - vec2(0.5)) * noiseOffsetNoise;
		noiseCoord += vec2( cycles[0], cycles[1]);
		
		vec2 noise2Coord = noiseCoord + vec2( -cycles[3], cycles[4] );
		vec2 noise3Coord = noiseCoord + vec2( -cycles[2], cycles[3] * 4.0 );
		vec2 noise4Coord = noiseCoord * 2.0 + vec2( cycles[4], cycles[0] * -2.0 );
		
		noiseCoord += vec2( cycles[1], cycles[2] );	
		float heat =	noise1Base + noise1Range *
			texture2D(texture[1], uv * 4.0, mip).r;
		heat *=	noise2Base + noise2Range *
			texture2D(texture[0], noise2Coord, mip).g;
		heat *=	noise2Base + noise2Range *
			texture2D(texture[0], noise3Coord, mip).b;
		heat *=	noise1Base + noise1Range *
			texture2D(texture[1], noise4Coord, mip).g;
		
		return heat;
}

float starSamp(vec2 uv){
		vec2 noiseCoord =uv * 3.0 + (texture2D(texture[1], uv * 16.0).xz - vec2(0.5)) * noiseOffsetNoise;
		noiseCoord += vec2( cycles[0], cycles[1]);
		
		vec2 noise2Coord = noiseCoord + vec2( -cycles[3], cycles[4] );
		vec2 noise3Coord = noiseCoord + vec2( -cycles[2], cycles[3] * 4.0 );
		vec2 noise4Coord = noiseCoord * 2.0 + vec2( cycles[4], cycles[0] * -2.0 );
		
		noiseCoord += vec2( cycles[1], cycles[2] );
		
		float heat =	noise1Base + noise1Range *
			texture2D(texture[1], uv * 4.0).r;
		heat *=	noise2Base + noise2Range *
			texture2D(texture[0], noise2Coord).g;
		heat *=	noise2Base + noise2Range *
			texture2D(texture[0], noise3Coord).b;
		heat *=	noise1Base + noise1Range *
			texture2D(texture[1], noise4Coord).g;
		
		return heat;
}
void main() {
	vec4 uvP = vec4(uvA, uvB);
	// do polar blend mask from world space normals
	float blendMask = clamp(abs(normal.y)* 4.0 - 2.0, 0.0, 1.0);
	
	float NdotV = max(0.0, dot(normal, v));

	float heat = 0.0;
	
	if (parallax){
		// star surface to noisy to work for parallax, so sample mip-blurred for approximate softened plumes
		heat = mix(starSampMip(uvB, 5.0), starSampMip(uvA, 5.0), blendMask);
		// make the parallax a little more interesting
		heat = pow4(abs(fract(heat) * 2.0 - 1.0));
		// and smoothstep
		heat *= heat * (3.0 - (2.0 * heat));
		
		vec3 dp1 = dp1Calc(-v);
		vec3 dp2 = dp2Calc(-v);
		
		// derive for both uv's
		vec4 duv1 = duv1Calc(uvP);
		vec4 duv2 = duv2Calc(uvP);
	
		vec3 dp2perp = cross(dp2, normal);
		vec3 dp1perp = cross(normal, dp1);
		
		// do parallax
		float fDet = dot(dp1, dp2perp);
		vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
		vec4 vProjVTex = (duv1 * vProjVScr.x + duv2 * vProjVScr.y);
		float vProjVTexZ = NdotV * ((1.0 - heat - 0.5) * scaleBias.r - scaleBias.g);
		
		uvP += (vProjVTex * vProjVTexZ);
	
	heat = mix(starSamp(uvP.zw), starSamp(uvP.xy), blendMask);
	}
	else{
		heat = mix(starSamp(uvB), starSamp(uvA), blendMask);
	}
	heat -=1.1;
	
	
	if(temperature < 3500.0) {
		heat += 0.05;
		if(heat < 0.0)
			heat *= 2.0;
		else
			heat *= 22.0 - (temperature / 350.0);
	}
	else if(temperature < 6500.0) {
		heat *= 4.0;
	}
	else if(temperature > 20000.0) {
		heat *= 5.0;
	}
	else {
		heat += 0.05;
		if(heat < 0.0)
			heat *= 2.0;
		else
			heat *= 20.0;
	}
	
	heat = mix(heat, 0.7, pow(max(distortion,0.0), 2.8));
	heat = mix(0.6, heat, NdotV) + 1.0;
	
	gl_FragColor.rgb = blackBody(temperature * heat) * clamp(heat, min(temperature / 2500.0, 1.0), 1.0);
	gl_FragColor.a = 1.0;
}
