texture gTextureSlot1;
texture gTextureSlot2;
texture gTextureSlot3;
texture gTextureSlot4;

sampler2D velocity = 
sampler_state
{
    Texture = (gTextureSlot1);
    MipFilter = POINT;
    MinFilter = POINT;
    MagFilter = POINT;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};

sampler2D pressure = 
sampler_state
{
    Texture = (gTextureSlot2);
    MipFilter = POINT;
    MinFilter = POINT;
    MagFilter = POINT;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};

sampler2D divergence = 
sampler_state
{
    Texture = (gTextureSlot1);
    MipFilter = POINT;
    MinFilter = POINT;
    MagFilter = POINT;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};

sampler2D sTextureSlot1L = 
sampler_state
{
    Texture = (gTextureSlot1);
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};

sampler2D obstacle = 
sampler_state
{
    Texture = (gTextureSlot3);
    MipFilter = POINT;
    MinFilter = POINT;
    MagFilter = POINT;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};

//
// General simulation constants
//

const float4 initValue;
const float fullTexel;
const float halfTexel;
const float dTime;

//
// XNA prototype specific
//
float2 Point;
float2 velForce;
float velCoef;

#define epsilon 0.0001

 
struct VS_INPUT { 
    float4 Position : POSITION0; 
    float2 UV : TEXCOORD0; 
};

struct VS_OUTPUT { 
    float4 Position : POSITION0; 
    float2 UV : TEXCOORD0; 
    float4 UVxOf : TEXCOORD1; 
    float4 UVyOf : TEXCOORD2; 
};
 
VS_OUTPUT NullVertexShader(VS_INPUT IN) { 
    VS_OUTPUT Output;
    
    Output.Position = IN.Position; 
    Output.UV = IN.UV; 
    
    Output.UVxOf = float4(IN.UV + float2(-fullTexel,0), IN.UV + float2(fullTexel,0));
    Output.UVyOf = float4(IN.UV + float2(0,fullTexel),  IN.UV + float2(0,-fullTexel));
 
    return Output; 
};

bool isObstacle(float2 UV)
{
    float2 check = tex2D(obstacle,UV).xy;
	return abs(check.x)+abs(check.y) > 0.0;    
}

bool checkObstacleFromVelocity(float2 obstacleVel)
{
	return abs(obstacleVel.x)+abs(obstacleVel.y) > 0.0;    
}

float2 geObstacletVelocity(float2 UV)
{
	return tex2D(obstacle,UV).xy;    
}

float4 psDrawNormal(VS_OUTPUT IN):COLOR0
{ 
    float nC = tex2D(sTextureSlot1L,IN.UV + halfTexel).x;    
    
    float nL = tex2D(sTextureSlot1L,IN.UVxOf.xy + halfTexel).x;
	float nR = tex2D(sTextureSlot1L,IN.UVxOf.zw + halfTexel).x;
	float nT = tex2D(sTextureSlot1L,IN.UVyOf.xy + halfTexel).x;
	float nB = tex2D(sTextureSlot1L,IN.UVyOf.zw + halfTexel).x;
	
	float dXn =  nL - nC;
	float dXp =  nR - nC;
	float dYn =  nT - nC;
	float dYp =  nB - nC;
	
	float derX = 0.5*(dXp - dXn);
	float derY = 0.5*(dYp - dYn);
	
	float3 tX = (float3(1,0,derX));
	float3 tY = (float3(0,1,derY));
	
	float3 normal = normalize(cross(tX,tY));
    
    return float4(0.5*(normal+1.0),1);
} 

float4 psDrawVelocity(VS_OUTPUT IN):COLOR0
{ 
    float4 cellVelocity = tex2D(sTextureSlot1L,IN.UV + 0.5*fullTexel).xyzw;
    
    //cellVelocity = normalize(cellVelocity);
    //cellVelocity = 0.5*(cellVelocity + 1.0);   
    
    return float4(abs(cellVelocity.xy),0,0);
} 

float4 psAddVelocityInfluence(VS_OUTPUT IN):COLOR0
{ 
    float2 cellVelocity = tex2D(velocity,IN.UV).xy; 
    
    float d = saturate(1.0-75.0*length(Point - IN.UV.xy)); 
    
    float2 outForce = (Point - IN.UV.xy);
    
    float2 vel = velCoef*d*(velForce - outForce)*10000.0;    

    return float4(cellVelocity+vel,0,0);
} 
 
float4 psAdvectVel(VS_OUTPUT IN):COLOR0
{ 
    float2 cellPosition = IN.UV;
    float2 cellVelocity = tex2D(velocity,IN.UV).xy;
    
    cellPosition -= dTime * cellVelocity;
    
    float2 advVel = tex2D(sTextureSlot1L,cellPosition + halfTexel);
 
    return float4(advVel,0,0);
} 

float4 psDivergence(VS_OUTPUT IN):COLOR0
{
	float2 fL = tex2D(velocity,IN.UVxOf.xy).xy;
	float2 fR = tex2D(velocity,IN.UVxOf.zw).xy;
	float2 fT = tex2D(velocity,IN.UVyOf.xy).xy;
	float2 fB = tex2D(velocity,IN.UVyOf.zw).xy;
	
	float2 oL = geObstacletVelocity(IN.UVxOf.xy);
	float2 oR = geObstacletVelocity(IN.UVxOf.zw);
	float2 oT = geObstacletVelocity(IN.UVyOf.xy);
	float2 oB = geObstacletVelocity(IN.UVyOf.zw);
	
	if(checkObstacleFromVelocity(oL)) fL = oL;
	if(checkObstacleFromVelocity(oR)) fL = oR;
	if(checkObstacleFromVelocity(oT)) fL = oT;
	if(checkObstacleFromVelocity(oB)) fL = oB;

    float divergence = 0.5 * ((fR.x - fL.x)+ (fT.y - fB.y));
    
    return divergence;
}

float4 psJacobiIteration(VS_OUTPUT IN):COLOR0
{
	float pC = tex2D(pressure,IN.UV).x;
	float divergence = tex2D(divergence,IN.UV).x;
	
	float pL = tex2D(pressure,IN.UVxOf.xy).x;
	float pR = tex2D(pressure,IN.UVxOf.zw).x;
	float pT = tex2D(pressure,IN.UVyOf.xy).x;
	float pB = tex2D(pressure,IN.UVyOf.zw).x;
	
	if(isObstacle(IN.UVxOf.xy))  pL = pC;
	if(isObstacle(IN.UVxOf.zw))  pR = pC;
	if(isObstacle(IN.UVyOf.xy))  pT = pC;
	if(isObstacle(IN.UVyOf.zw))  pB = pC;
	
	return float4((pL + pR + pT + pB - divergence)*0.25,0,0,0);
}

float4 psProject(VS_OUTPUT IN):COLOR0
{
	float pC = tex2D(pressure,IN.UV).x;

	float pL = tex2D(pressure,IN.UVxOf.xy).x;
	float pR = tex2D(pressure,IN.UVxOf.zw).x;
	float pT = tex2D(pressure,IN.UVyOf.xy).x;
	float pB = tex2D(pressure,IN.UVyOf.zw).x;
	
	float2 obstacleV = float2(0,0);
	float2 velocityNegative = float2(1,1);
	
	float2 oL = geObstacletVelocity(IN.UVxOf.xy);
	float2 oR = geObstacletVelocity(IN.UVxOf.zw);
	float2 oT = geObstacletVelocity(IN.UVyOf.xy);
	float2 oB = geObstacletVelocity(IN.UVyOf.zw);
	
	if(checkObstacleFromVelocity(oL)) {pL = pC; obstacleV.x = oL.x;  velocityNegative.x = 0;}
	if(checkObstacleFromVelocity(oR)) {pR = pC; obstacleV.x = oR.x;  velocityNegative.x = 0;}
	if(checkObstacleFromVelocity(oT)) {pT = pC; obstacleV.y = oT.y;  velocityNegative.y = 0;}
	if(checkObstacleFromVelocity(oB)) {pB = pC; obstacleV.y = oB.y;  velocityNegative.y = 0;}
	
	float2 gradient = 0.5 * float2((pR - pL),(pT - pB));
	
	float2 velocity = tex2D(velocity, IN.UV).xy;
	float2 projectedVelocity = velocity - gradient;	
	
	projectedVelocity = (velocityNegative*projectedVelocity) + obstacleV;

	return float4(projectedVelocity,0,0);
}

float4 psInit(VS_OUTPUT IN):COLOR0
{
	return initValue;
}

float4 psCopyArray(VS_OUTPUT IN):COLOR0
{
	return tex2D(velocity,IN.UV);
}
 
technique AdvectVel { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psAdvectVel(); 
    } 
}

technique Divergence { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psDivergence(); 
    } 
}

technique JacobiIteration { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psJacobiIteration(); 
    } 
}

technique Project { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psProject(); 
    } 
}

technique Init { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psInit(); 
    } 
}

technique CopyArray { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psCopyArray(); 
    } 
}
    
technique DrawVelocity { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psDrawVelocity(); 
    } 

}

technique DrawNormal { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psDrawNormal(); 
    } 

}

technique AddVelocityInfluence { 
    pass P0 { 
        VertexShader = compile vs_3_0 NullVertexShader(); 
        PixelShader = compile ps_3_0 psAddVelocityInfluence(); 
    } 

}