#info NODE Raytracer (by Kali)
#camera 3D

#vertex
#group Camera

// Field-of-view
uniform float FOV; slider[0,0.4,2.0] NotLockable
uniform vec3 Eye; slider[(-50,-50,-50),(0,0,-10),(50,50,50)] NotLockable
uniform vec3 Target; slider[(-50,-50,-50),(0,0,0),(50,50,50)] NotLockable
uniform vec3 Up; slider[(0,0,0),(0,1,0),(0,0,0)] NotLockable
uniform vec2 pixelSize;

varying vec3 dirDx; // out
varying vec3 dirDy; // out
varying vec3 from; // out
varying vec2 coord; // out
varying vec2 viewcoord; // out
varying float zoom; // out
varying vec3 dir; // out
varying vec3 up; // out
// in vec4 gl_Vertex; // in
void main(void)
{
	gl_Position =  gl_Vertex;
	coord = (gl_ProjectionMatrix*gl_Vertex).xy;
	coord.x*= pixelSize.y/pixelSize.x;
	// we will only use gl_ProjectionMatrix to scale and translate, so the following should be OK.
	vec2 ps =vec2(pixelSize.x*gl_ProjectionMatrix[0][0], pixelSize.y*gl_ProjectionMatrix[1][1]);
	zoom = length(ps);
	from = Eye;
	vec3 Dir = normalize(Target-Eye);
	up = Up-dot(Dir,Up)*Dir;
	up = normalize(up);
	vec3 Right = normalize( cross(Dir,up));
	dir = (coord.x*Right + coord.y*up )*FOV+Dir;
	dirDy = ps.y*up*FOV;
	dirDx = ps.x*Right*FOV;
	viewcoord=(gl_Vertex.xy+vec2(1))/2;
	
}
#endvertex

#group Raytracer

// Camera position and target.
in vec3 from,dir;
in vec2 viewcoord;
in float zoom;
uniform vec2 pixelSize;


#buffer RGBA32F
uniform sampler2D backbuffer;
uniform int backbufferCounter;


uniform float MaxDistance; slider[0,5,100]
uniform float FixedStep; slider[0.001,.05,.1]
uniform float ProgressiveStep; slider[0.0001,0.001,.01]
uniform int SearchSteps; slider[0,20,50]

#group Light
uniform vec3 LightDir; slider[(-1,-1,-1),(0,1,0),(1,1,1)]
uniform float SpotLight; slider[0,1,1]
uniform float CamLight; slider[0,0,1]
uniform float BlackLevel; slider[0,0,1]
uniform float Fog; slider[0,0,10]
uniform float FogExp; slider[1,2,10]
uniform vec3 Background; color[0.0,0.0,0.0]


bool formula(vec3 p);

float rand(vec2 co){
	// implementation found at: lumina.sourceforge.net/Tutorials/Noise.html
	return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

vec3 Normal(vec2 pix) {
	vec3 e=vec3(pixelSize,0);
	float w=texture2D(backbuffer,pix).w;
	float dx1=texture2D(backbuffer,pix-e.xz).w-texture2D(backbuffer,pix).w;
	float dx2=texture2D(backbuffer,pix).w-texture2D(backbuffer,pix+e.xz).w;
	float dy1=texture2D(backbuffer,pix-e.zy).w-texture2D(backbuffer,pix).w;
	float dy2=texture2D(backbuffer,pix).w-texture2D(backbuffer,pix+e.zy).w;
	vec3 n1=normalize(vec3(zoom,0,max(dx1,dx2)/w));
	vec3 n2=normalize(vec3(0,zoom,max(dy1,dy2)/w));
	return cross(n1,n2);
	
}

vec3 Light(vec2 pix, vec3 ldir) {
	vec3 n=Normal(pix);
	float diff=max(0.0,dot(-n,ldir));
	return vec3(diff);
}


vec4 trace (vec3 f,vec3 d) {
	vec3 prevc=texture2D(backbuffer,viewcoord).xyz;
	float prevr=texture2D(backbuffer,viewcoord).w;
	if (backbufferCounter==1) prevr=MaxDistance;
	vec3 newc=Background;
	vec3 c;
	float r=mod(float(backbufferCounter)*ProgressiveStep,FixedStep*1.12345);
	bool i=formula(f+r*d);
	while (!i && r<prevr) {
		r+=FixedStep;
		i=formula(f+r*d);
	}
	if (r<prevr) {
		int ss=0;
		float s=FixedStep;
		while (ss<SearchSteps) {
			ss+=1;
			s=s/2;
			if (i) r-=s; else r+=s;
			i=formula(f+r*d);
		}
	} else {r=prevr;}
	if (r<MaxDistance) newc=Light(viewcoord,normalize(LightDir))*SpotLight+Light(viewcoord,vec3(0,0,-1))*CamLight;
	newc=max(vec3(BlackLevel),newc);
	c=backbufferCounter>1?newc:vec3(1-r/MaxDistance);
	
	return vec4(mix(c,Background,1-exp(-pow(Fog,4)*pow(r,FogExp))),r);
}



#ifdef providesInit
void init(); // forward declare
#else
void init() {}
#endif
void main() {
	init();
	vec4 t = trace(from,dir);
	gl_FragColor = t;
}

bool inside(vec3 p);

bool formula(vec3 p) {
	
	//   write formula here and comment the "return inside(p)" line
	//  for testing the raytracer from this .frag
	
	//		return length(p)<1;
	
	return inside(p);
}