
// The data structure from the vertex shader to the geometry shader
struct VS_to_GS {
	float4 Pos : SV_POSITION;
	float Selection : COLOR;
};

// The data structure from the geometry shader to the pixel shader
struct GS_to_PS {
	float4 Pos : SV_POSITION;
	float2 Pos_Quad : TEXCOORD0;
	float Selection : COLOR;
};

// Edit preview
#define RadiansPerDegree 0.01745329252f
#define PV_A (Arg[0].xyz)
#define PV_B (Arg[1].xyz)
#define PV_DataIndex round(Arg[1].w)
	#define DataIndex_Pos 0
	#define DataIndex_UV1 1
	#define DataIndex_UV2 2
#define PV_Func round(Arg[0].w)
	#define Func_None 0
	#define Func_Move 1
	#define Func_Rotate_X 2
	#define Func_Rotate_Y 3
	#define Func_Rotate_Z 4
	#define Func_Scale 5
float3 RotateAroundX(float3 V, float Degrees) {
	float Radians;
	Radians = Degrees * RadiansPerDegree;
	return float3(V.x, (V.y * cos(Radians)) + (V.z * -sin(Radians)), (V.y * sin(Radians)) + (V.z * cos(Radians)));
}
float3 RotateAroundY(float3 V, float Degrees) {
	float Radians;
	Radians = Degrees * RadiansPerDegree;
	return float3((V.x * cos(Radians)) + (V.z * sin(Radians)), V.y, (V.x * -sin(Radians)) + (V.z * cos(Radians)));
}
float3 RotateAroundZ(float3 V, float Degrees) {
	float Radians;
	Radians = Degrees * RadiansPerDegree;
	return float3((V.x * cos(Radians)) + (V.y * sin(Radians)), (V.x * -sin(Radians)) + (V.y * cos(Radians)), V.z);
}
float4 EditPreview_Pos(float4 Pos,float Selection) {
	float3 TransPos = float3(0.0f, 0.0f, 0.0f);
	float3 RotationAxis = float3(0.0f, 0.0f, 0.0f);
	if (PV_DataIndex == DataIndex_Pos) {
		switch(PV_Func) {
		case Func_None:
			// Do nothing
			TransPos = Pos.xyz;
		break;
		case Func_Move:
			// Move by A
			TransPos = Pos.xyz + PV_A;
		break;
		case Func_Scale:
			// Scale by A around B
			TransPos = ((Pos.xyz - PV_B) * PV_A) + PV_B;
		break;
		case Func_Rotate_X:
			RotationAxis = float3(0.0f, PV_A.y, PV_A.z);
			TransPos = RotationAxis + RotateAroundX((Pos.xyz - RotationAxis), PV_A.x);
		break;
		case Func_Rotate_Y:
			RotationAxis = float3(PV_A.x, 0.0f, PV_A.z);
			TransPos = RotationAxis + RotateAroundY((Pos.xyz - RotationAxis), PV_A.y);
		break;
		case Func_Rotate_Z:
			RotationAxis = float3(PV_A.x, PV_A.y, 0.0f);
			TransPos = RotationAxis + RotateAroundZ((Pos.xyz - RotationAxis), PV_A.z);
		break;
		}
	} else {
		// Do nothing
		TransPos = Pos.xyz;
	}
	return float4(lerp(Pos.xyz,TransPos,Selection), 1.0f);
}

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_to_GS VS( VS_structure input ) {
	VS_to_GS output = (VS_to_GS)0;
	
	// Convert vertice positions from object space to 2D projection
	output.Pos = mul(mul(EditPreview_Pos(input.Pos,input.Selected), ObjectToWorld), WorldToImage);
	
	output.Selection = input.Selected;
	
	return output;
}

#define THICKNESS 3.8f

//--------------------------------------------------------------------------------------
// Geometry Shader
//--------------------------------------------------------------------------------------
[maxvertexcount(24)]
void GS( triangle VS_to_GS input[3], inout TriangleStream<GS_to_PS> TriStream ) {
	GS_to_PS output = (GS_to_PS)0;
	int I;
	int J;
	for( I = 0; I < 3; I++ ) {
		J = (I + 1) % 3;
		
		float2 Direction = normalize(input[I].Pos.xy - input[J].Pos.xy);
		float2 OffsetA = float2(Direction.x * THICKNESS / Dimensions.x, Direction.y * THICKNESS / Dimensions.y);
		float2 OffsetB = float2(Direction.y * THICKNESS / Dimensions.x, -Direction.x * THICKNESS / Dimensions.y);

		output.Selection = input[I].Selection;
		output.Pos = input[I].Pos + float4(OffsetB + OffsetA,0.0f,0.0f);
		output.Pos_Quad = float2(1.0f, 1.0f);
		TriStream.Append( output );
		output.Pos = input[I].Pos + float4(-OffsetB + OffsetA,0.0f,0.0f);
		output.Pos_Quad = float2(-1.0f, 1.0f);
		TriStream.Append( output );
		output.Pos = input[I].Pos + float4(OffsetB,0.0f,0.0f);
		output.Pos_Quad = float2(1.0f, 0.0f);
		TriStream.Append( output );
		output.Pos = input[I].Pos + float4(-OffsetB,0.0f,0.0f);
		output.Pos_Quad = float2(-1.0f, 0.0f);
		TriStream.Append( output );
		
		output.Selection = input[J].Selection;
		output.Pos = input[J].Pos + float4(OffsetB,0.0f,0.0f);
		output.Pos_Quad = float2(1.0f, 0.0f);
		TriStream.Append( output );
		output.Pos = input[J].Pos + float4(-OffsetB,0.0f,0.0f);
		output.Pos_Quad = float2(-1.0f, 0.0f);
		TriStream.Append( output );
		output.Selection = input[J].Selection;
		output.Pos = input[J].Pos + float4(OffsetB - OffsetA,0.0f,0.0f);
		output.Pos_Quad = float2(1.0f, -1.0f);
		TriStream.Append( output );
		output.Pos = input[J].Pos + float4(-OffsetB - OffsetA,0.0f,0.0f);
		output.Pos_Quad = float2(-1.0f, -1.0f);
		TriStream.Append( output );

		TriStream.RestartStrip();
	}
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( GS_to_PS input) : SV_Target {
	return float4(InstanceColor.rgb, InstanceColor.a * input.Selection * saturate(1.0f - length(input.Pos_Quad)));
}
