#include "graphics\\nuanceurs\\projectionconstants.type"

// The input patch size.  In this sample, it is 16 control points.
// This value should match the call to IASetPrimitiveTopology()
#define INPUT_PATCH_SIZE 16

// The output patch size.  In this sample, it is also 16 control points.
#define OUTPUT_PATCH_SIZE 16
cbuffer ConstantBuffer : register(b1)
{
    float gTessFactor;
}
float4 color : register (b2);

struct VS_CONTROL_POINT_INPUT
{
    float3 vPosition        : POSITION;
};

// Input control point
struct VS_CONTROL_POINT_OUTPUT
{
    float4 vPosition : SV_POSITION;
};

VS_CONTROL_POINT_OUTPUT BezierVS(VS_CONTROL_POINT_INPUT Input)
{
    VS_CONTROL_POINT_OUTPUT Output;

    Output.vPosition = mul(float4 (Input.vPosition, 1), World);

    return Output;
}

// Output control point
struct HS_OUTPUT
{
    float4 vPosition          : BEZIERPOS;
};

// Output patch constant data.
struct HS_CONSTANT_DATA_OUTPUT
{
	float   EdgeTessFactor[4] : SV_TessFactor; // e.g. would be [4] for a quad domain
	float InsideTessFactor[2] : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain
};



// Patch Constant Function
HS_CONSTANT_DATA_OUTPUT CalcHSPatchConstants(
	InputPatch<VS_CONTROL_POINT_OUTPUT, INPUT_PATCH_SIZE> ip,
	uint PatchID : SV_PrimitiveID)
{
	HS_CONSTANT_DATA_OUTPUT Output;

	// Insert code to compute Output here
    Output.EdgeTessFactor  [0] = gTessFactor;
    Output.EdgeTessFactor  [1] = gTessFactor;
    Output.EdgeTessFactor  [2] = gTessFactor;
    Output.EdgeTessFactor  [3] = gTessFactor;
    Output.InsideTessFactor[0] = gTessFactor;
    Output.InsideTessFactor[1] = gTessFactor;
		      // e.g. could calculate dynamic tessellation factors instead
    
	return Output;
}

[domain("quad")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(OUTPUT_PATCH_SIZE)]
[patchconstantfunc("CalcHSPatchConstants")]
HS_OUTPUT HS (
	InputPatch<VS_CONTROL_POINT_OUTPUT, INPUT_PATCH_SIZE> ip, 
	uint i : SV_OutputControlPointID,
	uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;

	// Insert code to compute Output here
	Output.vPosition = ip[i].vPosition;

	return Output;
}

struct DS_OUTPUT
{
    float4 vPosition        : SV_POSITION;
    float3 vWorldPos        : WORLDPOS;
    float3 vNormal          : NORMAL;

    float2 tex              : TEXCOORD0;
};


//--------------------------------------------------------------------------------------
float4 BernsteinBasis(float t)
{
    float invT = 1.0f - t;

    return float4( invT * invT * invT,
                   3.0f * t * invT * invT,
                   3.0f * t * t * invT,
                   t * t * t );
}

//--------------------------------------------------------------------------------------
float4 dBernsteinBasis(float t)
{
    float invT = 1.0f - t;

    return float4( -3 * invT * invT,
                   3 * invT * invT - 6 * t * invT,
                   6 * t * invT - 3 * t * t,
                   3 * t * t );
}

//--------------------------------------------------------------------------------------
float4 EvaluateBezier( const OutputPatch<HS_OUTPUT, OUTPUT_PATCH_SIZE> bezpatch,
                       float4 BasisU,
                       float4 BasisV )
{
    float4 Value = float4(0,0,0,0);
    Value  = BasisV.x * ( bezpatch[0].vPosition * BasisU.x + bezpatch[1].vPosition * BasisU.y + bezpatch[2].vPosition * BasisU.z + bezpatch[3].vPosition * BasisU.w );
    Value += BasisV.y * ( bezpatch[4].vPosition * BasisU.x + bezpatch[5].vPosition * BasisU.y + bezpatch[6].vPosition * BasisU.z + bezpatch[7].vPosition * BasisU.w );
    Value += BasisV.z * ( bezpatch[8].vPosition * BasisU.x + bezpatch[9].vPosition * BasisU.y + bezpatch[10].vPosition * BasisU.z + bezpatch[11].vPosition * BasisU.w );
    Value += BasisV.w * ( bezpatch[12].vPosition * BasisU.x + bezpatch[13].vPosition * BasisU.y + bezpatch[14].vPosition * BasisU.z + bezpatch[15].vPosition * BasisU.w );

    return Value;
}

// The domain shader is run once per vertex and calculates the final vertex's position
// and attributes.  It receives the UVW from the fixed function tessellator and the
// control point outputs from the hull shader.  Since we are using the DirectX 11
// Tessellation pipeline, it is the domain shader's responsibility to calculate the
// final SV_POSITION for each vertex.  In this sample, we evaluate the vertex's
// position using a Bernstein polynomial and the normal is calculated as the cross
// product of the U and V derivatives.

// The input SV_DomainLocation to the domain shader comes from fixed function
// tessellator.  And the OutputPatch comes from the hull shader.  From these, you
// must calculate the final vertex position, color, texcoords, and other attributes.

// The output from the domain shader will be a vertex that will go to the video card's
// rasterization pipeline and get drawn to the screen.

[domain("quad")]
DS_OUTPUT main( HS_CONSTANT_DATA_OUTPUT input,
                    float2 UV : SV_DomainLocation,
                    const OutputPatch<HS_OUTPUT, OUTPUT_PATCH_SIZE> bezpatch )
{
    float4 BasisU = BernsteinBasis( UV.x );
    float4 BasisV = BernsteinBasis( UV.y );
    float4 dBasisU = dBernsteinBasis( UV.x );
    float4 dBasisV = dBernsteinBasis( UV.y );

    float4 WorldPos = EvaluateBezier( bezpatch, BasisU, BasisV );
    float3 Tangent = EvaluateBezier( bezpatch, dBasisU, BasisV ).xyz;
    float3 BiTangent = EvaluateBezier( bezpatch, BasisU, dBasisV ).xyz;
    float3 Norm = normalize( cross( Tangent, BiTangent ) );

    DS_OUTPUT Output;
    //Output.vPosition = mul(mul /*TODO: as per check-in comment: Why ?*/(float4(WorldPos, 1), World), ViewProjection);
    Output.vPosition = mul(WorldPos, ViewProjection);
    Output.vWorldPos = WorldPos;
    Output.vNormal   = Norm;

    Output.  tex     = UV;

    return Output;    
}

//--------------------------------------------------------------------------------------
// Smooth shading pixel shader section
//--------------------------------------------------------------------------------------

// The pixel shader works the same as it would in a normal graphics pipeline.
// In this sample, it performs very simple N dot L lighting.

float4 BezierPS( DS_OUTPUT Input ) : SV_TARGET
{
    float3 N = normalize(Input.vNormal);
    float3 L = normalize(Input.vWorldPos.xyz - float3( 5, 25,- 50));
    float4 rv= abs(dot(N, L)) *color;
    rv.w=color.w;
    return rv;
}
