
#define Diffuse tex_0
#define Map tex_1
#define MapBorderColor Arg[0]

// The data structure from the vertex shader to the geometry shader
struct VS_to_GS {
	float4 Pos_WorldSpace : TEXCOORD0;
};

// 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( float3 Pos_WorldSpace );
void MakeTile(float3 StartPos, float WallHeight_Center, float WallHeight_Left, float WallHeight_Right, float WallHeight_Up, float WallHeight_Down, float3 Color, inout TriangleStream<GS_to_PS> TriStream);

//--------------------------------------------------------------------------------------
// 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 );
	
	return output;
}

float4 GetTile(int X, int Y, int Width, int Height) {
	if (X < 0 || Y < 0 || X >= Width || Y >= Height) {
		return MapBorderColor;
	} else {
		return Map.Load(int3(X,Y,0));
	}
}

//--------------------------------------------------------------------------------------
// Geometry Shader
//--------------------------------------------------------------------------------------
#define ADD_VERTEX(P) TriStream.Append(PGVS(P));
#define COMPLETE_STRIP TriStream.RestartStrip();
[maxvertexcount(20)]
void GS( triangle VS_to_GS input[3], inout TriangleStream<GS_to_PS> TriStream ) {
	// Make a bump
	float3 A = input[0].Pos_WorldSpace.xyz;
	
	int X = round(A.x);
	int Y = round(A.z);
	uint Width; uint Height; uint Levels;
	Map.GetDimensions(0,Width,Height,Levels);
	float4 Tile_Center = GetTile(X,Y,Width,Height);
	float TileHeight_Down = GetTile(X,Y-1,Width,Height).a;
	float TileHeight_Up = GetTile(X,Y+1,Width,Height).a;
	float TileHeight_Left = GetTile(X-1,Y,Width,Height).a;
	float TileHeight_Right = GetTile(X+1,Y,Width,Height).a;
	
	// Tile
	MakeTile(A,Tile_Center.a,TileHeight_Left,TileHeight_Right,TileHeight_Up,TileHeight_Down,Tile_Center.rgb,TriStream);
}

void MakeTile(float3 StartPos, float WallHeight_Center, float WallHeight_Left, float WallHeight_Right, float WallHeight_Up, float WallHeight_Down, float3 Color, inout TriangleStream<GS_to_PS> TriStream) {
	// Assume size 1 on triangles to speed up calculations
	float3 A = StartPos;
	float3 B = A + float3(0.0f,0.0f,1.0f);
	float3 C = A + float3(1.0f,0.0f,0.0f);
	float3 D = A + float3(1.0f,0.0f,1.0f);
	float3 N;
	if (WallHeight_Center > WallHeight_Left) {
		// Left -X
		N = float3(-1.0f,0.0f,0.0f);
		ADD_VERTEX(A + float3(0.0f,WallHeight_Left,0.0f))
		ADD_VERTEX(A + float3(0.0f,WallHeight_Left,1.0f))
		ADD_VERTEX(A + float3(0.0f,WallHeight_Center,0.0f))
		ADD_VERTEX(A + float3(0.0f,WallHeight_Center,1.0f))
		COMPLETE_STRIP
	}

	if (WallHeight_Center > WallHeight_Right) {
		// Right +X
		N = float3(1.0f,0.0f,0.0f);
		ADD_VERTEX(A + float3(1.0f,WallHeight_Center,0.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Center,1.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Right,0.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Right,1.0f))
		COMPLETE_STRIP
	}
	
	if (WallHeight_Center > WallHeight_Down) {
		// Down -Z
		N = float3(0.0f,0.0f,-1.0f);
		ADD_VERTEX(A + float3(0.0f,WallHeight_Down,0.0f))
		ADD_VERTEX(A + float3(0.0f,WallHeight_Center,0.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Down,0.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Center,0.0f))
		COMPLETE_STRIP
	}
	
	if (WallHeight_Center > WallHeight_Up) {
		// Up +Z
		N = float3(0.0f,0.0f,1.0f);
		ADD_VERTEX(A + float3(0.0f,WallHeight_Center,1.0f))
		ADD_VERTEX(A + float3(0.0f,WallHeight_Up,1.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Center,1.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Up,1.0f))
		COMPLETE_STRIP
	}
	
	if (WallHeight_Center > 0.01f) {
		// Roof
		N = float3(0.0f,1.0f,0.0f);
		ADD_VERTEX(A + float3(0.0f,WallHeight_Center,0.0f))
		ADD_VERTEX(A + float3(0.0f,WallHeight_Center,1.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Center,0.0f))
		ADD_VERTEX(A + float3(1.0f,WallHeight_Center,1.0f))
		COMPLETE_STRIP
	}
}

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

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