Shader "LuckyShaders/MainDiffuse2"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}

        _BumpMap ("Bump Map", 2D) = "bump" {}
        _BumpIntensity ("Bump Intensity", float) = 1

        _RoughnessMap ("Roughness Map", 2D) = "white" {}
        _Roughness ("Roughness", Range(0,1)) = 0.5
        [MaterialToggle] _RoughnessMapInverted ("Is inverted", Float) = 0

        _Metallic ("Metallic", Range(0,1)) = 0.0

        _SpecularMap ("Specular Map", 2D) = "white" {}
        _SpecularIntensity ("Specular Intensity", float) = 1

        _DispMap ("Displacement Map", 2D) = "gray" {}
        _DispIntensity ("Displacement", float) = 0
        _Tesselation ("Tesselation", Range(1,32)) = 4

        _WetMap ("Wetness Map", 2D) = "gray" {}
        _Wetness ("Wetness", Range(0,1)) = 0
        _WaterColor ("Water Color", Color) = (0.87, 0.92, 1, 0.15)

        _AOMap ("AO Map", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Burley fullforwardshadows vertex:vert tessellate:tessFixed
        #pragma target 4.6

        sampler2D _MainTex;
        sampler2D _DispMap;

        struct appdata {
            float4 vertex : POSITION;
            float4 tangent : TANGENT;
            float3 normal : NORMAL;
            float2 texcoord : TEXCOORD0;

            //These are needed for lightmaps and GI
            float2 texcoord1 : TEXCOORD1;
            float2 texcoord2 : TEXCOORD2;
        };

        struct Input
        {
            float2 uv_MainTex;
        };

        fixed _Roughness;
        fixed _Metallic;
        fixed _DispIntensity;
        fixed _Tesselation;
        fixed4 _Color;

        static const float PI = 3.14159265;
        static const float TWO_PI = 6.2831853;
        static const float INV_PI = 0.3183098865475127719056765682209; //TODO: see if reducing accuraccy leads to better performance without negative results
        static const float EPSILON = 0.0001;

        struct SurfaceOutputBurley {
            fixed3 Albedo;
            float subsurface;
            float roughness;
            float metallic;
            float specular;
            float specularTint; //???
            float clearcoat;
            float clearcoatGloss;
            float anisotropic;
            float sheen;
            float sheenTint; //???

            //Default variables
            float3 Normal;
            float3 Tangent;
            float Emission;
            float Alpha;
        };

        float pow2(float x) {
            return x*x;
        }

        float SchlickFresnel(float u) {
            float m = clamp(1.0-u, 0.0, 1.0);
            float m2 = m*m;
            return m2*m2*m; //pow(m,5)
        }

        float GTR1(float NdotH, float a) {
            if (a >= 1.0) return INV_PI;
            float a2 = a*a;
            float t = 1.0 + (a2 - 1.0) * NdotH * NdotH;
            return (a2-1.0) / (PI*log(a2)*t);
        }

        float GTR2(float NdotH, float a) {
            float a2 = a*a;
            float t = 1.0 + (a2 - 1.0) * NdotH * NdotH;
            return a2 / (PI * t*t);
        }

        float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay) {
            return 1.0 / (PI * ax*ay * pow2(pow2(HdotX/ax) + pow2(HdotY/ay) + NdotH*NdotH));
        }

        float smithG_GGX(float NdotV, float alphaG) {
            float a = alphaG*alphaG;
            float b = NdotV*NdotV;
            return 1.0 / (abs(NdotV) + max(sqrt(a + b - a*b), EPSILON));
        }

        float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay) {
            return 1.0 / (abs(NdotV) + sqrt(pow2(VdotX*ax) + pow2(VdotY*ay) + pow2(NdotV) ));
        }

        //Original code: https://github.com/wdas/brdf/blob/master/src/brdfs/disney.brdf
        //I have no idea what the vec3 X and vec3 Y arguments mean, but I'll make it work lol
        //EDIT: X seems to be tangent, Y seems to be binormal
        fixed3 BRDF(fixed3 L, fixed3 V, fixed3 N, fixed3 X, fixed3 Y, fixed3 baseColor, float roughness, float metallic, fixed3 sheenTint, float specular, fixed3 specularTint, float anisotropic) {
            float NdotL = dot(N,L);
            float NdotV = dot(N,V);
            if (NdotL < 0 || NdotV < 0) return fixed3(0.0, 0.0, 0.0);

            fixed3 H = normalize(L+V);
            float NdotH = dot(N,H);
            float LdotH = dot(L,H);

            fixed3 Cdlin = baseColor;
            float Cdlum = 0.3*Cdlin.x + 0.6*Cdlin.y + 0.1*Cdlin.z; //Luminance approximation

            fixed3 Ctint = Cdlum > 0 ? Cdlin/Cdlum : fixed3(1.0, 1.0, 1.0); //Normalize luminosity to isolate hue+sat
            fixed3 Cspec0 = lerp(specular*0.08*lerp(fixed3(1.0, 1.0, 1.0), Ctint, specularTint), Cdlin, metallic);
            fixed3 Csheen = lerp(fixed3(1.0, 1.0, 1.0), Ctint, sheenTint);

            //Diffuse fresnel - go from 1 at normal incidence to 0.5 at grazing
            //and lerp in diffuse retro-reflection based on roughness
            float FL = SchlickFresnel(NdotL);
            float FV = SchlickFresnel(NdotV);
            float Fd90 = 0.5 + 2.0 * LdotH*LdotH * roughness;
            float Fd = lerp(1.0, Fd90, FL) * lerp(1.0, Fd90, FV);

            //Based on Hanrahan-Krueger BRDF approximation of isotropic BSSRDF
            //1.25 scale is used to (roughly) preserve albedo
            //Fss90 used to "flatten" retroreflection based on roughness
            float Fss90 = LdotH*LdotH*roughness;
            float Fss = lerp(1.0, Fss90, FL) * lerp(1.0, Fss90, FV);
            float ss = 1.25 * (Fss * (1.0 / (NdotL + NdotV) - 0.5) + 0.5);

            //Specular
            float aspect = sqrt(1.0 - anisotropic*0.9);
            float ax = max(0.001, pow2(roughness)/aspect);
            float ay = max(0.001, pow2(roughness)*aspect);
            float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
        }

        inline fixed4 LightingBurley (SurfaceOutputBurley s, half3 lightDir, half3 viewDir, half atten) {
            fixed4 c;
            c.rgb = s.Tangent;
            c.a = 1.0;
            return c;
        }

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        float4 tessFixed()
        {
            return _Tesselation;
        }

        void vert (inout appdata v)//, out Input o)
        {
            //UNITY_INITIALIZE_OUTPUT(Input, o);

            float d = tex2Dlod(_DispMap, float4(v.texcoord.xy, 0,0)).r * _DispIntensity;
            v.vertex.xyz += v.normal * d;

            //o.binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
        }

        void surf (Input IN, inout SurfaceOutputBurley o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.metallic = _Metallic;
            o.roughness = _Roughness;
            //o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}