
// This shader is used for sine shaped roofs.
// The subdivision use linear interpolation and is not suitable for approximating round shapes.

// The data structure from the vertex shader to the geometry shader
struct VS_to_GS {
	float4 Tex : TEXCOORD0;
	float4 Color : COLOR;
	float3 Normal_WorldSpace : NORMAL;
	float4 Pos_WorldSpace : TEXCOORD1;
	float3 A_WorldSpace : TEXCOORD2;
	float3 B_WorldSpace : TEXCOORD3;
};

// The data structure from the geometry shader to the pixel shader
struct GS_to_PS {
	float4 Pos : SV_POSITION;
	float4 Tex : TEXCOORD0;
	float4 Color : COLOR;
	float3 Normal_WorldSpace : NORMAL;
	float4 Pos_WorldSpace : TEXCOORD1;
	float4 Pos_CameraSpace : TEXCOORD2;
	float3 A_WorldSpace : TEXCOORD3;
	float3 B_WorldSpace : TEXCOORD4;
};

void MakeStrip(in VS_to_GS A, in VS_to_GS B, in VS_to_GS C, in VS_to_GS D, inout TriangleStream<GS_to_PS> TriStream);
void MakeQuad(in VS_to_GS A, in VS_to_GS B, in VS_to_GS C, in VS_to_GS D, inout TriangleStream<GS_to_PS> TriStream);
VS_to_GS VertexLerp(in VS_to_GS X, in VS_to_GS Y, in float Ratio);
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 and tangent space to world space
	output.Normal_WorldSpace = mul(input.Normal.xyz,(float3x3)ObjectToWorld);
	output.A_WorldSpace = mul(input.A.xyz,(float3x3)ObjectToWorld);
	output.B_WorldSpace = mul(input.B.xyz,(float3x3)ObjectToWorld);
	
	// Give the texture coordinates to the geometry shader
	output.Tex = input.Tex;
	
	// Give the vertex color multiplied with the instance color to the geometry shader
	output.Color = lerp(input.Color * InstanceColor, float4(0.0f,0.0f,1.0f,1.0f), input.Selected);
	
	return output;
}

//--------------------------------------------------------------------------------------
// Geometry Shader
//--------------------------------------------------------------------------------------
#define ADD_VERTEX(A) TriStream.Append(PGVS(A));
#define COMPLETE_STRIP TriStream.RestartStrip();

[maxvertexcount(28)]
void GS( triangle VS_to_GS input[3], inout TriangleStream<GS_to_PS> TriStream ) {
	VS_to_GS A = input[0]; // Upper left
	VS_to_GS B = input[1]; // Upper right
	VS_to_GS C = input[2]; // Lower left
	VS_to_GS D; // Lower right
	VS_to_GS E; // Extruded lower left
	VS_to_GS F; // Extruded lower right
	VS_to_GS Center;
	Center = VertexLerp(B,C,0.5f); // Interpolate center
	D = VertexLerp(A,Center,2.0f); // Extrapolate corner
	
	// Tesselate an edge
	E = VertexLerp(A,C,0.9f);
	F = VertexLerp(B,D,0.9f);
	
	// Extrude the edge
	E.Pos_WorldSpace.xyz += C.Normal_WorldSpace * 0.1;
	F.Pos_WorldSpace.xyz += D.Normal_WorldSpace * 0.1;
	
	// Fake ambient occlusion with more contrast
	C.Color.rgb *= 0.3f;
	D.Color.rgb *= 0.3f;
	
	if (DetailLevel <= 0.5f) {
		// Upper to extruded
		MakeQuad(A,B,E,F,TriStream);

		// Extruded to lower
		MakeQuad(E,F,C,D,TriStream);
	} else {
		// Upper to extruded
		MakeStrip(A,B,E,F,TriStream);

		// Extruded to lower
		MakeStrip(E,F,C,D,TriStream);
	}
}

#define EDGES 7 // Quads per patch + 1
void MakeStrip(in VS_to_GS A, in VS_to_GS B, in VS_to_GS C, in VS_to_GS D, inout TriangleStream<GS_to_PS> TriStream) {
	int i;
	for ( i = 0; i <= EDGES - 1; i++) {
		ADD_VERTEX(VertexLerp(C,D,((float)i) / ((float)(EDGES - 1))))
		ADD_VERTEX(VertexLerp(A,B,((float)i) / ((float)(EDGES - 1))))
	}
	COMPLETE_STRIP
}

void MakeQuad(in VS_to_GS A, in VS_to_GS B, in VS_to_GS C, in VS_to_GS D, inout TriangleStream<GS_to_PS> TriStream) {
	ADD_VERTEX(A)
	ADD_VERTEX(B)
	ADD_VERTEX(C)
	ADD_VERTEX(D)
	COMPLETE_STRIP
}

VS_to_GS VertexLerp(in VS_to_GS X, in VS_to_GS Y, in float Ratio) {
	VS_to_GS output = (VS_to_GS)0;
	output.Tex = lerp(X.Tex,Y.Tex,Ratio);
	output.Color = lerp(X.Color,Y.Color,Ratio);
	output.Normal_WorldSpace = lerp(X.Normal_WorldSpace,Y.Normal_WorldSpace,Ratio);
	output.Pos_WorldSpace = lerp(X.Pos_WorldSpace,Y.Pos_WorldSpace,Ratio);
	output.A_WorldSpace = lerp(X.A_WorldSpace,Y.A_WorldSpace,Ratio);
	output.B_WorldSpace = lerp(X.B_WorldSpace,Y.B_WorldSpace,Ratio);
	return output;
}

//tex_0 is the diffuse texture
//tex_1 is the normal map with depth in the alpha channel

//--------------------------------------------------------------------------------------
// 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;
	
	// Get the depth
	float Depth = cos(input.Tex.x * 4 * 6.2831853f) * saturate((DetailLevel - 0.5f) * 3.0f) * 0.04f;
	
	// Apply the offset
	output.Pos_WorldSpace = float4(input.Pos_WorldSpace.xyz - (input.Normal_WorldSpace.xyz * Depth), input.Pos_WorldSpace.w);
	
	// Convert vertice positions from world space to camera space
	output.Pos_CameraSpace = mul( output.Pos_WorldSpace, WorldToCamera );
	// Convert vertice positions from camera space to 2D projection
	output.Pos = mul( output.Pos_CameraSpace, CameraToImage );
	
	// Give the normal to the pixel shader
	output.Normal_WorldSpace = input.Normal_WorldSpace;
	output.A_WorldSpace = input.A_WorldSpace;
	output.B_WorldSpace = input.B_WorldSpace;
	
	// Give the texture coordinates to the pixel shader
	output.Tex = input.Tex;
	
	// Give the vertex color
	output.Color = input.Color;
	return output;
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( GS_to_PS input) : SV_Target {
	// If the camera has a world space cutting plane, clip on the negative side.
	UseCuttingPlane(input.Pos_WorldSpace)
	
	float4 finalColor = tex_0.Sample( samAnisotropicMipmap, input.Tex.xy ) * input.Color;
	
	float3x3 TangentSpace;
	float3 FinalNormal;
	TangentSpace = float3x3(normalize(input.A_WorldSpace),normalize(input.B_WorldSpace),normalize(input.Normal_WorldSpace));
	FinalNormal = normalize(mul((tex_1.Sample( samAnisotropicMipmap, input.Tex.xy ).xyz * 2.0f) - 1.0f,TangentSpace));
	
	float3 DiffuseLight;
	Engine_GetDiffuseLight(input.Pos_WorldSpace.xyz,FinalNormal,DiffuseLight);
	finalColor = (finalColor * float4(DiffuseLight,1.0f));
	
	// Show the final color with fog on R,G,B channels
	return float4((lerp(finalColor,FogColor,saturate(length(input.Pos_CameraSpace) / FogDistance) * MaxFogIntensity)).xyz,finalColor.w);
}
