#include "depth_normal.fsh"

uniform sampler2D rotSampler4x4;

uniform vec2 depthToWHScale;
uniform vec2 zFactors;  // znear, zfar - znear

uniform float radius_std;
uniform float centerDepth;

const int SAMPLE_NUM = 8;
const float SAMPLE_DIVIDE = 1.0 / float(SAMPLE_NUM);
uniform vec3 kernels[SAMPLE_NUM];

in vec2 UV_f;
uniform vec2 rotUVScale;

layout(location = 0) out vec4 fragColor;

vec3 mirror(vec3 axis, vec3 v)
{
	float len = 2.0 * dot(v, axis);
    vec3 result = len * axis - v;
    return result;
}

// Calculate P by depth.
vec3 pos(vec2 uv, float depth)
{
    vec2 xy = (uv - 0.5) * depthToWHScale * depth;
    return vec3(xy, -depth);
}

vec2 _uv(vec3 pos)
{
    vec2 uv = pos.xy / (-pos.z * depthToWHScale);
    return uv + 0.5;
}

vec2 newuv(vec2 uv, float depth, vec3 offset)
{
    // 快速计算方式，与精确值相差0.0003左右
    return uv + offset.xy;
    
//    // 更精确的uv坐标，
//    vec3 offset_t = vec3(offset.xy * depth * depthToWHScale, offset.z);
//    return _uv(pos(uv, depth) + offset_t);
    
//    float n_depth = depth + offset.z;
//    return uv + offset.xy * (depth / n_depth);
}

mat3 RotationFromUp(vec3 v)
{
    float h = 1.0 / (1.0 + v.z);
    float hyy = h * v.y * v.y;
    float hxy = h * v.x * v.y;
    
    mat3 m;
    m[0] = vec3(hyy + v.z,    -hxy,       -v.x);
    m[1] = vec3(-hxy,         1.0 - hyy,  -v.y);
    m[2] = v;
    
    return m;
}

// rotation matrix
vec3 map(mat3 rotmat, vec3 v)
{
    return rotmat * v;
}

// mirrior
vec3 map(vec3 axis, vec3 v)
{
    return reflect(v, axis);
//    return mirror(axis, v);
}

mat3 RotationFromNormal(vec3 randomVec, vec3 normal)
{
    vec3 tangent   = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 TBN       = mat3(tangent, bitangent, normal);
    return TBN;
}

float _DepthZFrom01(float depth) { return depth * zFactors.y + zFactors.x; }

void main()
{
    ////////////////////////////////////////////////////////////////
    // depth, normal, rotation
    float depth;
    vec3 normal;
    _SampleDepthNormal(UV_f, depth, normal);
    depth = _DepthZFrom01(depth);
    
    vec3 rotSample = texture(rotSampler4x4, UV_f * rotUVScale).rgb;
    rotSample = 2.0 * rotSample - 1.0;
    
    ////////////////////////////////////////////////////////////////
    float depthFactor = max(depth / centerDepth, 1.0);
//    float depthFactor = depth / centerDepth;
    float normal_offset = sqrt(radius_std) * depthFactor * 0.03;
    float radius = radius_std;// * depthFactor2;
    
    vec3 sampleScale = vec3(radius);
    // Convert view offset to clip offset.
    sampleScale.xy *= 1.0 / (depth * depthToWHScale);
    
    ////////////////////////////////////////////////////////////////
    vec3 vIrrSample;
    vec4 sceneDepth4 = vec4(0.0);
    vec4 vAccess = vec4(0.0);
    
//    mat3 rotation = RotationFromUp(rotSample);
    mat3 rotation = RotationFromNormal(rotSample, normal);
//    vec3 rotation = rotSample;
    
    for(int i = 0; i < SAMPLE_NUM / 4; i++)
    {
        float v[4];
        for (int j = 0; j < 4; ++j)
        {
            vIrrSample = map(rotation, kernels[i * 4 + j]);
//            vIrrSample *= sign(dot(normal, vIrrSample));
            
            // reduce self shadowing
            vIrrSample += normal * normal_offset;
            
            vIrrSample *= sampleScale;
            vec2 uv = newuv(UV_f, depth, vIrrSample);
            v[j] = _DepthZFrom01(_SampleDepth(uv)) + vIrrSample.z;
        }
        sceneDepth4 = vec4(v[0], v[1], v[2], v[3]);
        
        vec4 dis4 = depth - sceneDepth4;	// greater than zero if occluded.
        vec4 rangeCheck = 1.0 - (dis4 / radius) * 0.5;
        
        vAccess += step(0.0, dis4) * max(vec4(0.0), rangeCheck);
//        vAccess += smoothstep(vec4(0.0), vec4(normal_offset), dis4) * max(vec4(0.0), rangeCheck);
    }
    
    float value = dot(vAccess, vec4(SAMPLE_DIVIDE));
    fragColor.r = value;
}
