
#ifndef UNITY_BUILTIN_3X_TREE_LIBRARY_INCLUDED
#define UNITY_BUILTIN_3X_TREE_LIBRARY_INCLUDED

#ifndef SHADER_LOD
	#define SHADER_LOD 0
#endif

#ifndef TREE_SHAODW_PASS
	#define TREE_SHAODW_PASS 0
#endif

struct appdata
{
	float4 vertex : POSITION;
#if !(TREE_DEPTH_PASS)
	#if !(TREE_SHAODW_PASS)
		float4 tangent : TANGENT;
	#endif
	float3 normal : NORMAL;
#endif
	float4 texcoord : TEXCOORD0;
	// float4 texcoord1    : TEXCOORD1;
    // float4 texcoord2    : TEXCOORD2;
    // float4 texcoord3    : TEXCOORD3;
	float2 lightmapUV   : TEXCOORD1;
	float3 branchDir : TEXCOORD2;
	float4 color : COLOR0;
	UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct v2f
{
	float4 position : SV_POSITION;
	float3 texcoord : TEXCOORD0;
	float4 uv : TEXCOORD6;
#if !(TREE_DEPTH_PASS || TREE_SHAODW_PASS)
	#if	SHADER_LOD
		float3 worldPos	   : TEXCOORD1;
		float3 worldNormal : TEXCOORD2;
	#else
		float4 tSpace0 : TEXCOORD1;
		float4 tSpace1 : TEXCOORD2;
		float4 tSpace2 : TEXCOORD3;
	#endif
#endif

#if !TREE_SHAODW_PASS
// #if LOD_FADE_CROSSFADE || _LEAF_DITHER
// 	float4 screen : TEXCOORD4;
// #endif
	DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 5);
	#ifdef _MAIN_LIGHT_SHADOWS
		float4 shadowCoord          : TEXCOORD4;
	#endif
#endif

#if !(TREE_DEPTH_PASS || TREE_SHAODW_PASS)
	float4 color : COLOR0;
#if	SHADER_LOD
	float4 color1 : COLOR1;
	float4 color2 : COLOR2;
#endif
#endif
	UNITY_VERTEX_INPUT_INSTANCE_ID
};
TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
// float _DarkColorHADD;
// wind
//whole
float _BendSpeed;
float _BendSpeedMax;
//side bend
float _SideBendVariance;
float _SideBendIntensity;
float _SideBendIntensityMax;
float _SideBendSoftness;
//down bend
float _DownBendIntensity;
float _DownBendIntensityMax;
float _DownBendVariance;
//gust
float _Gust;
//main wind
// xz: horizontal dir
// y: strength
// z: strength with wave
float4 _JRWind;
// x: time scale
// y: phase fix for time scale changing
// z: phase fix for clamped wind force (clamp01(_MiHoYoWind.y))
float4 SmoothCurve( float4 x ) {
    return x * x *( 3.0 - 2.0 * x );
}

float4 TriangleWave( float4 x ) {
    return abs( frac( x + 0.5 ) * 2.0 - 1.0 );
}

float4 AfsSmoothTriangleWave( float4 x ) {   
	return (SmoothCurve( TriangleWave( x )) - 0.5) * 2.0;   
}
float4 AfsLeafWave(in float4 vert, in float3 branchDir)
{
	float3 windDirection = float3(_JRWind.x, 0, _JRWind.z);
	float windStrength = _JRWind.w;

	// to world space
	float4 posInW = mul(unity_ObjectToWorld, vert);
	branchDir = TransformObjectToWorldNormal(branchDir);
	float3 pivot = unity_ObjectToWorld._14_24_34;
	float3 posInWDir = posInW - pivot;

	// pre calcuation
	float vertPhase = dot(posInW.xyz, 1);
	float dirPhase = dot(branchDir, 1);
	float dirDotWind = dot(branchDir, windDirection);

	float3 rod = vert;
	//float l = length(rod);

	float3 rodH = float3(rod.x, rod.y*0.2, rod.z);
	float lH = length(rodH);

	//gust
	float gust = windStrength * _Gust;

	//side wave
	float sideWavesIn = _Time.y * _BendSpeed;
	sideWavesIn += dirPhase * _SideBendVariance;
	//down wave
	float downWavesIn = _Time.y * _BendSpeed + dirPhase * _DownBendVariance - 0.5; // -0.5 to different from side wave

	float4 Waves = AfsSmoothTriangleWave(float4(sideWavesIn, downWavesIn, 0, 0)) * windStrength;
	float sideWavesSum = Waves.x;
	float sideBendStrength = pow(lH * 0.3, _SideBendSoftness) * (1 - abs(dirDotWind));
	posInW.xyz += normalize(windDirection) * sideWavesSum * sideBendStrength * _SideBendIntensity;

	float downWavesSum = Waves.y;
	float downBendStrength = lH * (1 - abs(branchDir.y));
	posInW.y += downWavesSum * downBendStrength* _DownBendIntensity;

	//gust
	posInW.xyz += normalize(windDirection) * gust;
	posInW.y -= gust * dot(rodH, windDirection) * 0.02;

	//preserve length
	posInW.xyz = pivot + normalize(posInWDir) * length(posInW - pivot);
	return posInW;
}
float4 calcWaves(float wavesIn)
{
	float4 waves = (frac( wavesIn * float4(1.975, 0.793, 0.375, 0.193) ) * 2.0 - 1.0);

	return SmoothCurve( TriangleWave( waves )) * 2.0 - 1.0;
}
float3 UnpackDirFromUV(float2 uv)
{
	float3 dir = float3(uv, 0);
	float zSign = -1;
	if (uv.x > 1.5)
	{
		dir.x -= 3;
		zSign = 1;
	}
	dir.z = zSign * sqrt(1.001 - dot(dir, dir));
	return dir;
}
float _TimeScale;
// Used for branchy tree.
float4 AfsLeafWaveWithBakedData(in float4 vert, in float3 branchDir)
{
	float4 posInW = mul(unity_ObjectToWorld, vert);
	branchDir = TransformObjectToWorldNormal(branchDir);
	if (branchDir.y < -0.9) 
		return posInW; // skip the vertex where branch dir point down

	float3 windDirection = normalize(float3(_JRWind.x, 0, _JRWind.z));
	float windStrength = _JRWind.y;
	float t = windStrength / 2.4;
	float bendSpeedPhase = lerp(_BendSpeed, _BendSpeedMax, t) * _Time.y;
	bendSpeedPhase += (_BendSpeedMax - _BendSpeed) * _TimeScale; // compensate to phase jump caused by wind strength change
	float sideBendIntensity = lerp(_SideBendIntensity, _SideBendIntensityMax, t);
	float downBendIntensity = lerp(_DownBendIntensity, _DownBendIntensityMax, t);

	// to world space
	float3 vertexDir = TransformObjectToWorldNormal(normalize(vert.xyz));
	float3 pivot = unity_ObjectToWorld._14_24_34;
	float3 posInWDir = posInW - pivot;

	// pre calcuation
	float vertPhase = dot(posInW.xyz, 1);
	float dirPhase = dot(branchDir, 1);
	float dirDotWind = dot(branchDir, windDirection);
	float downPhase = dot(vertexDir, 1);

	float3 rod = posInW - pivot;
	float l = length(rod);

	float3 rodH = float3(rod.x, rod.y*0.2, rod.z);
	float lH = length(rodH);

	//scene push
	// if (_CollisionOn)
	// {
	// 	float3 posForDisplacementTex = posInW.xyz;
	// 	float3 pushDist = ScenePushDisplacement(posForDisplacementTex, l*0.5);

	// 	// push along branchDir
	// 	float3 pushDir = branchDir;
	// 	pushDir.y = 0.001;
	// 	pushDir = normalize(pushDir);
	// 	posInW.xyz += dot(pushDir, pushDist) * pushDir;
	// }

	//side wave
	float sideWavesIn = bendSpeedPhase;
	sideWavesIn += dirPhase * _SideBendVariance * 2;
	float4 sideWaves = calcWaves(sideWavesIn);
	// float sideWavesSum = dot(sideWaves, float4(0.3, 0.6, 1.2, 2.0));
	float sideWavesSum = dot(sideWaves, float4(0.3, 0.0, 0.0, 0.0));
	float sideBendStrength = pow(lH * 0.3, _SideBendSoftness);// * (1-abs(dirDotWind));
	posInW.xyz += windDirection * sideWavesSum * sideBendStrength * sideBendIntensity;

	//down wave
	float downWavesIn = bendSpeedPhase + downPhase * _DownBendVariance - 0.5; // -0.5 to different from side wave
	float4 downWaves = calcWaves(downWavesIn);
	float downWavesSum = downWaves.x; // only need one level
	float downBendStrength = lH * (1 - abs(vertexDir.y));
	posInW.y += downWavesSum * downBendStrength* downBendIntensity;

	//gust
	float gust = _JRWind.w * _Gust;
	posInW.xyz += windDirection * gust * sideBendStrength;

	//preseve length
	float3 rodNew = posInW.xyz - pivot.xyz;
	float lNew = length(rodNew);
	posInW.xyz = pivot.xyz + rodNew * lerp(l / lNew, 1, 0.3);

	return posInW;
}
////////////////////////////////////////////////BRDF
#define PI      3.141592653589793238462643383279502884197f
#define PI_INV  0.318309886183790671537767526745028724068f
#if SHADER_API_METAL
// max(a, b) will cause precision problem on ios by haonan.zhou
#define FLOAT_MAX(a, b) (b < a ? a : b)
#else
#define FLOAT_MAX(a, b) max(a, b)
#endif
half Pow5(half x)
{
	return x*x*x*x*x;
}
float safe_div(float n, float d)
{
#if defined(SHADER_API_PSSL) || defined(SHADER_API_D3D11) || defined(SHADER_API_D3D9) || defined(SHADER_API_D3D11_9X)
    // ref: unity SRP
    return (n != d) ? (n / d) : 1.0f;
#else
    return n / FLOAT_MAX(d, BRDF_FILE_EPS);
#endif
}
float3 F_schlickMagical(float3 F0, float LoH, float magic)
{
    return F0 + (1 - F0) * Pow5(1 - LoH) * magic;
}
float C_D_GGX(float a /* roughness */, float NoH)
{
    a *= a;
    float d = (NoH * a - NoH) * NoH + 1.0f; // 2 mad
    float dd = d * d;
    return PI_INV * safe_div(a, dd);
}
float RoughnessRemap_PhyBRDF(float a)
{
    return a * a;
}

// Moved from Common_Lighting.cginc, DeferredshadingCommon.cginc, and Internal-PrePassLighting.shader
half3 PhyBRDF(half3 N, half3 V, half3 L, half roughness, half3 specCol)
{
	float3 H = SafeNormalize(V + L);
	float NoH = saturate(dot(N, H));
	float LoH = saturate(dot(L, H));
    float NoV = saturate(dot(N, V));
    float NoL = saturate(dot(N, L));

	// Specular D
    float a = RoughnessRemap_PhyBRDF(roughness);
    float D = C_D_GGX(a, NoL);
    D = clamp(D, 0.0f, 12.0f);

	//Fresnel
	float3 F = F_schlickMagical(specCol, LoH, 2.0 * (1.0 - a * a)); // Magic Number : 2.0 * ( 1 - m) to implement Fresnel light

	//Output 
	half3 output = D * F;

	return output;
}
half4 PhyBRDF(half3 N, half3 V, half3 L, half roughness, half3 specCol,half shadow)
{
    return half4(PhyBRDF(N, V, L, roughness, specCol), 1.0f);
}
half3 CalculateForwardLight(Light light, half3 bakedGI, half3 albedo, half3 spec, half3 normal, half3 viewDir, half roughness)
{
	half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
	half3 diffuseColor = bakedGI + LightingLambert(attenuatedLightColor, light.direction, normal);
	diffuseColor *= albedo.rgb;

	half nl = saturate(dot(light.direction, normal));

	half4 specColor = PhyBRDF(normal, viewDir, light.direction, roughness, spec, nl);

	return specColor.rgb + diffuseColor;
}
float3 HSVToRGB( float3 c )
{
	float4 K = float4( 1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0 );
	float3 p = abs( frac( c.xxx + K.xyz ) * 6.0 - K.www );
	return c.z * lerp( K.xxx, saturate( p - K.xxx ), c.y );
}	

float3 RGBToHSV(float3 c)
{
	float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
	float4 p = lerp( float4( c.bg, K.wz ), float4( c.gb, K.xy ), step( c.b, c.g ) );
	float4 q = lerp( float4( p.xyw, c.r ), float4( c.r, p.yzx ), step( p.x, c.r ) );
	float d = q.x - min( q.w, q.y );
	float e = 1.0e-10;
	return float3( abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
#endif 
