

#ifndef X2M_GRASS_ANIMATE_INCLUDED
#define X2M_GRASS_ANIMATE_INCLUDED
#include "FoliageWindField.hlsl"

struct vertex_input_compress {
	float4 vertex : POSITION;
	UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct vertex_input {
	float4 vertex;
	half3 normal;
	float2 texcoord;
	half4 texcoord1;
};

vertex_input DecodeVertexInputCompress(vertex_input_compress v)
{


	////////////// vertex, uv, normal, color covert to uint3   max bit is 30 is not 32 /////////////////////////////
	// x
	//  0                           8                               14                              20                      30
	//      vertex.x -> 256 ->8 bit     vertex.y ->64->6 bit             normal.x -> 64 ->6 bit          uv.x ->1024->10bit
	//
	// y
	// 0                            8                       14                          20                           30
	//      vertex.z ->256 ->8 bit    normal.y->64->6 bit        normal.z ->64 ->6 bit        uv.y ->1024->10 bit
	//
	// z
	// 0                                            9                                        18                                 26                                      30
	//   color.r -> 64 * 10 = 640 ->512->9 bit        color.g -> 64 * 10 = 640 ->512->9 bit      color.b -> 360 = 256 ->8 bit       color.a -> 3.0 * 10 -> 16 -> 4bit
	//////////////////////////////////////////////////////////////////////////////////////////

	vertex_input ret = (vertex_input)0;
	uint3 data = asuint(v.vertex.xyz);
	uint4 x = (data.xxxx >> uint4(0, 8, 14, 20));
	x &= uint4(255, 63, 63, 1023);
	uint4 y = (data.yyyy >> uint4(0, 8, 14, 20));
	y &= uint4(255, 63, 63, 1023);
	uint4 z = (data.zzzz >> uint4(0, 9, 18, 26));
	z &= uint4(511, 511, 255, 15);

	ret.vertex.xyzw = float4(x.xy, y.x, 1.0);
	ret.vertex.xyz *= float3(1.0 / 256.0, 1.0 / 64.0, 1.0 / 256.0);
	ret.vertex.xz -= float2(0.5, 0.5);

	ret.texcoord.xy = float2(x.w, y.w);
	ret.texcoord.xy *= (1.0 / 1024.0);
	
	ret.normal = half3(x.z, y.yz);
	ret.normal *= (1.0 / 64.0);
	ret.normal -= half3(0.5, 0.5, 0.5);
	ret.normal = normalize(ret.normal);

	ret.texcoord1 = z;
	// xy : offset [0m, 8m], z : rotate [0, 3.141592 * 2], w : scale [0, 3]
	ret.texcoord1 *= half4(8.0 / 512.0, 8.0 / 512.0, (3.141592 * 2.0) / 256.0, 3.0 / 16.0);

	// test only
	//ret.texcoord1.y *= 0;
	//ret.texcoord1.x *= 0;
	//ret.texcoord1.w *= 3.0;
	// test end

	return ret;
}


float4 CalHeigthMapUVFromWorldPos(float2 uv, float2 vOffset)
{
    vOffset.xy *= 0.875; //7.0 / 8.0;
    uv += vOffset.xy;
    uv *= _RegionHeightMap_TexelSize.xy; //world pos -> uv
    uv = frac(uv);
    uv.xy += float2(1, 1);
    uv = frac(uv);
    uv += (_RegionHeightMap_TexelSize.xy * 0.5);
	//uv.y = 1.0 - uv.y;
	//uv += 0.5f; //heightmap offset -256 -> uv offset 0.5
    float offset = 260.0 / 512.0; //According to: HeightMapMaskExport.Export(): Vector3 rayPos = new Vector3(ni - 260, 1000, nj - 260)
    uv += offset;
	
//#if UNITY_UV_STARTS_AT_TOP
//			 DX
//			uv.y = 1.0 - uv.y;
//			 OpenGL, don't need 1.0 - y
//#endif
    return float4(uv, 0, 0);
}


half2 GetHeightInBlade(float3 localPos, float4 worldPos)
{
	float4 uv = CalHeigthMapUVFromWorldPos(worldPos.xz, localPos.xz);
	float4 heightMapValue1 = SAMPLE_TEXTURE2D_LOD(_RegionHeightMap, sampler_RegionHeightMap, uv.xy, uv.w).rgba;
	heightMapValue1.r *= -25.5;

	return heightMapValue1.xy;

}


half3 DecodeDisplacement(half2 angle)
{
	/*float s1 = sin(zenith);
	float s2 = sin(azimuth);
	float c1 = cos(zenith);
	float c2 = cos(azimuth);

	float3 ret = float3(s1 * c2,
		c1,
		s1 * s2);*/

	half2 s = sin(angle);
	half2 c = cos(angle);
	half3 ret = half3(s.x, c.x, s.x);
	ret.xz *= half2(c.y, s.y);
	ret = normalize(ret);
	return ret;

}

half3 DecodeNormal(half2 angle)
{
	/*half s1 = sin(zenith);
	half s2 = sin(azimuth);*/
	half2 s = sin(angle);
	half2 c = cos(angle);
	//half c1 = cos(zenith);
	//half c2 = cos(azimuth);

	//half3 ret = half3(s1 * c2,
	//	c1,
	//	s1 * s2);

	half3 ret = half3(s.x, c.x, s.x);
	ret.xz *= half2(c.y, s.y);
	ret = normalize(ret);
	return ret;

}


void GetDisplacement(float3 worldPos, half angle, half randomSeed2, half curBend, out half3 vDisplacement, out half3 vNormal)
{
	half4 ret = half4(0, 0, 0, 0);

	half maxDistance = 128.0;	// 8m->64 grass, 1024 / 64 = 16 * 8 = 128;
	half distance = fmod(worldPos.x + randomSeed2 * 0.2, maxDistance);
	// now is (-maxDistance, maxDistance), next to (0, 2.0 * maxDistance)
	distance += maxDistance;
	// to (0, maxDistance)
	distance = fmod(distance, maxDistance);
	float delayTime = distance / maxDistance;//distance / _Speed;
	//delayTime /= (_DisplacementV2Tex_TexelSize.z * (1.0 / 30.0));
	delayTime *= _Speed;

	float2 uvDisplacement = float2(
		_CurDisplacementFrame - delayTime,
		clamp(angle * 360 + 1 + 0.5, 1.5, 360.5) * _DisplacementV2Tex_TexelSize.y
		) ;

	uvDisplacement.x += 1.0f;
	uvDisplacement.x = fmod(uvDisplacement.x, 1.0);

#if UNITY_UV_STARTS_AT_TOP
	// DX
	uvDisplacement.y = 1.0 - uvDisplacement.y;
	// OpenGL, don't need 1.0 - y
#endif
	ret = SAMPLE_TEXTURE2D_LOD(_DisplacementV2Tex, sampler_DisplacementV2Tex, uvDisplacement, 0);

	//curBend = (1.0 - curBend) * (1.0 - curBend) * -1 + 2.0 * curBend * (1.0 - curBend) * _BendCoef;
	curBend *= _BendCoef;
	curBend *= _global_wind_scale;
	vDisplacement = DecodeDisplacement(half2(ret.x * curBend, ret.y));
	vNormal = DecodeNormal(ret.zw);
}

half4 DecodeIdleDisplacement(half randomSeed1, half randomSeed2, half r)
{
	half b = randomSeed1 * 6.28;
	half a = randomSeed2 * 3.14 / 2.0;//*(_Idle.y / 180.0);	//zenith
	/*float s1 = sin(a);
	float s2 = sin(b);
	float c1 = cos(a);
	float c2 = cos(b);


	float4 ret = float4(s1 * c2,
		c1,
		s1 * s2,
		0.0);

	ret = normalize(ret) * r;*/
	
	return half4(DecodeDisplacement(half2(a, b)) * r, 0);

}

half4 GetSkillforGrassDirectionEncode(float3 worldPos)
{
	half4 ret = half4(0, 0, 0, 0);
	half2 uv = floor(worldPos.xz * 5) - floor(_MainCharPos.xz * 5);
	uv *= _SkillforGrassDirectionEncodeTex_TexelSize.xy;
	uv += half2(0.5, 0.5);

	#if UNITY_UV_STARTS_AT_TOP
	// DX
		uv.y = 1.0 - uv.y;
	// OpenGL, don't need 1.0 - y
	#endif

	if (uv.x >= 0 && uv.x <= 1 && uv.y >= 0 && uv.y <= 1)
	{
		ret = SAMPLE_TEXTURE2D_LOD(_SkillforGrassDirectionEncodeTex, sampler_SkillforGrassDirectionEncodeTex, uv, 0);
	}

	return ret;
}

half4 GetSkillforGrassTimeEncode(float3 worldPos)
{
	half4 ret = half4(0, 0, 0, 0);
	half2 uv = floor(worldPos.xz * 5) - floor(_MainCharPos.xz * 5);
	uv *= _SkillforGrassTimeEncodeTex_TexelSize.xy;
	uv += half2(0.5, 0.5);

	#if UNITY_UV_STARTS_AT_TOP
	// DX
		uv.y = 1.0 - uv.y;
	// OpenGL, don't need 1.0 - y
	#endif
	if (uv.x >= 0 && uv.x <= 1 && uv.y >= 0 && uv.y <= 1)
	{
		ret = SAMPLE_TEXTURE2D_LOD(_SkillforGrassTimeEncodeTex, sampler_SkillforGrassTimeEncodeTex, uv, 0);
	}

	return ret;
}


half3 Translucency(half3 vLight, half3 vView, half3 vNormal)
{
	half3 transLightDir = vLight + vNormal *  _TranslucentDistortion;
	half transDot = pow(saturate(dot(vView, -transLightDir)), _TranslucentPower) *  _TranslucentScale;
	half3 transLight = transDot *  _TranslucentAttenuation.rgb * _MainLightColor.rgb;
	return transLight;
}


half3 PhongSpec(half3 vLight, half3 vEye, half3 vNormal)
{
	half3 r = reflect(-vLight, vNormal);
	float d = max(0, dot(vEye, r));
	half spec = pow(d, _SpecularPower * 128.0) * _Gloss;
	return spec;
}

half3 BlinnPhongSpec(half3 vLight, half3 vEye, half3 vNormal)
{
	half3 h = normalize(vLight + vEye);
	half nh = max(0, dot(vNormal, h));
	half spec = pow(nh, _SpecularPower * 128.0) * _Gloss;
	return spec;
}


half2 CalLOD0(half dist, half maskScale)
{
	half2 lodDist = half2(dist / (32.0 - 8), (dist - 32) / -8.0);
	lodDist = saturate(lodDist);
	half4 lodValue = half4(_LOD0.xy * maskScale, _LOD0.zw);
	half2 LODScaleWidth = lerp(lodValue.xz, lodValue.yw, lodDist.x);
	LODScaleWidth *= lodDist.y;
	return  LODScaleWidth;
}




half3 RotateY(half3 localPos)
{
	half3 vCenter = half3(4, 0, 4);
	half3 vDif = localPos - vCenter;
	half3 vDifRotateY = vDif;
	//vDif = mul((float3x3)unity_ObjectToWorld, vDif);
	vDifRotateY.x = dot(vDif.xz, half2(unity_ObjectToWorld[0][0], unity_ObjectToWorld[0][2]));
	vDifRotateY.z = dot(vDif.xz, half2(unity_ObjectToWorld[2][0], unity_ObjectToWorld[2][2]));
	return vDifRotateY + vCenter;
}

void CommonVertPosAnimate(vertex_input v,
	out float4 oroot,
	out half odist,
	out half oscale,
	out half orandomValue,
	out half4 overtexColor,
	out half omaskScale)
{
	//overtexColor = DecodeTemplateColor(v.color);
	//overtexColor.xy = v.texcoord1.xy * 8.0;
	overtexColor = v.texcoord1;

	half3 localPos = half3(overtexColor.x, 0, overtexColor.y);
	localPos = RotateY(localPos);
	oroot = float4(unity_ObjectToWorld[0][3], unity_ObjectToWorld[1][3], unity_ObjectToWorld[2][3], 1.0f);

	float2 heightValue = GetHeightInBlade(localPos, oroot);
	localPos.y = heightValue.r + _UpOffset;
	float3 worldOffset = localPos.xyz - _WorldOffset.xyz;
	oroot.xyz += worldOffset.xyz;

	orandomValue = overtexColor.z / 6.28;
#if _SCENE_EDITOR
	if (_IsMask == 1)
#endif
	{
		omaskScale = heightValue.g;
	}

	oscale = overtexColor.w * _Height;

	odist = length(_WorldSpaceCameraPos.xyz - oroot.xyz);
}

void CommonVertTransformAnimate(vertex_input v,
	float4 root, half dist, half randomValue,  half4 vertexColor,
	half2 LODScaleWidth,
	out half3 onorm,
	out float3 overt
)
{
	
	float4 vertex = v.vertex;
	// begin Anti-Aliasing
	//vertex.xz *= max(1.0, (1.0 + dist / 32.0 * _AntiAliasing));
	// end Anti-Aliasing

	vertex.xz *= (LODScaleWidth.y * _Width);

	//half4 vUp = half4(0, 1, 0, 0);	// in world and in local is the same vector		

	half3 displacement = half3(0.0, 1.0, 0.0);	//DecodeIdleDisplacement(randomValue, randomValue2, 1.0);//float3(0.0, 1.0, 0.0);


	half3 vNormal = half3(0, 0, 1);

	GetDisplacement(root.xyz,
		randomValue, vertexColor.w / 3.0, 
		v.vertex.y * 0.5,
		displacement, vNormal);

	half4 v2p = half4(displacement.xyz
		, 0.0);

	half3 v2pNormal = normalize(v2p.xyz);
	half3 vBiNormal = cross(v2pNormal, vNormal);

	half vertexLength = length(vertex.xyz);
	vertexLength *= LODScaleWidth.x;

	//// rotate and offset the vertex
	float3x3 matrixRotate = float3x3(
		vBiNormal.x, v2pNormal.x, vNormal.x,
		vBiNormal.y, v2pNormal.y, vNormal.y,
		vBiNormal.z, v2pNormal.z, vNormal.z
		);

	vertex.xyz = mul(matrixRotate, vertex.xyz);
	onorm = mul(matrixRotate, float3(v.normal.xyz));
	// real pos
	vertex.xyz = normalize(vertex.xyz) * vertexLength;

	overt = vertex.xyz;
}

bool ReduceDensity(half density, half key)
{
	if (key > density)
		return true;
	else
		return false;
}

half3 RotateZ(half angle, half3 overt)
{
	half3 rotVert = overt;
	half cosA = cos(angle);
	half sinA = sin(angle);

	rotVert.x = overt.x * cosA + overt.y * sinA;
	rotVert.y = -overt.x * sinA + overt.y * cosA;
	return rotVert;
}
//Goldman fomula
half3 RotateAboutAxis(half angle, half3 axis, half3 verts) {
	half3 rotVert;
	float cosA = cos(angle);
	float sinA = sin(angle);
	float one_cosA = 1 - cosA;
	float azs = axis.z*sinA;
	//half ays = axis.y*sinA;
	//half ays = 0;
	float axs = axis.x*sinA;
	//half axay1_c = axis.x*axis.y*one_cosA;
	//half axay1_c = 0;
	float axaz1_c = axis.x*axis.z*one_cosA;
	//half ayaz1_c = axis.y*axis.z*one_cosA;
	//half ayaz1_c = 0;
	float axax1_cpc = axis.x*axis.x*one_cosA + cosA;
	//half ayay1_cpc =  axis.y*axis.y*one_cosA + cosA;
	float ayay1_cpc = cosA;
	float azaz1_cpc = axis.z*axis.z*one_cosA + cosA;

	//half3x3 rotMatrix = half3x3(
	//	axax1_cpc, axay1_c - azs, axaz1_c + ays,
	//	axay1_c + azs, ayay1_cpc, ayaz1_c - axs,
	//	axaz1_c - ays, ayaz1_c + axs, azaz1_cpc
	//	);
	float3x3 rotMatrix = float3x3(
		axax1_cpc, - azs, axaz1_c,
		azs, ayay1_cpc, - axs,
		axaz1_c, axs, azaz1_cpc
		);
	return rotVert = mul(rotMatrix, verts);
}
#endif // X2M_GRASS_ANIMATE_INCLUDED
