uniform sampler2D tex2;
uniform mat4 modelview;
uniform vec3 lightvec1;
uniform vec3 cam;
flat  in float seed;
flat  in float ratio;
flat  in  vec4 rcol;
flat  in  vec4 camera;          // xyz- camera origin(LY)  , w - 1/(halfsize of bilboard,meters)
flat  in  vec4 cs;
in  vec2 vx;

out vec4 fragColor;

const float rock_size=1.92,rock_scale=10.;
#define PI 3.14159265

vec4 hash4( vec4 n ) { return fract(sin(n)*753.5453123); }

const mat2 rM = mat2(.7071, .7071, -.7071, .7071);
mat2 rot2( float a ){ vec2 v = sin(vec2(1.570796, 0) + a);  return mat2(v, -v.y, v.x); }

vec3 tex3D(in vec3 p, in vec3 n )
{
    n = max(abs(n), 0.001);//n = max((abs(n) - 0.2)*7., 0.001); //  etc.
    n /= (n.x + n.y + n.z );
    p = (texture(tex2, p.yz)*n.x + texture(tex2, p.zx)*n.y + texture(tex2, p.xy)*n.z).xyz;
    return p*p;
}

float drawObject(in vec3 p,vec4 v)
{
  return length(p+(v.xyz-vec3(0.5))*v.w*ratio*.5);
}

vec4 rv[4];

float cellTile(in vec3 p)
{
    vec4 v, d;
    d.x = drawObject(p , rv[0]);
    p.xy *= rM;
    d.y = drawObject(p , rv[1]);
    p.yz *= rM;
    d.z = drawObject(p , rv[2]);
    p.zx *= rM;
    d.w = drawObject(p , rv[3]);
    v.xy = min(d.xz, d.yw);
    return  min(v.x, v.y);
}

//------------------------- Ray-Sphere Intersection --------------------------
bool RSI(in vec3 ro,in vec3 rd,out vec2 res)
{
  float b = dot(ro,rd);
  float c = dot(ro,ro) - 1.0;
  float d = b*b - c;
  if(d <= 0.0) return false;
  d = sqrt(d);
  res.x=(-b - d);
  res.y=(-b + d);
  return true;
}


float map(vec3 p)
{
    p*=.1;
    vec3 q = p + (cos(p*(12.6+rv[0].x*3.14) - sin(p.zxy*(12.5+rv[0].y*3.14))))*.06*ratio;
    float d=cellTile(q)*rock_scale-rock_size;
    return d;
}

float bumpSurf3D( in vec3 p, in vec3 n){

    return (cellTile(p/2.))*.8 + (cellTile(p*1.5))*.2;

}

vec3 doBumpMap(in vec3 p, in vec3 n, float bf)
{
    const vec2 e = vec2(0.001, 0);
    mat3 m = mat3( tex3D(p - e.xyy, n), tex3D(p - e.yxy, n), tex3D(p - e.yyx, n));
    vec3 g = vec3(0.299, 0.587, 0.114)*m; // Converting to greyscale.
    g = (g - dot(tex3D(p , n), vec3(0.299, 0.587, 0.114)) )/e.x; g -= n*dot(n, g);
    return normalize( n + g*bf ); // Bumped normal. "bf" - bump factor.
}

vec3 calcNormal(in vec3 p)
{
    vec2 e = vec2(0.0025, -0.0025);
    return normalize(e.xyy * map(p + e.xyy) +
                     e.yyx * map(p + e.yyx) +
                     e.yxy * map(p + e.yxy) +
                     e.xxx * map(p + e.xxx));
}

void main( )
{
 const float scale=4.;
 const float texscale = .3;
 if (rcol.a<0.01) discard;
 mat3 mx=mat3(modelview);
 vec3 ro =camera.xyz*camera.w;
 vec3 rd =normalize(vec3(vx,0.)-ro);
 ro=ro*mx;
 rd=rd*mx;
 ro=ro*scale;

 mat2 rxz = mat2(-cs.w,cs.z,cs.z,cs.w);
 mat2 rxy = mat2(-cs.y,cs.x,cs.x,cs.y);
 vec3 lv  =-lightvec1;
 ro.xy=ro.xy*rxy;
 rd.xy=rd.xy*rxy;
 ro.xz=ro.xz*rxz;
 rd.xz=rd.xz*rxz;
 lv.xy=lv.xy*rxy;
 lv.xz=lv.xz*rxz;

 vec2 ts;
 if (!RSI(ro/scale,rd,ts)) discard;
 ts*=scale;
 rv[0]=hash4(vec4(seed));
 rv[1]=hash4(rv[0]);
 rv[2]=hash4(rv[1]);
 rv[3]=hash4(rv[2]);
 float t=max(ts.x,0.0);
 vec3  sp;
 for(;t<=ts.y;)
 {
  sp = ro + rd*t;
  float h = map(sp);
  if(abs(h)<0.0025*(ts.x*.125 + 1.))
   break;
  t += h*.8;
 }
 if (t>ts.y) /*{ fragColor=vec4(.5); return; }*/ discard;
 vec3 sn = calcNormal(sp);
 if (rcol.a>0.99) sn = doBumpMap(sp*texscale, sn, 0.1);
 // sn = doBumpMap(sp,sn,.75);
 const float ambience = 0.05;
 float diff = max( dot(sn, lv), 0.0);
 float spec = pow(max( dot( reflect(-lv, sn), -rd ), 0.0 ), 32.);
 vec3 texCol  = tex3D(sp*texscale, sn)*3.5;
 texCol *= bumpSurf3D(sp, sn)*.5 + .5;
 fragColor.rgb = texCol*(diff + spec + ambience);
 // if (rcol.a>0.99) fragColor.rgb *= shadows(sp + sn*.005, lv, .05, 1., 10.);
 fragColor.rgb = clamp(sqrt(fragColor.rgb)*rcol.rgb*2., 0., 1.);
 fragColor.a   = rcol.a;
}
