#version 120
varying vec2 PixelPos;
varying vec3 Ray;

#vertexshader


// in_Position was bound to attribute index 0 and in_Color was bound to attribute index 1
//in  vec2 in_Position;
//in  vec3 in_Color;

//in	vec3 in_Ray;

//noperspective out vec2 PixelPos;
//noperspective out vec3 Ray;

void main(void) {
    // Since we are using flat lines, our input only had two points: x and y.
    // Set the Z coordinate to 0 and W coordinate to 1

   // gl_Position = vec4(in_Position.x, in_Position.y, 0.0, 1.0);
    gl_Position = vec4(gl_Vertex.x, gl_Vertex.y, 0.0, 1.0);
    

    // GLSL allows shorthand use of vectors too, the following is also valid:
    // gl_Position = vec4(in_Position, 0.0, 1.0);
    // We're simply passing the color through unmodified

    PixelPos = (gl_Position.xy+1.0)*0.5;
    Ray = gl_MultiTexCoord0.xyz;
}


#fragmentshader
// It was expressed that some drivers required this next line to function properly
//precision highp float;

//noperspective in vec2 PixelPos;
//noperspective in vec3 Ray;

uniform vec2 dUV;
uniform vec2 dUVRcp;
uniform vec3 Eye;

uniform vec3 S0C;
uniform float S0R;

uniform vec3 S1C;
uniform float S1R;

uniform vec3 S2C;
uniform float S2R;

uniform vec3 C0C;
uniform float C0R;

uniform vec3 C1C;
uniform float C1R;

uniform vec3 MCol0;
uniform vec3 MCol1;
uniform vec3 MCol2;

uniform vec3 L0C;
uniform vec4 L0Col;

uniform vec3 L1C;
uniform vec4 L1Col;

uniform float bColBleed;
uniform float bAO;
uniform float bShad;


//out vec4 gl_FragColor;


float Hit(vec3 _RO, vec3 _RD)
{
	vec3 SO = vec3(3,0,0);
	float SR = 1.0;
	
	vec3 Dst = _RO - SO;
	float B = dot(Dst, _RD);
	float C = dot(Dst, Dst) - SR*SR;
	float D = B*B - C;
	
	return D > 0.0 ? 1.0 : 0.0;
	//return D > 0 ? -B - sqrt(D) : std::numeric_limits<float>::infinity();
}

float lengthsqr(vec3 _V)
{
	return dot(_V, _V);
}

float DSphr(vec3 _P, vec3 _CO, float _CR)
{
	return length(_CO - _P) - _CR;
}

/*float DBox(vec3 _P, vec3 _C0, vec3 _B0, vec3 _B1, vec3 _B2, vec3 _Ext)
{
	vec3 P = _P - _C0;

	vec3 LP;
	LP.x = dot(LP, _B0);
	LP.y = dot(LP, _B1);
	LP.z = dot(LP, _B2);
	
	LPD = abs(LP) - _Ext;
	return min (LPD.x, LPD.y, LPD.z);
} */

/*float DCube(vec3 _P, vec3 _BO, float _S)
{
	vec3 V = _P - _BO;
	vec3 LP = clamp(V, -_S, _S);
	return length(LP + _BO -_P);
}*/

float DCube(vec3 _P, vec3 _BO, float _S)
{
	const float CR = 0.15;
	vec3 V = _P - _BO;
	
	float RDR = (1.0-CR)*_S;
	
	vec3 LP = clamp(V, -RDR, RDR);
	
	return length(_BO + LP -_P)-CR*_S;
}

float DPlane(vec3 _P, vec3 _PO, vec3 _N)
{
	vec3 LP = _P - _PO;
	return abs(dot(LP, _N));
}

struct Mater
{
	vec3 Col;
	float SpecAmount;
	float SpecPow;
};

Mater Materlist[3];

/*
struct HitD
{
	float D;
	int M;
}; 
*/

vec2 MatMin(vec2 _L, vec2 _R)
{
	if(_L.x < _R.x)
		return _L;
	else
		return _R;
}

vec2 DMod0(vec3 _P)
{
	float Dist = 1000000;
//	Dist = min(Dist, DSphr(_P, vec3(3, 2, -4), 2.0));
//	Dist = min(Dist, DSphr(_P, vec3(3, 1, 1), 1.0));
	
	Dist = min(Dist, DSphr(_P, S0C, S0R));
	Dist = min(Dist, DSphr(_P, S1C, S1R));
	return vec2(Dist, 0);
}

vec2 DMod1(vec3 _P)
{
	float Dist = 1000000;
//	Dist = min(Dist, DSphr(_P, vec3(3, 1.5, -8), 1.5));	
//	Dist = min(Dist, DCube(_P, vec3(6,2,0), 2.0));		
//	Dist = min(Dist, DCube(_P, vec3(6,2,6), 2.5));

	Dist = min(Dist, DSphr(_P, S2C, S2R));	
	Dist = min(Dist, DCube(_P, C0C, C0R));		
	Dist = min(Dist, DCube(_P, C1C, C1R));
	
	return vec2(Dist, 1);
}

vec2 DMod2(vec3 _P)
{
	float Dist = 1000000;
	Dist = min(Dist, DPlane(_P, vec3(0,0,0), vec3(0,1,0)));
	return vec2(Dist, 2);
}

vec2 Distance(vec3 _P)
{
	vec2 Hit = vec2(100000,0);
//	float Dist = 1000000.0;
	/*for(int y=-2; y<3; y++)
		for(int x=-3; x<4; x++)
			Dist = min(Dist, DSphr(_P, vec3(6.0, float(y), float(x)), 1.0));
	return Dist; */


	Hit = MatMin(Hit, DMod0(_P));
	Hit = MatMin(Hit, DMod1(_P));
	Hit = MatMin(Hit, DMod2(_P));
	
/*	
	
	
*/
//	return DSphr(_P, vec3(6, 0, 0), 1.0);
	
	return Hit;
}

struct LightStruct
{
	vec3 Pos;
	vec4 Col;
};


vec4 AOCalc(vec3 _P, float _DHit, vec4 _AOSum, float _W)
{	
	vec2 Hit = Distance(_P);
	vec3 HitCol = Materlist[int(Hit.y)].Col;
	float AO = (_DHit-Hit.x);
	_AOSum.a += AO*_W;
	_AOSum.rgb += HitCol*(AO)*_W;
	return _AOSum;
}


void main(void) 
{
	Materlist[0].SpecAmount =  0.5;
	Materlist[0].SpecPow =  128.0;
	Materlist[1].SpecAmount =  0.3;
	Materlist[1].SpecPow =  16.0;
	Materlist[2].SpecAmount =  0.2;
	Materlist[2].SpecPow =  8.0;
	
	Materlist[0].Col =  MCol0;
	Materlist[1].Col =  MCol1;
	Materlist[2].Col =  MCol2;

	
    // Pass through our original color with full opacity.
    //gl_FragColor = vec4(ex_Color,1.0);
    //gl_FragColor.r = 0.2;
    
    gl_FragColor = vec4(0,0,0,(dUV*dUVRcp).x);
    
	vec3 RayN = normalize(Ray);
    //PixelPos*dUV*dUVRcp;
    
    float Hi = Hit(Eye, RayN);
    
    vec3 P = Eye + RayN;
	float D = 0.0;
    float bHit = 0.0;
    vec2 Hit = vec2(1000, 0);
    int i=0;
    int nSteps=0;
    for(i=0;i<64;i++)
	{
		Hit = Distance(P);
		D = Hit.x;
		if(D < 0.01)
		{			
			bHit = 1.0;
			break;	
		}
		P = P + RayN*D;
	}
	nSteps+=i;
	
	int iMat = int(Hit.y);
	
	LightStruct Lights[2];
//	Lights[0].Pos = vec3(-20,20,-10);
//	Lights[0].Col = vec3(0.5,0.5,0.5);
//	Lights[1].Pos = vec3(-10,20,10);
//	Lights[1].Col = vec3(0.5,0.5,0.5);
	
	Lights[0].Pos = L0C;
	Lights[0].Col = L0Col;
	Lights[1].Pos = L1C;
	Lights[1].Col = L1Col;
	
	const float Epsilon = 0.0001;
	float dx = D - Distance(P + vec3(Epsilon, 0.0, 0.0)).x;
	float dy = D - Distance(P + vec3(0.0, Epsilon, 0.0)).x;
	float dz = D - Distance(P + vec3(0.0, 0.0, Epsilon)).x;
	
	nSteps+=3;
	
	vec3 N = normalize(-vec3(dx, dy, dz));
	vec3 EyeVec = normalize(P-Eye);
	vec3 Reflected = normalize(reflect(EyeVec, N));
	
	// shadow
	vec3 Sh = vec3(0.0);
	float ShSpec = 0.0;
	for(int iL=0; iL<2; iL++)
	{
		vec3 LightPos = Lights[iL].Pos;
		vec3 VLight = LightPos - P;
		vec3 NVLight = normalize(VLight);
		
		float l = dot(N, NVLight)+0.1;
			
		const int nShadowTests = 10;
		float Lit = 0.0;
		for(i=1; i<nShadowTests+1; i++)
		{
			//vec3 TestP = P + VLight*(1.0/float(nShadowTests+1))*float(i);
			float Dist  = float(i)*0.5;
			float Fac = Dist*0.7;
			vec3 TestP = P + NVLight*Dist;
			
			float SDist = Distance(TestP).x;
			Lit += clamp(Fac - SDist, 0, Fac)*(1.0/(Fac))*(1.0/nShadowTests)*1.25;
		}
		
		Lit = (1-Lit);
		
		Lit = clamp(Lit, 0, 1);
		
		l = l * mix(1.0f, Lit, bShad);
		
		ShSpec += Materlist[iMat].SpecAmount*pow(clamp(dot(NVLight, Reflected), 0.0, 1.0), Materlist[iMat].SpecPow) * l * Lights[iL].Col.a;
		Sh += Lights[iL].Col.rgb * l;
	}
	
	// ao
	vec4 AO = vec4(0.0);
	{
		const float SD0 = 0.7;
		const float SD1 = 2.0;
		
			
		vec3 xSeed = vec3(1,0,0);
		
		vec3 AODir = normalize(N + vec3(0,0.8, 0));
		
		//float px = dot(xSeed, N);
		//if(abs(px) < 0.5)
		if(abs(AODir.x) > 0.5)
			xSeed = vec3(0,1,0);
			
		vec3 x = normalize(xSeed - dot(xSeed, AODir)*AODir);
		vec3 y = cross(AODir, x);
		const float ConRcp = 0.4;
		
		float DS = 0.7;
		float W = 0.1;
		vec3 POff = P + AODir*DS;
		
		AO = AOCalc(POff + x*DS*ConRcp, DS, AO, W);
		AO = AOCalc(POff + y*DS*ConRcp, DS, AO, W);
		AO = AOCalc(POff - x*DS*ConRcp, DS, AO, W);
		AO = AOCalc(POff - y*DS*ConRcp, DS, AO, W);
					
		DS = 2.0;
		W = 0.05;
		
		POff = P + AODir*DS;
		AO = AOCalc(POff + x*DS*ConRcp, DS, AO, W);
		AO = AOCalc(POff + y*DS*ConRcp, DS, AO, W);
		AO = AOCalc(POff - x*DS*ConRcp, DS, AO, W);
		AO = AOCalc(POff - y*DS*ConRcp, DS, AO, W);
		
	/*	AO = AOCalc(P + N*SD0, SD0, AO, 0.2);
		AO = AOCalc(P + N*SD1, SD1, AO, 0.1);
		AO = AOCalc(P + RayN*SD0, SD0, AO, 0.3);		
		AO = AOCalc(P + RayN*SD1, SD1, AO, 0.2); */
		
		
	//	vec3 TestP0 = P + N*SD0;
	//	float D0 = Distance(TestP0).x;
	//	AO += (SD0-D0)*0.2;
		
	//	vec3 TestP1 = P + N*SD1;
	//	float D1 = Distance(TestP1).x;		
	//	AO += (SD1-D1)*0.10;
		
	//	vec3 TestP2 = P - RayN*SD0;
	//	float D2 = Distance(TestP2).x;
	//	AO += (SD0-D2)*0.3;
		
	//	vec3 TestP3 = P - RayN*SD1;		
	//	float D3 = Distance(TestP3).x;
	//	AO += (SD1-D3)*0.2;
		
		AO = clamp(AO, 0.0, 1.0);
		nSteps += 4;
	}
	
	Sh = Sh*(1.0-AO.a*bAO);
	//Sh = vec3(1.0 - AO.a);
	
    //vec3 Col = vec3(6.0 - (length(P-Eye)))/2.0;
    vec3 mCol = Materlist[int(Hit.y)].Col;
    
	vec3 Col = mCol*Sh*bHit+AO.rgb*(Sh + 0.1)*bColBleed + ShSpec;
	//vec3 Col = vec3(float(nSteps)/140.0);
    
    //Col = vec3(1-AO.a);
    //Col = AO.rgb;
	Col = Col + vec3(ShSpec) - Col*vec3(ShSpec);
    
    gl_FragColor.rgb = clamp(Col, 0.0, 1.0);    
}
