
// The data structure from the vertex shader to the geometry shader
struct VS_to_GS {
	float3 Normal_WorldSpace : NORMAL;
	float4 Pos_WorldSpace : TEXCOORD1;
};

// The data structure from the geometry shader to the pixel shader
struct GS_to_PS {
	float4 Pos : SV_POSITION;
};

VS_to_GS VertexSmoothMiddle(VS_to_GS X, VS_to_GS Y, float Round);
GS_to_PS PGVS(VS_to_GS input);

//--------------------------------------------------------------------------------------
// Pre geometry vertex Shader
// This is an entry point that is required by the engine
//--------------------------------------------------------------------------------------
VS_to_GS VS( VS_structure input ) {
	VS_to_GS output = (VS_to_GS)0;
	
	// Convert vertice positions from object space to world space
	output.Pos_WorldSpace = mul( input.Pos, ObjectToWorld );
	
	// Transform normals to world space
	output.Normal_WorldSpace = mul(input.Normal,(float3x3)ObjectToWorld);
	
	return output;
}

//--------------------------------------------------------------------------------------
// Geometry Shader
//--------------------------------------------------------------------------------------
#define ADD_VERTEX(A) TriStream.Append(PGVS(A));
#define COMPLETE_STRIP TriStream.RestartStrip();
[maxvertexcount(24)]
void GS( triangle VS_to_GS input[3], inout TriangleStream<GS_to_PS> TriStream ) {
	VS_to_GS A = input[0];
	VS_to_GS B = input[1];
	VS_to_GS C = input[2];
	VS_to_GS AB;
	VS_to_GS BC;
	VS_to_GS CA;
	if (DetailLevel <= 0.00001f) {
		// The original triangle
		ADD_VERTEX(A)
		ADD_VERTEX(B)
		ADD_VERTEX(C)
		COMPLETE_STRIP
	} else if (DetailLevel <= 1.0f) {
		float Roundness = DetailLevel;
		AB = VertexSmoothMiddle(A,B,Roundness);
		BC = VertexSmoothMiddle(B,C,Roundness);
		CA = VertexSmoothMiddle(C,A,Roundness);
		
		// 3 triangles in a strip between A and C
		ADD_VERTEX(A)
		ADD_VERTEX(AB)
		ADD_VERTEX(CA)
		ADD_VERTEX(BC)
		ADD_VERTEX(C)
		COMPLETE_STRIP
		
		// The last triangle at B
		ADD_VERTEX(AB)
		ADD_VERTEX(B)
		ADD_VERTEX(BC)
		COMPLETE_STRIP
	} else {
		AB = VertexSmoothMiddle(A,B,1.0f);
		BC = VertexSmoothMiddle(B,C,1.0f);
		CA = VertexSmoothMiddle(C,A,1.0f);
		float FinalRoundness = DetailLevel - 1.0f;
		VS_to_GS AB_CA = VertexSmoothMiddle(AB,CA,FinalRoundness);
		VS_to_GS BC_AB = VertexSmoothMiddle(BC,AB,FinalRoundness);
		VS_to_GS CA_BC = VertexSmoothMiddle(CA,BC,FinalRoundness);
		VS_to_GS A_CA = VertexSmoothMiddle(CA,A,FinalRoundness);
		VS_to_GS A_AB = VertexSmoothMiddle(A,AB,FinalRoundness);
		VS_to_GS B_AB = VertexSmoothMiddle(AB,B,FinalRoundness);
		VS_to_GS B_BC = VertexSmoothMiddle(B,BC,FinalRoundness);
		VS_to_GS C_BC = VertexSmoothMiddle(BC,C,FinalRoundness);
		VS_to_GS C_CA = VertexSmoothMiddle(C,CA,FinalRoundness);
		
		// 7 triangles in a strip between A and C
		ADD_VERTEX(A)
		ADD_VERTEX(A_AB)
		ADD_VERTEX(A_CA)
		ADD_VERTEX(AB_CA)
		ADD_VERTEX(CA)
		ADD_VERTEX(CA_BC)
		ADD_VERTEX(C_CA)
		ADD_VERTEX(C_BC)
		ADD_VERTEX(C)
		COMPLETE_STRIP
		
		// 5 triangles in a strip between A_AB and C_BC
		ADD_VERTEX(A_AB)
		ADD_VERTEX(AB)
		ADD_VERTEX(AB_CA)
		ADD_VERTEX(BC_AB)
		ADD_VERTEX(CA_BC)
		ADD_VERTEX(BC)
		ADD_VERTEX(C_BC)
		COMPLETE_STRIP
		
		// 3 triangles in a strip between AB and BC
		ADD_VERTEX(AB)
		ADD_VERTEX(B_AB)
		ADD_VERTEX(BC_AB)
		ADD_VERTEX(B_BC)
		ADD_VERTEX(BC)
		COMPLETE_STRIP
		
		// The last triangle at B
		ADD_VERTEX(B_AB)
		ADD_VERTEX(B)
		ADD_VERTEX(B_BC)
		COMPLETE_STRIP
	}
}

VS_to_GS VertexSmoothMiddle(VS_to_GS X, VS_to_GS Y, float Round) {
	VS_to_GS output = (VS_to_GS)0;
	output.Normal_WorldSpace = lerp(X.Normal_WorldSpace,Y.Normal_WorldSpace,0.5f);
	float3 FlatPos = lerp(X.Pos_WorldSpace,Y.Pos_WorldSpace,0.5f).xyz;
	float DistX = dot(X.Normal_WorldSpace,FlatPos - X.Pos_WorldSpace);
	float DistY = dot(Y.Normal_WorldSpace,FlatPos - Y.Pos_WorldSpace);
	output.Pos_WorldSpace = float4(FlatPos - (normalize(X.Normal_WorldSpace + Y.Normal_WorldSpace) * (DistX + DistY) * 0.33f * Round),1.0f);
	return output;
}

//--------------------------------------------------------------------------------------
// Post geometry vertex shader
// This is not an entry point since we just call it from GS
//--------------------------------------------------------------------------------------
GS_to_PS PGVS( VS_to_GS input ) {
	GS_to_PS output = (GS_to_PS)0;
	
	// Convert vertice positions from world space to 2D projection
	output.Pos = mul( input.Pos_WorldSpace, WorldToImage );
	
	return output;
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( GS_to_PS input) : SV_Target {
	return float4(0.0f, 1.0f, 0.0f, 0.0f);
}
