
// common VS params
float4x4 globtm     : register(c0);
float3   view_x     : register(c4);
float3   view_y     : register(c5);
float3   view_z     : register(c6);
float4   view_pos   : register(c7);
float4   options    : register(c8);
float4   depth_proj : register(c9);

#if VSH
  float4 sun_dir     : register(c30);
  float3 skyV0       : register(c31);
  float3 skyVS       : register(c32);
  float3 skyVG       : register(c33);
  float3 skyS0       : register(c34);
  float3 skyS1       : register(c35);
  float3 skyS2       : register(c36);
  float3 skyCS       : register(c37);
  float3 skyCG       : register(c38);
  float3 sunD        : register(c39);
  float3 sunL        : register(c40);
  float3 skyL0       : register(c41);
  float3 skyL1       : register(c42);
#endif


// common PS sky params (for fog)
#if PSH
  float4 sun_dir     : register(c0);
  float3 skyV0       : register(c1);
  float3 skyVS       : register(c2);
  float3 skyVG       : register(c3);
  float3 skyS0       : register(c4);
  float3 skyS1       : register(c5);
  float3 skyS2       : register(c6);
  float3 skyCS       : register(c7);
  float3 skyCG       : register(c8);
  float3 sunD        : register(c9);
  float3 sunL        : register(c10);
  float3 skyL0       : register(c11);
  float3 skyL1       : register(c12);
#endif


#define HMAP_SIZE 256


static const float horizon_cos=-0.14;
static const float planet_k=0.9;


// sky lighting
float3 calc_sky_light(float3 dir)
{
  float vs=dot(dir, sun_dir.xyz);
  float mu=dir.y;

  float3 c;
  c = lerp(skyL0, skyL1, mu) * (skyS0+(vs>0?skyS1:skyS2)*(vs*vs)) ;
  c = lerp(0, c, mu*0.5+0.5);
  return c;
}


float3 calc_simple_light(float3 norm, float ao=1)
{
  float3 light=calc_sky_light(norm);
  light+=sunL.rgb*saturate(dot(norm, sun_dir.xyz));
  return light*ao;
}


// sky color
float3 calc_sky_color(float mu, float vs, float vsd)
{
  float3 c = (skyV0+skyVS*exp(-skyVG/(abs(mu)+0.001))) * (skyS0+(vs>0?skyS1:skyS2)*(vs*vs)) ;
  return c;
}


float3 calc_sky_color(float3 dir)
{
  //dir.y=abs(dir.y);

  float vs=dot(dir, sun_dir.xyz);
  float mu=(dir.y-horizon_cos)/(1-horizon_cos);

  float vsd=min(vs+(1-sun_dir.w), 1);

  return calc_sky_color(mu, vs, vsd);
}


float3 calc_sky_sun_color(float mu, float vs, float vsd, float hf)
{
  float svs=sqrt(1-vsd*vsd);
  // (V0+VS*exp(-VG/(abs(mu)+0.001))) * (S0+(vs>0?S1:S2)*(vs*vs)+CS*exp(-CG*acos(vsd))*hf)

  float3 c=
    (skyV0+skyVS*exp(-skyVG/(abs(mu)+0.001)))
//    1
//    * (skyCS*exp(-skyCG*acos(vsd))*hf+skyS0+(vs>0?skyS1:skyS2)*(vs*vs))
    * (skyCS*exp(-skyCG*(vsd>=0?svs:2-svs))*hf+skyS0+(vs>0?skyS1:skyS2)*(vs*vs))
  ;

  return c;
}


float3 calc_sky_sun_color(float3 vdir)
{
  float mu=(vdir.y-horizon_cos)/(1-horizon_cos);

  float vs=dot(vdir, sun_dir.xyz);

  float vsun=vs;
  float vsd=min(vsun+(1-sun_dir.w), 1);

  float hf=smoothstep(-0.005, 0, mu);

  return calc_sky_sun_color(mu, vs, vsd, hf);
}


// fog
static const float fog_range=256/2-1;

float4 vs_calc_fog_color(float3 pos)
{
  float3 eyeVec=pos-view_pos.xyz;
  float3 v=eyeVec;
  v.y*=0.5;

  float t=saturate(dot(v, v)/(fog_range*fog_range));
  t=(2-t)*t;
  t=t*t;
  t*=options.x;

  return float4(calc_sky_color(normalize(eyeVec))*planet_k, t);
}


// gamma correction
float4 pack_color(float3 c, float a) { return float4(sqrt(c), a); }
void unpack_color(inout float3 c) { c*=c; }


// land normals
float3 unpack_land_normal(float4 v)
{
  float2 xz=v.xy+v.zw*(1.0/256);
  //xz=v.xy;
  xz=xz*2-1;
  return float3(xz.x, sqrt(1-dot(xz, xz)), xz.y);
}


float fastexp(float t)
{
  return rcp(((t+1)*t+1)*t+1);
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


float3 blend_surf(float3 baseColor, float3 surfColor, float da, float sa)
{
  const float transRange=0.3;
  return lerp(baseColor, surfColor.rgb,
    saturate((da-1)/transRange+pow(abs(sa), 0.7)*(1+1/transRange)));
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


struct GBuffer
{
  float4 o0 : COLOR0; // depth, depth, depth, water
  float4 o1 : COLOR1; // nx, ny, nz, gloss
  float4 o2 : COLOR2; // color, glow
};


float2 pack_to_8x2(float v)
{
  v*=255;
  float h=floor(v);
  float l=v-h;
  return float2(h/255, l);
}


float3 pack_to_8x3(float v)
{
  float2 p=pack_to_8x2(v);
  return float3(p.x, pack_to_8x2(p.y));
}


float unpack_from_8x2(float2 p)
{
  return p.x+p.y/255;
}


float unpack_from_8x3(float3 p)
{
  return p.x+p.y/255+p.z/(255*255);
}


GBuffer pack_gbuf(float depth, float3 norm, float3 color,
  float glow, float gloss, float water=0)
{
  GBuffer gb;
  gb.o0.xyz=pack_to_8x3(depth);
  gb.o0.w=water;
  gb.o1.xyz=norm*0.5+0.5;
  gb.o1.w=gloss;
  gb.o2.rgb=color*0.5;
  gb.o2.a=glow;
  return gb;
}


sampler gbuf0_tex : register(s8);
sampler gbuf1_tex : register(s9);
sampler gbuf2_tex : register(s10);
sampler light_tex : register(s11);


void unpack_gbuf(float4 gb0, float4 gb1, float4 gb2,
  out float depth, out float3 norm,
  out float3 color, out float glow, out float gloss, out float water)
{
  depth=unpack_from_8x3(gb0.xyz);
  water=gb0.w;
  norm =normalize(gb1.xyz*2-1);
  gloss=gb1.w;
  color=gb2.rgb*2;
  glow =gb2.a;
}


#define UNPACK_GBUF(tc) \
  float depth, glow, gloss, water; \
  float3 norm, color; \
  unpack_gbuf( \
    tex2D(gbuf0_tex, tc), \
    tex2D(gbuf1_tex, tc), \
    tex2D(gbuf2_tex, tc), \
    depth, norm, \
    color, glow, gloss, water);
