// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "DBV/Kristo/InstancedConsumeGrassShader"
{
    Properties {
        _MainTex("Main tex", 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
        _Height("Height",float) = 1

        [Space]
        _WindTex("Wind tex", 2D) = "grey" {}
        _WindAmount("Wind strength", float) = 1
        _WindSize("Wind size", float) = 1
        _WindSpeed("Wind speed", float) = 1

        [Space]
        _AlphaCutoff ("Alpha cutoff", Range(0,1)) = 0.05
        _DitherTex ("Dither tex.", 2D) = "white" {}

        fadeStartDist ("Fade start",float) = 1
        fadeEndDist ("Fade end",float) = 1
        }
    SubShader
    {
        Tags { "RenderType"="Instanced" }
        LOD 200
        Cull Off
        //ZWrite Off

        CGPROGRAM
        #pragma surface surf BlinnPhong vertex:vert noshadowmask nodynlightmap nodirlightmap nolightmap noshadow
        #pragma multi_compile_instancing
        #pragma instancing_options procedural:setup

        #pragma target 4.5

        sampler2D _MainTex, _DitherTex, _WindTex;
        fixed4 _DitherTex_TexelSize, _WindTex_TexelSize;

        struct Input{
            float2 uv_MainTex;
            float4 screenPos;
            float3 worldPos;
            };

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

        struct custom_appdata {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
            float4 texcoord1 : TEXCOORD1;
            float4 texcoord2 : TEXCOORD2;
            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 dat

        #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
            StructuredBuffer<GrassElement> grassBuffer;
            StructuredBuffer<uint> inGrassBuffer;//ConsumeStructuredBuffer<uint> inGrassBuffer;
        #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, _Height;

        float fadeStartDist, fadeEndDist, _WindAmount, _WindSize, _WindSpeed;

        //float3 pos;
        float colorBlend;
        float rotationInDegrees;

        void setup () {
            #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
                uint index = inGrassBuffer[unity_InstanceID];//inGrassBuffer.Consume();//Consume buffer is not working at all for some reason
                GrassElement data = grassBuffer[index];//unity_InstanceID

                //save data
                float3 pos = data.position.xyz;
                colorBlend = data.colorBlend;
                rotationInDegrees = data.rotation;

                //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*_Height, 0, 0);
                unity_ObjectToWorld._13_23_33_43 = float4(0, 0, size, 0);

                //position the mesh
                unity_ObjectToWorld._14_24_34_44 = float4(pos, 1);
                
                //No idea what this does but it must be important
                unity_WorldToObject = unity_ObjectToWorld;
                unity_WorldToObject._14_24_34 *= -1;
                unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
            #else
                //pos = float3(0,2,0);
                colorBlend = 0.5;
                rotationInDegrees = 45;
            #endif
            }

        void vert (inout custom_appdata v) {
            //Rotate
            v.vertex.xyz = mul(YRotationMatrix(rotationInDegrees),v.vertex);

            //Do wind
            float vertexHeight = v.vertex.y;

            float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
            float2 windCoord = (worldPos.xz)*_WindSize + (_Time.yy)*_WindSpeed;
            float3 wind = (tex2Dlod(_WindTex,float4(windCoord.xy,0,0)*2 - 1 )).xyz * _WindAmount;//tex2D(_WindTex, windCoord).rgb * vertexHeight;

            v.vertex.xyz += float3(wind.x-0.5,0,wind.z) * vertexHeight;
            }

        void surf (Input IN, inout SurfaceOutput o) {
            //Get color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);

            //Do cutout
            clip(c.a - _AlphaCutoff);

            //Do dithering
            float2 screenPos = IN.screenPos.xy / IN.screenPos.w;
            float2 ditherCoord = screenPos * _ScreenParams.xy * _DitherTex_TexelSize.xy;
            float ditherAmount = tex2D(_DitherTex, ditherCoord).r;

            float distToCamera = distance(IN.worldPos, _WorldSpaceCameraPos);
            if(distToCamera > fadeStartDist) {
                float ditherRange = 1 - saturate((distToCamera-fadeStartDist)/(fadeEndDist-fadeStartDist));
                clip(ditherRange - ditherAmount);
                }

            //Do blending
            fixed4 col = colorBlend * _ColorA + (1-colorBlend) * _ColorB;

            // 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"
}