Shader "DBV/Kristo/InstancedGrassShader"
{
    Properties {
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0

        [Space]
        _ColorA("Color A", Color) = (1,1,1,1)
        _ColorB("Color B", Color) = (1,1,1,1)

        [Space]
        _Size("Size",float) = 1

        [Space]
        _AlphaCutoff ("Alpha cutoff", Range(0,1)) = 0.05
        }
    SubShader
    {
        Tags { "RenderType"="Instanced" }
        LOD 200
        Cull Off

        CGPROGRAM
        #pragma surface surf Standard vertex:vert //addshadow
        #pragma multi_compile_instancing
        #pragma instancing_options procedural:setup lodfade

        #pragma target 4.5

        sampler2D _MainTex;

        struct Input{
            float2 uv_MainTex;
            };

        struct GrassElement {
            float3 position;
            float rotation;
            float size;
            float colorBlend;//0-1
            };

        struct custom_appdata {
            float4 vertex : POSITION;
            //float4 tangent : TANGENT;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
            float4 texcoord1 : TEXCOORD1;
            float4 texcoord2 : TEXCOORD2;
            //float4 texcoord3 : TEXCOORD3;
            //fixed4 color : COLOR;
            UNITY_VERTEX_INPUT_INSTANCE_ID
        };

        //Note
        //StructuredBuffers are good when there are loads of data that needs to be read at different places by different threads
        //Constant buffers are good when there isent too much data and threads read the same data

        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            StructuredBuffer<GrassElement> grassBuffer;
        #endif

        
        //https://forum.unity.com/threads/rotating-mesh-in-vertex-shader.501709/
        float3x3 YRotationMatrix(float degrees) {
            float alpha = degrees * UNITY_PI / 180.0;
            float sina, cosa;
            sincos(alpha, sina, cosa);
            return float3x3(
                cosa, 0, -sina,
                0, 1, 0,
                sina, 0, cosa);
            }
        //USAGE: pos.xyz = mul(YRotationMatrix(degrees),pos.xyz);

        void rotate2D(inout float2 v, float r) {
            float s, c;
            sincos(r, s, c);
            v = float2(v.x * c - v.y * s, v.x * s + v.y * c);
            }

        half _Glossiness;
        half _Metallic;
        fixed4 _ColorA, _ColorB;
        float _AlphaCutoff, _Size;

        void setup () {
            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                GrassElement data = grassBuffer[unity_InstanceID];
                //Scale the mesh
                float size = _Size * data.size;
                unity_ObjectToWorld._11_21_31_41 = float4(size, 0, 0, 0);
                unity_ObjectToWorld._12_22_32_42 = float4(0, size, 0, 0);
                unity_ObjectToWorld._13_23_33_43 = float4(0, 0, size, 0);
                //position the mesh
                unity_ObjectToWorld._14_24_34_44 = float4(data.position.xyz, 1);
                
                //No idea what this does
                unity_WorldToObject = unity_ObjectToWorld;
                unity_WorldToObject._14_24_34 *= -1;
                unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
            #endif
            
            }

        void vert (inout custom_appdata v) {
            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                GrassElement data = grassBuffer[unity_InstanceID];
                //Rotate
                v.vertex.xyz = mul(YRotationMatrix(data.rotation),v.vertex);
            #endif
            }

        void surf (Input IN, inout SurfaceOutputStandard o) {

            //Clip the grass
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            clip(c.a - _AlphaCutoff);

            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                GrassElement data = grassBuffer[unity_InstanceID];

                float blend = data.colorBlend;
                fixed4 col = blend * _ColorA + (1-blend) * _ColorB;
            #else
                fixed4 col = _ColorA;
            #endif

            // Albedo comes from a texture tinted by color
            c = c * col;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
            }
        ENDCG
    }
    FallBack "Diffuse"
}