//friend attraction range, enemy avoidance range, collision avoidance range, friend attraction strength, velocity adaptation strength, enemy avoidance strength, collision avoidance strength
float g_fRules[7];
float3 g_vLightDir = normalize(float3(1, -1, 1));
float PI = 3.141592654;

texture g_texPos;
texture g_texVel;
texture g_texForce;
texture g_texQuad;
texture g_texNormalBuf;
texture g_texColorBuf;
float2 g_vCurBoid;
float g_fTimeDiff;
float4x4 g_mViewProj;
float g_fBoundary;
float g_fLimit;
float g_fForceStr;
float g_fVelStr;
int g_iCurBoid;
int g_iTexSize;
int g_iBoidCount;
int g_iBoidTypes;
float3 g_vColors[] = {
	float3(1, 0, 0),
	float3(0, 1, 0),
	float3(0, 1, 1),
	float3(1, 1, 0),
	float3(1, 0, 1),
	float3(1, 1, 1),
	float3(1, 0.5, 0),
	float3(1, 0, 0.5)
};
float g_fVelLimit;
bool g_b3D;
bool g_bClipSpace;
bool g_bCubeSpace;

sampler samplerPos = sampler_state
{
    Texture = <g_texPos>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
};
sampler samplerVel = sampler_state
{
    Texture = <g_texVel>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
};
sampler samplerForce = sampler_state
{
    Texture = <g_texForce>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
};
sampler samplerQuad = sampler_state
{
    Texture = <g_texQuad>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
};
sampler samplerNormalBuf = sampler_state
{
    Texture = <g_texNormalBuf>;
    MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
};
sampler samplerColorBuf = sampler_state
{
    Texture = <g_texColorBuf>;
    MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
};

int getID(float2 vTexCoord)
{
	return floor(vTexCoord.x * g_iBoidTypes);
}

float mbFactor(float dist)
{
	if (dist > 1)
		return 0;
	return exp(-4 * dist * dist);
}

float4 PSRules(float2 vTexCoord : TEXCOORD0) : COLOR0
{
	float4 pos = tex2D(samplerPos, g_vCurBoid);
	float4 posT = tex2D(samplerPos, vTexCoord);
	float3 vel = tex2D(samplerVel, g_vCurBoid);
	int iID = getID(g_vCurBoid);
	int iIDT = getID(vTexCoord);
	float3 dir = pos - posT;
	float len = length(dir);
	float3 force = float3(0, 0, 0);
	if (len > 0)
	{
		dir = normalize(dir);
		float3 velAvg = tex2Dlod(samplerVel, float4(vTexCoord, 1, 1));
		if (iID == iIDT)
		{
			if (len < g_fRules[0] && len >= g_fRules[2])
			{
				force += g_fRules[3] * exp(-len) * dir;
				force += g_fRules[4] * vel;
			}
		}
		else
		{
			if (len < g_fRules[1])
				force -= g_fRules[5] * dir;
		}
		if (len < g_fRules[2])
			force -= g_fRules[6] * (1 - len / 0.1) * dir;
	}
	return float4(force, 1);
}

struct PSPOS_OUT
{
	float4 vPos : COLOR0;
	float4 vVel : COLOR1;
};

PSPOS_OUT PSUpdatePos(float2 vTexCoord : TEXCOORD0)
{
	float3 P = tex2D(samplerPos, vTexCoord);
	float3 V = tex2D(samplerVel, vTexCoord);
	float3 F = tex2D(samplerForce, vTexCoord);
	V += F * g_fTimeDiff * g_fForceStr;
	if (length(V) > 0)
		V = normalize(V) * clamp(length(V), -g_fVelLimit, g_fVelLimit);
	P += V * g_fTimeDiff * g_fVelStr;
	if (g_bClipSpace)
	{
		if (g_bCubeSpace)
			P = clamp(P, -g_fBoundary, g_fBoundary);
		else if (length(P) > g_fBoundary)
			P = normalize(P) * g_fBoundary;
	}
	else
	{
		if (g_bCubeSpace)
		{
			if (P.x < -g_fBoundary) P.x = g_fBoundary; else if (P.x > g_fBoundary) P.x = -g_fBoundary;
			if (P.y < -g_fBoundary) P.y = g_fBoundary; else if (P.y > g_fBoundary) P.y = -g_fBoundary;
			if (P.z < -g_fBoundary) P.z = g_fBoundary; else if (P.z > g_fBoundary) P.z = -g_fBoundary;
		}
		else if (length(P) > g_fBoundary)
			P = -normalize(P) * g_fBoundary;
	}
	PSPOS_OUT Out;
	Out.vPos = float4(P, 1);
	Out.vVel = float4(V, 1);
	return Out;
}

float4 PSRenderQuad(float2 vTexCoord : TEXCOORD0) : COLOR0
{
	float3 color = tex2D(samplerQuad, vTexCoord);
	float fLimit = g_fLimit;
	return float4(color / (2 * fLimit) + 0.5, 1);
}

struct VSBOID_OUT
{
	float4 vPos : POSITION;
	float2 vTexCoord : TEXCOORD0;
};

VSBOID_OUT VSRenderBoidPoint(float2 vTexCoord : TEXCOORD0, out float ofSize : PSIZE)
{
	float4 P = tex2Dlod(samplerPos, float4(vTexCoord, 0, 0));
	VSBOID_OUT Out;
	Out.vPos = mul(float4(P.xyz, 1), g_mViewProj);
	Out.vTexCoord = vTexCoord;
	if (g_b3D)
		ofSize = 4 * g_fBoundary / Out.vPos.w;
	else
		ofSize = 1.5;
	return Out;
}

float4 PSRenderBoidPoint(float2 vTexCoord : TEXCOORD0) : COLOR0
{
	int iID = getID(vTexCoord);
	float factor = vTexCoord.y * 0.75 + 0.25;
	return float4(factor * g_vColors[iID], 1);
}

VSBOID_OUT VSRenderBoidSphere(float3 vPos : POSITION, float3 vN : NORMAL, float2 vTexCoord : TEXCOORD0, out float2 ovN : TEXCOORD1)
{
	float4 P = tex2Dlod(samplerPos, float4(g_vCurBoid, 0, 0));
	VSBOID_OUT Out;
	Out.vPos = mul(float4(0.025 * g_fBoundary * vPos + P.xyz, 1), g_mViewProj);
	Out.vTexCoord = g_vCurBoid;
	ovN = vN;
	return Out;
}

float4 PSRenderBoidSphere(float2 vCurBoid : TEXCOORD0, float2 vN : TEXCOORD1) : COLOR0
{
	int iID = getID(vCurBoid);
	float factor = clamp(dot(-vN, g_vLightDir), 0, 0.7) + 0.3;
	return float4(factor * g_vColors[iID], 1);
}

VSBOID_OUT VSRenderBoidBlob(float3 vPos : POSITION, float2 vTexCoord : TEXCOORD0, out float2 ovCurBoid : TEXCOORD1)
{
	float4 P = tex2Dlod(samplerPos, float4(g_vCurBoid, 0, 0));
	VSBOID_OUT Out;
	Out.vPos = mul(float4(0.05 * g_fBoundary * vPos + P.xyz, 1), g_mViewProj);
	Out.vTexCoord = vTexCoord;
	ovCurBoid = g_vCurBoid;
	return Out;
}

float4 PSRenderBoidBlob(float2 vTexCoord : TEXCOORD0, float2 vCurBoid : TEXCOORD1) : COLOR0
{
	float factor = mbFactor(length(2 * (vTexCoord - 0.5)));
	return float4(g_vColors[getID(vCurBoid)], factor);
}

VSBOID_OUT VSRenderBoidMetaball(float3 vPos : POSITION, float2 vTexCoord : TEXCOORD0, out float2 ovCurBoid : TEXCOORD1)
{
	float4 P = tex2Dlod(samplerPos, float4(g_vCurBoid, 0, 0));
	VSBOID_OUT Out;
	Out.vPos = mul(float4(0.1 * g_fBoundary * vPos + P.xyz, 1), g_mViewProj);
	Out.vTexCoord = vTexCoord;
	ovCurBoid = g_vCurBoid;
	return Out;
}

struct PSMETABALL_OUT
{
	float4 vColor : COLOR0;
	float4 vNormal : COLOR1;
};

PSMETABALL_OUT PSRenderBoidMetaball(float2 vTexCoord : TEXCOORD0, float2 vCurBoid : TEXCOORD1)
{
	float2 P = 2 * (vTexCoord - 0.5);
	float dist = length(P);
	float factor = mbFactor(length(P));
	PSMETABALL_OUT Out;
	if (dist > 1)
	{
		Out.vColor = float4(0, 0, 0, 0);
		Out.vNormal = float4(0, 0, 0, 0);
		return Out;
	}
	Out.vColor = float4(g_vColors[getID(vCurBoid)], factor);
	float angS, angC;
	sincos(2 * dist / PI, angS, angC);
	Out.vNormal = float4(normalize(float3(angS * normalize(P.xy), -angC)), factor);
	return Out;
}

float4 PSRenderBoidMetaballPP(float2 vTexCoord : TEXCOORD0) : COLOR0
{
	float4 color = tex2D(samplerColorBuf, vTexCoord);
	if (color.a < 0.4)
		return float4(0, 0, 0, 1);
	float3 N = tex2D(samplerNormalBuf, vTexCoord);
	N.xy *= 4;
	N = normalize(N);
	float light = 0.8 * max(0, dot(-N, g_vLightDir)) + 0.2;
	float spec = pow(max(0, dot(reflect(float3(0, 0, 1), N), -g_vLightDir)), 8);
	return float4(light * saturate(color.rgb) + spec, 1);
}

technique CalculateForces
{
	pass Rules
	{
		VertexShader = null;
		PixelShader = compile ps_3_0 PSRules();
		ColorOp[0] = SELECTARG1;
        ColorArg1[0] = TEXTURE;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = SELECTARG1;
		AlphaArg1[0] = TEXTURE;
		AlphaOp[1] = DISABLE;
		AlphaBlendEnable = TRUE;
		SrcBlend = SRCALPHA;
		DestBlend = ONE;
	}
}

technique UpdatePosition
{
	pass P0
	{
		VertexShader = null;
		PixelShader = compile ps_3_0 PSUpdatePos();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
	}
}

technique RenderQuad
{
	pass P0
	{
		VertexShader = null;
		PixelShader = compile ps_3_0 PSRenderQuad();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
	}
}

technique RenderBoidPoint
{
	pass P0
	{
		VertexShader = compile vs_3_0 VSRenderBoidPoint();
		PixelShader = compile ps_3_0 PSRenderBoidPoint();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
	}
}

technique RenderBoidSphere
{
	pass P0
	{
		VertexShader = compile vs_3_0 VSRenderBoidSphere();
		PixelShader = compile ps_3_0 PSRenderBoidSphere();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
	}
}

technique RenderBoidBlob
{
	pass P0
	{
		VertexShader = compile vs_3_0 VSRenderBoidBlob();
		PixelShader = compile ps_3_0 PSRenderBoidBlob();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
		AlphaBlendEnable = TRUE;
		SrcBlend = SRCALPHA;
		DestBlend = INVSRCALPHA;
		ZFunc = ALWAYS;
	}
}

technique RenderBoidMetaball
{
	pass P0
	{
		VertexShader = compile vs_3_0 VSRenderBoidMetaball();
		PixelShader = compile ps_3_0 PSRenderBoidMetaball();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
		AlphaBlendEnable = TRUE;
		SrcBlend = SRCALPHA;
		DestBlend = ONE;
		ZFunc = ALWAYS;
	}
}

technique RenderBoidMetaballPP
{
	pass P0
	{
		VertexShader = NULL;
		PixelShader = compile ps_3_0 PSRenderBoidMetaballPP();
		ColorOp[0] = MODULATE;
        ColorArg1[0] = TEXTURE;
        ColorArg2[0] = CURRENT;
        ColorOp[1] = DISABLE;
        AlphaOp[0] = DISABLE;
	}
}