uniform sampler1D spherePositions;
uniform sampler1D sphereColors;
uniform sampler2D sphereNeighbors;
uniform int sphereCount;
uniform int neighborCount;

vec4 hitSpheres[2000];
int hitSphereCount;

float raySphere(vec3 ray, vec3 dir, vec3 center, float radius, float closest)
{
  vec3 rc = ray-center;
  float c = dot(rc, rc) - (radius*radius);
  float b = dot(dir, rc);
  float d = b*b - c;
  float t = -b - sqrt(abs(d));
  if (d < 0.0 || t < 0.0 /*|| t > closest*/) {
    return -closest;
  } else {
    return t;
  }
}

float raySphere2(vec3 eye, vec3 dir, vec3 center, float radius)
{
  center = -center;
  float a = dot(dir, dir);
  float b = 2.0 * dot(eye - center, dir);
  float c = dot(eye - center, eye - center) - (radius * radius);
  
  float d = b*b - 4.0*a*c;
  
  if (d >= 0.0)
  {
    float t1 = (-b + sqrt(d)) / (2.0 * a);
    float t2 = (-b - sqrt(d)) / (2.0 * a);
    
    /*if (t1 < 0.0 && t2 < 0.0)
    {
      return -1;
    }
    else
    {
      return 1;
    }*/
    if (t1 > 0.0 && t2 > 0.0)
    {
      return min(t1, t2);
    }
    else
    {
      return -1;
    }
  }
  else
  {
	   return -1;
	}

}


float cube(vec3 p,float s,float t){
    //float angle=p.y*10*t;
    //p=vec3(p.x*cos(angle)+p.z*sin(angle),p.y,p.z*cos(angle)-p.x*sin(angle));
	return max(max(abs(p.x)-s,abs(p.y)-s),abs(p.z)-s);
}
float sphere(vec3 p,float r){
	return sqrt(p.x*p.x+p.y*p.y+p.z*p.z)-r;
}

float metaball(vec3 p,float r){
	return sphere(p,r) + r/sqrt(p.x*p.x+p.y*p.y+p.z*p.z);
}

/*
float distance(vec3 p,float t){
	//float c=cube(vec3(mod(p.x,1.),mod(p.y,1.),mod(p.z,1.)),0.3,t);
    
	//float angle=(p.x+p.y+p.z)*t;
    //p=vec3(p.x*cos(angle)+p.z*sin(angle),p.y,p.z*cos(angle)-p.x*sin(angle));

	float c=cube(vec3(p.x,p.y,p.z),0.3,0.0);
	float s=sphere(p,0.5);
	float s1=sphere(p-vec3(0, 0.25,0),0.2);
	float s2=sphere(p-vec3(0,-0.25,0),0.2);
	float s3=sphere(p-vec3(0,0, 0.25),0.2);
	float s4=sphere(p-vec3(0,0,-0.25),0.2);
	float s5=sphere(p-vec3( 0.25,0,0),0.2);
	float s6=sphere(p-vec3(-0.25,0,0),0.2);
	

	//return c;
	//return mix(s,c,t/2)

	return min(s1,min(s2,min(s3,min(s4,min(s5,min(s6,mix(s,c,t/2)))))))
	
	//return max(-min(s1,min(s2,min(s3,min(s4,min(s5,s6))))),c)
	+t*0.01*(sin(p.x*50)+cos(p.y*50)+sin(p.z*50))
	+t*0.001*(sin(p.x*500)+cos(p.y*500)+sin(p.z*500))
	;
}
*/

float dist1(vec3 p)
{
	float c=cube(vec3(p.x,p.y,p.z),0.3,0.0);
	float s=sphere(p,0.5);
	float s1=sphere(p-vec3(0, 0.25,0),0.2);
	float s2=sphere(p-vec3(0,-0.25,0),0.2);
	float s3=sphere(p-vec3(0,0, 0.25),0.2);
	float s4=sphere(p-vec3(0,0,-0.25),0.2);
	float s5=sphere(p-vec3( 0.25,0,0),0.2);
	float s6=sphere(p-vec3(-0.25,0,0),0.2);

	return mix(  s,  min(s1,min(s2,min(s3,min(s4,min(s5,min(s6,c)))))),  0.5  );	
}

float dist2(vec3 p)
{
	float angle=p.y*2;
    p=vec3(p.x*cos(angle)+p.z*sin(angle),p.y,p.z*cos(angle)-p.x*sin(angle));

	float c=cube(vec3(p.x,p.y,p.z),0.3,0.0);
	float s=sphere(p,0.5);
	float s1=sphere(p-vec3(0, 0.28,0),0.2);
	float s2=sphere(p-vec3(0,-0.28,0),0.2);
	float s3=sphere(p-vec3(0,0, 0.28),0.2);
	float s4=sphere(p-vec3(0,0,-0.28),0.2);
	float s5=sphere(p-vec3( 0.28,0,0),0.2);
	float s6=sphere(p-vec3(-0.28,0,0),0.2);

	return mix( -s,   max(-min(s1,min(s2,min(s3,min(s4,min(s5,s6))))),c),  0.85 );
}

float dist3(vec3 p)
{
	float s=sphere(p,0.3);

	return s
	-0.1*(abs((sin(p.x*10)+cos(p.y*10)+sin(p.z*10))))
	;
}

float dist4(vec3 p)
{
	float r=0.4;
	float r2=0.1;
	float c=0.05;
	return max(abs(length(p.xz)-r)-r2,abs(p.y)-c);
}


float distance(vec3 p,vec4 v)
{
	vec3 p1,p2,p3;
	
	p1=vec3(v.z,v.x,0);
	p2=vec3(0.2*v.y,0,0.7*v.x);
	p3=vec3(v.x,v.y*v.z,0);

	float b1=sphere(p-p1*0.67,0.4);
	float b2=sphere(p-p2*0.67,0.4);
	float b3=dist1(p-p3*0.67);
	
	//float b1=sphere(p-vec3(0.27,0,0),0.3);
	//float b2=sphere(p+vec3(0.27,0,0),0.3);

	float col=mix(b1,b2,smoothstep(0.,1.,abs(b1-b2)));
	col=mix(col,b3,smoothstep(0.,1.,abs(col-b3)));

	//float col=min(b1,min(b2,b3));
	//if (col<.1) col=sqrt(col*10.);
	
	//col=mix(col,(1-b2)*(1-col),b2);

	//col=min(col,mix(col,(1-b3)*(1-col),b3));
	
	//return mix(b1,b2,col);
	return col;
}

float distance2(vec3 p,vec4 v)
{
    /*float q;
	return mb(p,16.0,q);*/
	
	//p=vec3(fract(p.x+0.5),fract(p.y+0.5),fract(p.z+0.5))-vec3(0.5);
	
	/*return sphere(p-vec3(0.5,0.5,0.5),0.2)
	+clamp(v.z,0.,1.)*0.02*(sin(p.x*50)+cos(p.y*50)+sin(p.z*50))
	+clamp(v.z,0.,1.)*0.003*(sin(p.x*380)+cos(p.y*380)+sin(p.z*380))
	;*/

	/*p.y+=0.01*sin(p.x*20);
	p.y+=0.01*cos(p.z*20);*/
	
	/*return mix(
		max(max(abs(p.x)-0.15,abs(p.y)-0.01),abs(p.z)-0.15),
		sphere(p,0.15),
		clamp(v.z,-0.5,1.));*/

	//return dist2(p);
	
	
	return mix(
	mix(mix(-dist1(p),dist1(p),v[3]),dist2(p),v.x),
	mix(mix(-dist3(p),dist3(p),v[3]),dist4(p),v.x),
	v.y)
	+clamp(v.z,0.,1.)*0.02*(sin(p.x*50)+cos(p.y*50)+sin(p.z*50))
	+clamp(v.z,0.,1.)*0.003*(sin(p.x*380)+cos(p.y*380)+sin(p.z*380))
	;

}

/*float rand()
{
    return fract(sin(dot(gl_FragCoord.xy ,vec2(12.9898,78.233))) * 43758.5453);
} */

/*float rand(int i)
{
    return fract(sin(dot(vec2(float(i) * 543543.0, float(-i) * 342.0) ,vec2(12.9898,78.233))) * 43758.5453);
}*/

uint wang_hash(uint seed)
{
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}
                 
float rand(uint value)
{         
	return float(wang_hash(value)) * (1.0 / 4294967296.0);
}

float blob7(float d1, float d2, float d3, float d4, float d5, float d6, float d7)
{
    float k = 2.0;
	return -log(exp(-k*d1)+exp(-k*d2)+exp(-k*d3)+exp(-k*d4)+exp(-k*d5)+exp(-k*d6)+exp(-k*d7))/k;
}


float smin( float a, float b, float k )
{
    float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
    return mix( b, a, h ) - k*h*(1.0-h);
}

float smin2( float a, float b, float k )
{
    a = pow( a, k ); b = pow( b, k );
    return pow( (a*b)/(a+b), 1.0/k );
}

float dst(vec3 p,vec4 v)
{
	//return distance2(vec3(mod(p.x,1.),mod(p.y,1.),mod(p.z,1.)), v);
  //return sphere(p + hitSpheres[0].rgb, 0.2);//dist2(p);
  
  float d = 9999.0;
  //float d = 0.0;
  int i;
  
  float k = 12.0;
  
  
  //d = sphere(hitSpheres[0].rgb, hitSpheres[0].a);
  for (i = 0; i < hitSphereCount; i++)
  {
    vec3 p_ = hitSpheres[i].rgb;
    float r_ = hitSpheres[i].a;
        
    //d = smin(d, sphere(p + p_, r_), 0.1);
    d = smin(d, sphere(p + p_, r_), 0.1);
    
    //float t = sphere(p + p_, r_);
    //float t = cube(p + p_,0.003,0.0);
    
    //d += exp(-k*t);
  }     
  
  /*for (i = 0; i < sphereCount; i++)
  {
    //vec3 p_ = vec3(-1.0 + 2.0 * rand(40 * i + 0), -1.0 + 2.0 * rand(40 * i + 10), -1.0 + 2.0 * rand(40 * i + 20)) * 5.3;
    //float r_ = 1.1;//(0.02 + rand(4 * i + 3) * 0.01) * 0.01;
  
    vec4 texel = texelFetch(spherePositions, i, 0);    
    //p_ = mix(p_, texel.rgb / 1000.0, 0.9);
    vec3 p_ = texel.rgb / 200.0;
    float r_ = texel.a / 200.0; 
        
    d = min(d, sphere(p + p_, r_));
    
    //float t = sphere(p + p_, r_);
    //float t = cube(p + p_,0.003,0.0);
    
    //d += exp(-k*t);
  }*/              

  return d;
  //return -log(d) / k;
  
  //return distance(p, v);
}

         /*
flaot plane(vec3 eye, vec3 dir, vec3 normal)
{
  if (dot(dir, normal) >= 0)
  {
  }
}      */

varying vec4 v;varying vec3 eye,dir,light;varying float time;
void main(){
	int i;
	vec3 col,p,n,halfv;
	float mn,e,j,ao,dist=0.,ndotl,t,q;
	
	p=eye;

	//dir.x+=0.2*sin(dir.y*6.4);
	//dir.y+=0.2*sin(dir.x*4.8);

	col=vec3(0);
    
   hitSphereCount = 0;

  float minx = 999.5;
  float closestHit = 9999.0;
  int closestHitIndex = 0;
  
  vec3 useColor = vec3(1,0,0);
  
  //sphereCount = 6000;
  
  int maxSphereCnt = 2;
  
  bool found = false;
  
  for (int i = 0; i < sphereCount; i+=1)
  {
    vec4 texel = texelFetch(spherePositions, i, 0);
    vec3 p_ = texel.rgb / 200.0;
    float r_ = texel.a / 200.0;
    
    float hit = raySphere2(eye, dir, p_, r_ * 1.0);
    if (hit > 0.0)
    {
      found = true;     
	     //hitSpheres[hitSphereCount] = vec4(p_, r_);
       //hitSphereCount++;
       
       if (hit < closestHit)
       {
        closestHit = hit;
        //useColor = texelFetch(sphereColors, i, 0).rgb;
        
        useColor = vec3(float(i) / float(sphereCount), 0.0, 0.0);
        
        hitSpheres[hitSphereCount] = vec4(p_, r_);
        closestHitIndex = i;
        
        //hitSphereCount++;
        /*if (hitSphereCount >= maxSphereCnt)
        {
          hitSphereCount = 0;
        }*/
       } 
      
      //if (length(p_ - eye) < minx) {
      //minx = length(p_ - eye);
      //hitSpheres[0] = vec4(p_, r_); }
      
      //if ( hitSphereCount >= 3) break;
    }
  }
  
  /*gl_FragColor = vec4(vec3(closestHit / 50.0), 1.0);
  return;*/

  if (!found)  
  {
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    return;
  }
  
  /*for (int i = 0; i < sphereCount; i+=1)
  {
    vec4 texel = texelFetch(spherePositions, i, 0);
    vec3 p_ = texel.rgb / 200.0;
    float r_ = texel.a / 200.0;
    
    float l = length(dir * 7.0 - p_);
    
    if (l < closestHit)
    {
      closestHit = l;
      hitSpheres[0] = vec4(p_,r_);
      
      useColor = texelFetch(sphereColors, i, 0).rgb;
    }    
  }
  
  //gl_FragColor = vec4(vec3(closestHit), 1.0);
  gl_FragColor = vec4(useColor, 1.0);
  //return;
   */
   
  neighborCount = 3;
  

  /*for (int i = 0; i < neighborCount; i++)
  {
    vec4 texel = texelFetch(sphereNeighbors, ivec2(closestHitIndex, i), 0);
    vec3 p_ = texel.rgb / 200.0;
    float r_ = texel.a / 200.0;
    hitSpheres[1 + i] = vec4(p_, r_);
  } */
  
  //hitSphereCount = neighborCount + 1;
  
  
  hitSphereCount = 1;
  
  
  //gl_FragColor = vec4(useColor, 1.0);
  //return;     
  
  //col = vec3(1.0, (float( hitSphereCount) / float(sphereCount)) * 200.0, 0.0);
  
  //hitSphereCount = 100;
  //gl_FragColor = vec4(1.0, (float( hitSphereCount) / 10.0) * 1.0, 0.0, 1.0);
  //gl_FragColor = vec4(1.0, float(hitSphereCount) / 2200.0, 0.0, 1.0);
  //gl_FragColor = vec4(0.0, minx / 10.0, 0.0, 1.0);
  //gl_FragColor = vec4(dir, 1.0);
  //return;
  
  //gl_FragColor = vec4(0.0, hitSpheres[0].z, 0.0, 1.0);
  //return;
  
  //gl_FragColor = vec4(1,1,1,1);
  //return;
    
	i=0;
	e=0.005;
	//t=sin(time/4)*2;
  t = 0.0;
	mn=0.;
	while(i<120 && dist<120.){
		dist=dst(p,v);
		
    if(dist<mn)mn=dist;
    
		if(dist<0.005){
			n.x=dst(p+vec3(e,0.0,0.0),v)-dst(p-vec3(e,0.0,0.0),v);
			n.y=dst(p+vec3(0.0,e,0.0),v)-dst(p-vec3(0.0,e,0.0),v);
			n.z=dst(p+vec3(0.0,0.0,e),v)-dst(p-vec3(0.0,0.0,e),v);
			n=normalize(n);

					ao=0;
					for(j=1;j<6;j++){
						ao+=(1/pow(2.,j)) * (j*0.06-dst(p+dir*j*0.06,v));
					}
			
			halfv=normalize(eye+light);
			ndotl=max(dot(n,halfv),0.0);
			//float d=dst(p-1.7*light,v);
			//ndotl=max(d,0.0);
			
			//col=vec4(vec3(q/16.,1.-q/16.,0)*max(ndotl,0.15),1.0);			
			//col=vec4(vec3(ndotl,ndotl*0.96,0.86)*max(ndotl,0.25),1.0);
			//col=vec4(vec3(0.7)*max(ndotl,0.25),1.0);
			

			/*if(length(p)<2.){*/
				
        
        /*col=mix(
				2.0*vec4(vec3(0.9+ndotl*0.1,0.8+ndotl*0.1,0.7)*max(ndotl,0.25),1.0),
				vec4(vec3(ndotl,ndotl*0.96,0.86)*max(ndotl,0.25),1.0),
				v[3]);*/
        
        
			/*}else{
				col=vec4(vec3(0.9,0.3,0.27)*max(ndotl,0.25),1.0);
			}*/


			//col=mix(col,vec4(1.0,0.97,0.94,1.0),pow(ndotl,10.0));
			
      col = vec4(vec3(useColor.r+ndotl*0.2,useColor.g+ndotl*0.2,useColor.b+ndotl*0.2)*max(ndotl,0.25),1.0);
      
      
      
			col=mix(vec4(0.0,0.0,0.1,1),col,clamp(300*ao*ao*ao,0.3,1.));
      
      
      			
			//col=mix(vec4(0,0,0,1),col,clamp(10*ao,0.,1.));

			//col*=1-((float)i/100.);
			//col*=vec3(length(p))*2.;

			col=mix(col,vec4(1.0),pow(ndotl,50.0));
      
      //col=((float)i/100.); 
			break;
		}
		p+=dir*dist;
		i++;
	}

	//gl_FragColor=vec4(col+mn*vec3(0.7,0.8,1.0),1);

  gl_FragColor=vec4(col,1);
}